diff options
Diffstat (limited to 'keystone-moon')
184 files changed, 17646 insertions, 1914 deletions
diff --git a/keystone-moon/.coveragerc b/keystone-moon/.coveragerc index de2b16c5..674b3d56 100644 --- a/keystone-moon/.coveragerc +++ b/keystone-moon/.coveragerc @@ -1,7 +1,7 @@ [run] branch = True source = keystone -omit = keystone/tests/*,keystone/openstack/* +omit = keystone/tests/* [report] -ignore-errors = True +ignore_errors = True diff --git a/keystone-moon/.gitignore b/keystone-moon/.gitignore new file mode 100644 index 00000000..efa3c29d --- /dev/null +++ b/keystone-moon/.gitignore @@ -0,0 +1,33 @@ +*.pyc +*.sw? +*.egg/ +vendor +.ksl-venv +.venv +.update-venv/ +.tox +keystone.egg-info/ +*.log +.coverage +coverage.xml +cover/* +covhtml +pep8.txt +nosetests.xml +doc/build +.DS_Store +doc/source/api +doc/source/modules.rst +ChangeLog +AUTHORS +build/ +dist/ +etc/keystone.conf +etc/logging.conf +etc/keystone/ +keystone/tests/tmp/ +.project +.pydevproject +keystone/locale/*/LC_MESSAGES/*.mo +.testrepository/ +*.db diff --git a/keystone-moon/.gitreview b/keystone-moon/.gitreview new file mode 100644 index 00000000..cd914fe0 --- /dev/null +++ b/keystone-moon/.gitreview @@ -0,0 +1,4 @@ +[gerrit] +host=review.openstack.org +port=29418 +project=openstack/keystone.git diff --git a/keystone-moon/bandit.yaml b/keystone-moon/bandit.yaml index 89d2551d..d1f561ec 100644 --- a/keystone-moon/bandit.yaml +++ b/keystone-moon/bandit.yaml @@ -11,9 +11,9 @@ plugin_name_pattern: '*.py' #output_colors: # DEFAULT: '\033[0m' # HEADER: '\033[95m' -# INFO: '\033[94m' -# WARN: '\033[93m' -# ERROR: '\033[91m' +# LOW: '\033[94m' +# MEDIUM: '\033[93m' +# HIGH: '\033[91m' # optional: log format string #log_format: "[%(module)s]\t%(levelname)s\t%(message)s" @@ -29,31 +29,75 @@ exclude_dirs: - '/tests/' profiles: - keystone_conservative: + gate: include: + + # TODO: + # - any_other_function_with_shell_equals_true + + # TODO: + # - assert_used + - blacklist_calls + + # TODO: + # - blacklist_import_func + - blacklist_imports - - request_with_no_cert_validation - exec_used - - set_bad_file_permissions - - subprocess_popen_with_shell_equals_true + + # TODO: + # - execute_with_run_as_root_equals_true + + # TODO: + # - hardcoded_bind_all_interfaces + + # Not working because wordlist/default-passwords file not bundled, + # see https://bugs.launchpad.net/bandit/+bug/1451575 : + # - hardcoded_password + + # Not used because it's prone to false positives: + # - hardcoded_sql_expressions + + # TODO: + # - hardcoded_tmp_directory + + # TODO: + # - jinja2_autoescape_false + - linux_commands_wildcard_injection - - ssl_with_bad_version + # TODO: + # - paramiko_calls + + # TODO: + # - password_config_option_not_marked_secret - keystone_verbose: - include: - - blacklist_calls - - blacklist_imports - request_with_no_cert_validation - - exec_used - set_bad_file_permissions - - hardcoded_tmp_directory - subprocess_popen_with_shell_equals_true - - any_other_function_with_shell_equals_true - - linux_commands_wildcard_injection - - ssl_with_bad_version + + # TODO: + # - subprocess_without_shell_equals_true + + # TODO: + # - start_process_with_a_shell + + # TODO: + # - start_process_with_no_shell + + # TODO: + # - start_process_with_partial_path + - ssl_with_bad_defaults + - ssl_with_bad_version + - ssl_with_no_version + + # TODO: + # - try_except_pass + + # TODO: + # - use_of_mako_templates blacklist_calls: bad_name_sets: @@ -65,8 +109,8 @@ blacklist_calls: qualnames: [marshal.load, marshal.loads] message: "Deserialization with the marshal module is possibly dangerous." - md5: - qualnames: [hashlib.md5] - message: "Use of insecure MD5 hash function." + qualnames: [hashlib.md5, Crypto.Hash.MD2.new, Crypto.Hash.MD4.new, Crypto.Hash.MD5.new, cryptography.hazmat.primitives.hashes.MD5] + message: "Use of insecure MD2, MD4, or MD5 hash function." - mktemp_q: qualnames: [tempfile.mktemp] message: "Use of insecure and deprecated function (mktemp)." @@ -107,8 +151,13 @@ blacklist_imports: level: ERROR message: "Telnet is considered insecure. Use SSH or some other encrypted protocol." +hardcoded_tmp_directory: + tmp_dirs: ['/tmp', '/var/tmp', '/dev/shm'] + hardcoded_password: - word_list: "wordlist/default-passwords" + # Support for full path, relative path and special "%(site_data_dir)s" + # substitution (/usr/{local}/share) + word_list: "%(site_data_dir)s/wordlist/default-passwords" ssl_with_bad_version: bad_protocol_versions: @@ -132,3 +181,6 @@ execute_with_run_as_root_equals_true: - neutron.agent.linux.utils.execute - nova.utils.execute - nova.utils.trycmd + +try_except_pass: + check_typed_exception: True diff --git a/keystone-moon/doc/source/apache-httpd.rst b/keystone-moon/doc/source/apache-httpd.rst index 91eb7011..dbebc86e 100644 --- a/keystone-moon/doc/source/apache-httpd.rst +++ b/keystone-moon/doc/source/apache-httpd.rst @@ -32,12 +32,14 @@ Files ----- Copy the ``httpd/wsgi-keystone.conf`` sample configuration file to the -appropriate location for your Apache server:: +appropriate location for your Apache server, on Debian/Ubuntu systems +it is:: - /etc/$APACHE_DIR/conf.d/sites-available/wsgi-keystone.conf + /etc/apache2/sites-available/wsgi-keystone.conf -Where ``$APACHE_DIR`` is ``httpd`` on Fedora-based systems and ``apache2`` on -Debian/Ubuntu systems. +On Red Hat based systems it is:: + + /etc/httpd/conf.d/wsgi-keystone.conf Update the file to match your system configuration. Note the following: @@ -49,10 +51,11 @@ Keystone's primary configuration file (``etc/keystone.conf``) and the PasteDeploy configuration file (``etc/keystone-paste.ini``) must be readable to HTTPD in one of the default locations described in :doc:`configuration`. -Enable the site by creating a symlink from ``sites-enabled`` to the file in -``sites-available``:: +Enable the site by creating a symlink from the file in ``sites-available`` to +``sites-enabled``, for example, on Debian/Ubuntu systems +(not required on Red Hat based systems):: - ln -s /etc/$APACHE_DIR/sites-available/keystone.conf /etc/$APACHE_DIR/sites-enabled/ + ln -s /etc/apache2/sites-available/keystone.conf /etc/apache2/sites-enabled/ Restart Apache to have it start serving keystone. diff --git a/keystone-moon/doc/source/architecture.rst b/keystone-moon/doc/source/architecture.rst index 75b0ceae..c119e2bd 100644 --- a/keystone-moon/doc/source/architecture.rst +++ b/keystone-moon/doc/source/architecture.rst @@ -156,17 +156,19 @@ variety of environments and needs. The backend for each service is defined in the keystone.conf file with the key ``driver`` under a group associated with each service. -A general class under each backend named ``Driver`` exists to provide an +A general class exists under each backend to provide an abstract base class for any implementations, identifying the expected service -implementations. The drivers for the services are: - -* :mod:`keystone.assignment.core.Driver` -* :mod:`keystone.assignment.core.RoleDriver` -* :mod:`keystone.catalog.core.Driver` -* :mod:`keystone.identity.core.Driver` -* :mod:`keystone.policy.core.Driver` -* :mod:`keystone.resource.core.Driver` -* :mod:`keystone.token.core.Driver` +implementations. The classes are named after the keystone release in which +they were introduced. For eg. ``DriverV8`` for keystone release version 8. +The corresponding drivers for the services are: + +* :mod:`keystone.assignment.core.AssignmentDriverV8` +* :mod:`keystone.assignment.core.RoleDriverV8` +* :mod:`keystone.catalog.core.CatalogDriverV8` +* :mod:`keystone.identity.core.IdentityDriverV8` +* :mod:`keystone.policy.core.PolicyDriverV8` +* :mod:`keystone.resource.core.ResourceDriverV8` +* :mod:`keystone.token.core.TokenDriverV8` If you implement a backend driver for one of the Keystone services, you're expected to subclass from these classes. diff --git a/keystone-moon/doc/source/configure_tokenless_x509.rst b/keystone-moon/doc/source/configure_tokenless_x509.rst new file mode 100644 index 00000000..40b9fd20 --- /dev/null +++ b/keystone-moon/doc/source/configure_tokenless_x509.rst @@ -0,0 +1,328 @@ +.. + 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. + +================================================ +Configuring Keystone for Tokenless Authorization +================================================ + +.. NOTE:: + + This feature is experimental and unsupported in Liberty. + +----------- +Definitions +----------- + +* `X.509 Tokenless Authorization`: Provides a means to authorize client + operations within Keystone by using an X.509 SSL client certificate + without having to issue a token. For details, please refer to the specs + `Tokenless Authorization with X.509 Client SSL Certificate`_ + +.. _`Tokenless Authorization with X.509 Client SSL Certificate`: http://specs.openstack.org/openstack/keystone-specs/specs/liberty/keystone-tokenless-authz-with-x509-ssl-client-cert.html + +Prerequisites +------------- + +Keystone must be running in a web container with https enabled; tests have +been done with Apache/2.4.7 running on Ubuntu 14.04 . Please refer to +`running-keystone-in-httpd`_ and `apache-certificate-and-key-installation`_ +as references for this setup. + +.. _`running-keystone-in-httpd`: http://docs.openstack.org/developer/keystone/apache-httpd.html +.. _`apache-certificate-and-key-installation`: https://www.digitalocean.com/community/tutorials/how-to-create-a-ssl-certificate-on-apache-for-ubuntu-14-04 + +-------------------- +Apache Configuration +-------------------- + +To enable X.509 tokenless authorization, SSL has to be enabled and configured +in the Apache virtual host file. The Client authentication attribute +``SSLVerifyClient`` should be set as ``optional`` to allow other token +authentication methods and attribute ``SSLOptions`` needs to set as +``+StdEnvVars`` to allow certificate attributes to be passed. The following +is the sample virtual host file used for the testing. + +.. code-block:: ini + + <VirtualHost *:443> + WSGIScriptAlias / /var/www/cgi-bin/keystone/main + ErrorLog /var/log/apache2/keystone.log + LogLevel debug + CustomLog /var/log/apache2/access.log combined + SSLEngine on + SSLCertificateFile /etc/apache2/ssl/apache.cer + SSLCertificateKeyFile /etc/apache2/ssl/apache.key + SSLCACertificatePath /etc/apache2/capath + SSLOptions +StdEnvVars + SSLVerifyClient optional + </VirtualHost> + +---------------------- +Keystone Configuration +---------------------- + +The following options can be defined in `keystone.conf`: + +* ``trusted_issuer`` - The multi-str list of trusted issuers to further + filter the certificates that are allowed to participate in the X.509 + tokenless authorization. If the option is absent then no certificates + will be allowed. The naming format for the attributes of a Distinguished + Name(DN) must be separated by a comma and contain no spaces; however + spaces are allowed for the value of an attribute, like 'L=San Jose' in + the example below. This configuration option may be repeated for multiple + values. Please look at the sample below. +* ``protocol`` - The protocol name for the X.509 tokenless authorization + along with the option `issuer_attribute` below can look up its + corresponding mapping. It defaults to ``x509``. +* ``issuer_attribute`` - The issuer attribute that is served as an IdP ID for + the X.509 tokenless authorization along with the protocol to look up its + corresponding mapping. It is the environment variable in the WSGI + enviornment that references to the Issuer of the client certificate. It + defaults to ``SSL_CLIENT_I_DN``. + +This is a sample configuration for two `trusted_issuer` and a `protocol` set +to ``x509``. + +.. code-block:: ini + + [tokenless_auth] + trusted_issuer = emailAddress=mary@abc.com,CN=mary,OU=eng,O=abc,L=San Jose,ST=California,C=US + trusted_issuer = emailAddress=john@openstack.com,CN=john,OU=keystone,O=openstack,L=Sunnyvale,ST=California,C=US + protocol = x509 + +------------- +Setup Mapping +------------- + +Like federation, X.509 tokenless authorization also utilizes the mapping +mechanism to formulate an identity. The identity provider must correspond +to the issuer of the X.509 SSL client certificate. The protocol for the +given identity is ``x509`` by default, but can be configurable. + +Create an Identity Provider(IdP) +-------------------------------- + +In order to create an IdP, the issuer DN in the client certificate needs +to be provided. The following sample is what a generic issuer DN looks +like in a certificate. + +.. code-block:: ini + + E=john@openstack.com + CN=john + OU=keystone + O=openstack + L=Sunnyvale + S=California + C=US + +The issuer DN should be constructed as a string that contains no spaces +and have the right order seperated by commas like the example below. +Please be aware that ``emailAddress`` and ``ST`` should be used instead +of ``E`` and ``S`` that are shown in the above example. The following is +the sample Python code used to create the IdP ID. + +.. code-block:: python + + import hashlib + issuer_dn = 'emailAddress=john@openstack.com,CN=john,OU=keystone, + O=openstack,L=Sunnyvale,ST=California,C=US' + hashed_idp = hashlib.sha256(issuer_dn) + idp_id = hashed_idp.hexdigest() + print(idp_id) + +The output of the above Python code will be the IdP ID and the following +sample curl command should be sent to keystone to create an IdP with the +newly generated IdP ID. + +.. code-block:: bash + + curl -k -s -X PUT -H "X-Auth-Token: <TOKEN>" \ + -H "Content-Type: application/json" \ + -d '{"identity_provider": {"description": "Stores keystone IDP identities.","enabled": true}}' \ + https://<HOSTNAME>:<PORT>/v3/OS-FEDERATION/identity_providers/<IdP ID> + +Create a Map +------------ + +A mapping needs to be created to map the ``Subject DN`` in the client +certificate as a user to yield a valid local user if the user's ``type`` +defined as ``local`` in the mapping. For example, the client certificate +has ``Subject DN`` as ``CN=alex,OU=eng,O=nice-network,L=Sunnyvale, +ST=California,C=US``, in the following examples, ``user_name`` will be +mapped to``alex`` and ``domain_name`` will be mapped to ``nice-network``. +And it has user's ``type`` set to ``local``. If user's ``type`` is not +defined, it defaults to ``ephemeral``. + +Please refer to `mod_ssl`_ for the detailed mapping attributes. + +.. _`mod_ssl`: http://httpd.apache.org/docs/current/mod/mod_ssl.html + +.. code-block:: javascript + + { + "mapping": { + "rules": [ + { + "local": [ + { + "user": { + "name": "{0}", + "domain": { + "name": "{1}" + }, + "type": "local" + } + } + ], + "remote": [ + { + "type": "SSL_CLIENT_S_DN_CN" + }, + { + "type": "SSL_CLIENT_S_DN_O" + } + ] + } + ] + } + } + +When user's ``type`` is not defined or set to ``ephemeral``, the mapped user +does not have to be a valid local user but the mapping must yield at least +one valid local group. For example: + +.. code-block:: javascript + + { + "mapping": { + "rules": [ + { + "local": [ + { + "user": { + "name": "{0}", + "type": "ephemeral" + } + }, + { + "group": { + "id": "12345678" + } + } + ], + "remote": [ + { + "type": "SSL_CLIENT_S_DN_CN" + } + ] + } + ] + } + } + +The following sample curl command should be sent to keystone to create a +mapping with the provided mapping ID. The mapping ID is user designed and +it can be any string as opposed to IdP ID. + +.. code-block:: bash + + curl -k -s -H "X-Auth-Token: <TOKEN>" \ + -H "Content-Type: application/json" \ + -d '{"mapping": {"rules": [{"local": [{"user": {"name": "{0}","type": "ephemeral"}},{"group": {"id": "<GROUPID>"}}],"remote": [{"type": "SSL_CLIENT_S_DN_CN"}]}]}}' \ + -X PUT https://<HOSTNAME>:<PORT>/v3/OS-FEDERATION/mappings/<MAPPING ID> + + +Create a Protocol +----------------- + +The name of the protocol will be the one defined in `keystone.conf` as +``protocol`` which defaults to ``x509``. The protocol name is user designed +and it can be any name as opposed to IdP ID. + +A protocol name and an IdP ID will uniquely identify a mapping. + +The following sample curl command should be sent to keystone to create a +protocol with the provided protocol name that is defined in `keystone.conf`. + +.. code-block:: bash + + curl -k -s -H "X-Auth-Token: <TOKEN>" \ + -H "Content-Type: application/json" \ + -d '{"protocol": {"mapping_id": "<MAPPING ID>"}}' \ + -X PUT https://<HOSTNAME>:<PORT>/v3/OS-FEDERATION/identity_providers/<IdP ID>/protocols/<PROTOCOL NAME> + +------------------------------- +Setup ``auth_token`` middleware +------------------------------- + +In order to use ``auth_token`` middleware as the service client for X.509 +tokenless authorization, both configurable options and scope information +will need to be setup. + +Configurable Options +-------------------- + +The following configurable options in ``auth_token`` middleware +should set to the correct values: + +* ``auth_protocol`` - Set to ``https``. +* ``certfile`` - Set to the full path of the certificate file. +* ``keyfile`` - Set to the full path of the private key file. +* ``cafile`` - Set to the full path of the trusted CA certificate file. + +Scope Information +----------------- + +The scope information will be passed from the headers with the following +header attributes to: + +* ``X-Project-Id`` - If specified, its the project scope. +* ``X-Project-Name`` - If specified, its the project scope. +* ``X-Project-Domain-Id`` - If specified, its the domain of project scope. +* ``X-Project-Domain-Name`` - If specified, its the domain of project scope. +* ``X-Domain-Id`` - If specified, its the domain scope. +* ``X-Domain-Name`` - If specified, its the domain scope. + +--------------------- +Test It Out with cURL +--------------------- + +Once the above configurations have been setup, the following curl command can +be used for token validation. + +.. code-block:: bash + + curl -v -k -s -X GET --cert /<PATH>/x509client.crt \ + --key /<PATH>/x509client.key \ + --cacert /<PATH>/ca.crt \ + -H "X-Project-Name: <PROJECT-NAME>" \ + -H "X-Project-Domain-Id: <PROJECT-DOMAIN-ID>" \ + -H "X-Subject-Token: <TOKEN>" \ + https://<HOST>:<PORT>/v3/auth/tokens | python -mjson.tool + +Details of the Options +---------------------- + +* ``--cert`` - The client certificate that will be presented to Keystone. + The ``Issuer`` in the certificate along with the defined ``protocol`` + in `keystone.conf` will uniquely identify the mapping. The ``Subject`` + in the certificate will be mapped to the valid local user from the + identified mapping. +* ``--key`` - The corresponding client private key. +* ``--cacert`` - It can be the Apache server certificate or its issuer + (signer) certificate. +* ``X-Project-Name`` - The project scope needs to be passed in the header. +* ``X-Project-Domain-Id`` - Its the domain of project scope. +* ``X-Subject-Token`` - The token to be validated. + diff --git a/keystone-moon/doc/source/developing_drivers.rst b/keystone-moon/doc/source/developing_drivers.rst new file mode 100644 index 00000000..1e3996de --- /dev/null +++ b/keystone-moon/doc/source/developing_drivers.rst @@ -0,0 +1,130 @@ +.. + 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. + +=========================== +Developing Keystone Drivers +=========================== + +A driver, also known as a backend, is an important architectural +component of Keystone. It is an abstraction around the data access +needed by a particular subsystem. This pluggable implementation is not +only how Keystone implements its own data access, but how you can +implement your own! + +Each major subsystem (that has data access needs) implements the data access +by using drivers. Some examples of Keystone's drivers: + +- :class:`keystone.identity.backends.ldap.Identity` +- :class:`keystone.token.providers.fernet.Provider` +- :class:`keystone.contrib.federation.backends.sql.Federation` + +In/Out of Tree +-------------- + +It's best to start developing your custom driver outside of the Keystone +development process. This means developing it in your own public or private git +repository and not worrying about getting it upstream (for now). + +This is better for you because it gives you more freedom and you are not bound +to the strict OpenStack development rules or schedule. You can iterate faster +and take whatever shortcuts you need to get your product out of the door. + +This is also good for Keystone because it will limit the amount of drivers +that must be maintained by the team. If the team had to maintain a +driver for each NoSQL DB that deployers want to use in production there +would be less time to make Keystone itself better. Not to mention that +the team would have to start gaining expertise in potentially dozens of +new technologies. + +As you'll see below there is no penalty for open sourcing your driver, +on GitHub for example, or even keeping your implementation private. We +use `Setuptools entry points`_ to load your driver from anywhere in the +Python path. + +.. _Setuptools entry points: no good resource? + +How To Make a Driver +-------------------- + +The TLDR; steps (and too long didn't write yet): + +1. Determine which subsystem you would like write a driver for +2. Subclass the most current version of the driver interface +3. Implement each of the abstract methods for that driver + + a. We are currently not documenting the exact input/outputs of the + driver methods. The best approach right now is to use an existing + driver as an example of what data your driver will receive and + what data your driver will be required to return. + b. There is a plan in place to document these APIs in more detail. + +4. Register your new driver as an entry point +5. Configure your new driver in ``keystone.conf`` +6. Sit back and enjoy! + +Driver Versioning +----------------- + +In the past the driver class was named ``Driver`` and changes would +sometimes be devastating to developers that depend on our driver +contracts. To help alleviate some of the issues we are now creating +version driver classes, e.g. ``DriverV8``. + +We'll be supporting the current driver version for at least one version back. +This gives developers a full cycle to update their drivers. Some cases, such +as critical security flaws, may require a change to be introduced that breaks +compatibility. These special cases will be communicated as widely as possible +via the typical OpenStack communication channels. + +As new driver interface versions are added old ones will be moved to a +"deprecated" state and will output deprecation messages when used. When a +driver version moves from "deprecated" to "unsupported" it will be +removed from the keystone source tree. + +Removing Methods +~~~~~~~~~~~~~~~~ + +Newer driver interfaces may remove methods that are currently required. +Methods are removed when they are no longer required or invoked by Keystone. +There is no reason why methods removed from the Keystone interface need to be +removed from custom drivers. + +Adding Methods +-------------- + +The most common API changes will be adding method to support new +features. We'll do our best to add methods in a way that is backward +compatible. The new version of the driver will define the new method as +an ``abc.abstractmethod`` that must be implemented by driver +implementations. When possible we'll also go back to our supported drivers and +add the method, with a default implementation. + +For example, given a ``thing.DriverV8`` that added a new method +``list_things_by_name()``, we will go back to ``thing.DriverV7`` and +implement that method. This is good because in many cases your driver +will just work, but there are a couple of unfortunate side effects. +First if you have already used that method name you will have to rename +your method and cut a new version. Second is that the default +implementation may cause a performance penalty due to its naive +implementation. + +Updating Methods +~~~~~~~~~~~~~~~~ + +We will try not to update existing methods in ways that will break old +driver implementations. That means that: + +* We will respect existing parameters and not just delete them. If they are + to be removed we will respect their behavior and deprecate them in older + versions. +* We will add new parameters as optional with backward compatible defaults. diff --git a/keystone-moon/doc/source/federation/websso.rst b/keystone-moon/doc/source/federation/websso.rst index 4ada0a4c..682449ac 100644 --- a/keystone-moon/doc/source/federation/websso.rst +++ b/keystone-moon/doc/source/federation/websso.rst @@ -35,9 +35,17 @@ prevent man-in-the-middle (MITM) attacks. 2. Update httpd vhost file with websso information. -The `/v3/auth/OS-FEDERATION/websso/<protocol>` route must be protected by the -chosen httpd module. This is performed so the request that originates from -horizon will use the same identity provider that is configured in keystone. +The `/v3/auth/OS-FEDERATION/websso/<protocol>` and +`/v3/auth/OS-FEDERATION/identity_providers/{idp_id}/protocols/{protocol_id}/websso` +routes must be protected by the chosen httpd module. This is performed so the +request that originates from horizon will use the same identity provider that +is configured in keystone. + +.. WARNING:: + By using the IdP specific route, a user will no longer leverage the Remote + ID of a specific Identity Provider, and will be unable to verify that the + Identity Provider is trusted, the mapping will remain as the only means to + controlling authorization. If `mod_shib` is used, then use the following as an example: @@ -52,6 +60,11 @@ If `mod_shib` is used, then use the following as an example: Require valid-user ... </Location> + <Location ~ "/v3/auth/OS-FEDERATION/identity_providers/idp_1/protocols/saml2/websso"> + AuthType shibboleth + Require valid-user + ... + </Location> </VirtualHost> If `mod_auth_openidc` is used, then use the following as an example: @@ -61,6 +74,7 @@ If `mod_auth_openidc` is used, then use the following as an example: <VirtualHost *:5000> OIDCRedirectURI http://localhost:5000/v3/auth/OS-FEDERATION/websso/redirect + OIDCRedirectURI http://localhost:5000/v3/auth/OS-FEDERATION/identity_providers/idp_1/protocol/oidc/websso/redirect ... @@ -69,6 +83,11 @@ If `mod_auth_openidc` is used, then use the following as an example: Require valid-user ... </Location> + <Location ~ "/v3/auth/OS-FEDERATION/identity_providers/idp_1/protocols/oidc/websso"> + AuthType openid-connect + Require valid-user + ... + </Location> </VirtualHost> If `mod_auth_kerb` is used, then use the following as an example: @@ -87,6 +106,14 @@ If `mod_auth_kerb` is used, then use the following as an example: Krb5Keytab /etc/apache2/http.keytab ... </Location> + <Location ~ "/v3/auth/OS-FEDERATION/identity_providers/idp_1/protocols/kerberos/websso"> + AuthType Kerberos + AuthName "Acme Corporation" + KrbMethodNegotiate on + KrbMethodK5Passwd off + Krb5Keytab /etc/apache2/http.keytab + ... + </Location> </VirtualHost> If `mod_auth_mellon` is used, then use the following as an example: @@ -103,6 +130,12 @@ If `mod_auth_mellon` is used, then use the following as an example: Require valid-user ... </Location> + <Location ~ "/v3/auth/OS-FEDERATION/identity_providers/idp_1/protocols/saml2/websso"> + AuthType Mellon + MellonEnable auth + Require valid-user + ... + </Location> </VirtualHost> .. NOTE:: @@ -182,6 +215,9 @@ Horizon Changes Django OpenStack Auth version 1.2.0 or higher is required for these steps. + Identity provider and federation protocol specific webSSO is only available + in Django OpenStack Auth version 2.0.0 or higher. + 1. Set the Identity Service version to 3 Ensure the `OPENSTACK_API_VERSIONS` option in horizon's local_settings.py has @@ -214,20 +250,45 @@ this will provide users with an updated login screen for horizon. 4. (Optional) Create a list of authentication methods with the `WEBSSO_CHOICES` option. -Within horizon's settings.py file, a list of supported authentication methods -can be specified. The entries in the list map to keystone federation protocols, -with the exception of ``credentials`` which is reserved by horizon, and maps to -the user name and password used by keystone's identity backend. +Within horizon's settings.py file, a list of supported authentication methods can be +specified. The list 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`). With the exception of ``credentials`` +which is reserved by horizon, and maps to the user name and password used by keystone's +identity backend. .. code-block:: python WEBSSO_CHOICES = ( ("credentials", _("Keystone Credentials")), ("oidc", _("OpenID Connect")), - ("saml2", _("Security Assertion Markup Language")) + ("saml2", _("Security Assertion Markup Language")), + ("idp_1_oidc", "Acme Corporation - OpenID Connect"), + ("idp_1_saml2", "Acme Corporation - SAML2") ) -5. (Optional) Specify an initial choice with the `WEBSSO_INITIAL_CHOICE` +5. (Optional) Create a dictionary of specific identity provider and federation + protocol combinations. + +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. + +.. code-block:: python + + WEBSSO_IDP_MAPPING = { + "idp_1_oidc": ("idp_1", "oidc"), + "idp_1_saml2": ("idp_1", "saml2") + } + +.. NOTE:: + + The value is expected to be a tuple formatted as: (<idp_id>, <protocol_id>). + +6. (Optional) Specify an initial choice with the `WEBSSO_INITIAL_CHOICE` option. The list set by the `WEBSSO_CHOICES` option will be generated in a drop-down diff --git a/keystone-moon/doc/source/index.rst b/keystone-moon/doc/source/index.rst index c77d7738..511bc89f 100644 --- a/keystone-moon/doc/source/index.rst +++ b/keystone-moon/doc/source/index.rst @@ -54,6 +54,7 @@ Getting Started configure_federation mapping_combinations mapping_schema + configure_tokenless_x509 configuringservices extensions key_terms @@ -74,6 +75,7 @@ Developers Documentation :maxdepth: 1 developing + developing_drivers architecture middlewarearchitecture http-api diff --git a/keystone-moon/doc/source/man/keystone-all.rst b/keystone-moon/doc/source/man/keystone-all.rst index ea958fe0..b9c219b3 100644 --- a/keystone-moon/doc/source/man/keystone-all.rst +++ b/keystone-moon/doc/source/man/keystone-all.rst @@ -7,9 +7,9 @@ Keystone Startup Command ------------------------ :Author: openstack@lists.openstack.org -:Date: 2014-10-16 +:Date: 2015-10-15 :Copyright: OpenStack Foundation -:Version: 2014.2 +:Version: 8.0.0 :Manual section: 1 :Manual group: cloud computing diff --git a/keystone-moon/doc/source/man/keystone-manage.rst b/keystone-moon/doc/source/man/keystone-manage.rst index 21a3ca4a..2ef2d51a 100644 --- a/keystone-moon/doc/source/man/keystone-manage.rst +++ b/keystone-moon/doc/source/man/keystone-manage.rst @@ -7,9 +7,9 @@ Keystone Management Utility --------------------------- :Author: openstack@lists.openstack.org -:Date: 2015-4-7 +:Date: 2015-10-15 :Copyright: OpenStack Foundation -:Version: 2015.1 +:Version: 8.0.0 :Manual section: 1 :Manual group: cloud computing diff --git a/keystone-moon/doc/source/mapping_schema.rst b/keystone-moon/doc/source/mapping_schema.rst index a020178b..036df827 100644 --- a/keystone-moon/doc/source/mapping_schema.rst +++ b/keystone-moon/doc/source/mapping_schema.rst @@ -22,7 +22,7 @@ The schema for mapping is a description of how a mapping should be created. It shows all the requirements and possibilities for a JSON to be used for mapping. Mapping schema is validated with `JSON Schema -<http://http://json-schema.org/documentation.html>`__ +<http://json-schema.org/documentation.html>`__ Mapping Schema -------------- diff --git a/keystone-moon/etc/keystone.conf.sample b/keystone-moon/etc/keystone.conf.sample index ec5a08cc..9c76fc0d 100644 --- a/keystone-moon/etc/keystone.conf.sample +++ b/keystone-moon/etc/keystone.conf.sample @@ -760,8 +760,8 @@ # A list of trusted dashboard hosts. Before accepting a Single Sign-On request # to return a token, the origin host must be a member of the trusted_dashboard # list. This configuration option may be repeated for multiple values. For -# example: trusted_dashboard=http://acme.com trusted_dashboard=http://beta.com -# (multi valued) +# example: trusted_dashboard=http://acme.com/auth/websso +# trusted_dashboard=http://beta.com/auth/websso (multi valued) #trusted_dashboard = # Location of Single Sign-On callback handler, will return a token to a trusted @@ -1934,6 +1934,32 @@ #hash_algorithm = md5 +[tokenless_auth] + +# +# From keystone +# + +# The list of trusted issuers to further filter the certificates that are +# allowed to participate in the X.509 tokenless authorization. If the option is +# absent then no certificates will be allowed. The naming format for the +# attributes of a Distinguished Name(DN) must be separated by a comma and +# contain no spaces. This configuration option may be repeated for multiple +# values. For example: trusted_issuer=CN=john,OU=keystone,O=openstack +# trusted_issuer=CN=mary,OU=eng,O=abc (multi valued) +#trusted_issuer = + +# The protocol name for the X.509 tokenless authorization along with the option +# issuer_attribute below can look up its corresponding mapping. (string value) +#protocol = x509 + +# The issuer attribute that is served as an IdP ID for the X.509 tokenless +# authorization along with the protocol to look up its corresponding mapping. +# It is the environment variable in the WSGI environment that references to the +# issuer of the client certificate. (string value) +#issuer_attribute = SSL_CLIENT_I_DN + + [trust] # diff --git a/keystone-moon/keystone/assignment/backends/ldap.py b/keystone-moon/keystone/assignment/backends/ldap.py index 4ca66c4d..b52dc46e 100644 --- a/keystone-moon/keystone/assignment/backends/ldap.py +++ b/keystone-moon/keystone/assignment/backends/ldap.py @@ -31,11 +31,11 @@ CONF = cfg.CONF LOG = log.getLogger(__name__) -class Assignment(assignment.Driver): +class Assignment(assignment.AssignmentDriverV8): @versionutils.deprecated( versionutils.deprecated.KILO, remove_in=+2, - what='ldap') + what='ldap assignment') def __init__(self): super(Assignment, self).__init__() self.LDAP_URL = CONF.ldap.url diff --git a/keystone-moon/keystone/assignment/backends/sql.py b/keystone-moon/keystone/assignment/backends/sql.py index 89ff64b5..e249ba34 100644 --- a/keystone-moon/keystone/assignment/backends/sql.py +++ b/keystone-moon/keystone/assignment/backends/sql.py @@ -49,7 +49,7 @@ class AssignmentType(object): raise exception.AssignmentTypeCalculationError(**locals()) -class Assignment(keystone_assignment.Driver): +class Assignment(keystone_assignment.AssignmentDriverV8): def default_role_driver(self): return 'sql' diff --git a/keystone-moon/keystone/assignment/core.py b/keystone-moon/keystone/assignment/core.py index a001e6b1..a510c3c1 100644 --- a/keystone-moon/keystone/assignment/core.py +++ b/keystone-moon/keystone/assignment/core.py @@ -381,7 +381,10 @@ class Manager(manager.Manager): self.driver.remove_role_from_user_and_project(user_id, project_id, role_id) - self.identity_api.emit_invalidate_user_token_persistence(user_id) + if project_id: + self._emit_invalidate_grant_token_persistence(user_id, project_id) + else: + self.identity_api.emit_invalidate_user_token_persistence(user_id) self.revoke_api.revoke_by_grant(role_id, user_id=user_id, project_id=project_id) @@ -911,7 +914,7 @@ class Manager(manager.Manager): @six.add_metaclass(abc.ABCMeta) -class Driver(object): +class AssignmentDriverV8(object): def _role_to_dict(self, role_id, inherited): role_dict = {'id': role_id} @@ -1158,6 +1161,9 @@ class Driver(object): raise exception.NotImplemented() # pragma: no cover +Driver = manager.create_legacy_driver(AssignmentDriverV8) + + @dependency.provider('role_api') @dependency.requires('assignment_api') class RoleManager(manager.Manager): @@ -1219,7 +1225,7 @@ class RoleManager(manager.Manager): @six.add_metaclass(abc.ABCMeta) -class RoleDriver(object): +class RoleDriverV8(object): def _get_list_limit(self): return CONF.role.list_limit or CONF.list_limit @@ -1287,3 +1293,6 @@ class RoleDriver(object): """ raise exception.NotImplemented() # pragma: no cover + + +RoleDriver = manager.create_legacy_driver(RoleDriverV8) diff --git a/keystone-moon/keystone/assignment/role_backends/ldap.py b/keystone-moon/keystone/assignment/role_backends/ldap.py index d5a06a4c..6e5e038e 100644 --- a/keystone-moon/keystone/assignment/role_backends/ldap.py +++ b/keystone-moon/keystone/assignment/role_backends/ldap.py @@ -27,7 +27,7 @@ CONF = cfg.CONF LOG = log.getLogger(__name__) -class Role(assignment.RoleDriver): +class Role(assignment.RoleDriverV8): def __init__(self): super(Role, self).__init__() diff --git a/keystone-moon/keystone/assignment/role_backends/sql.py b/keystone-moon/keystone/assignment/role_backends/sql.py index f19d1827..3c707aa8 100644 --- a/keystone-moon/keystone/assignment/role_backends/sql.py +++ b/keystone-moon/keystone/assignment/role_backends/sql.py @@ -15,7 +15,7 @@ from keystone.common import sql from keystone import exception -class Role(assignment.RoleDriver): +class Role(assignment.RoleDriverV8): @sql.handle_conflicts(conflict_type='role') def create_role(self, role_id, role): diff --git a/keystone-moon/keystone/auth/controllers.py b/keystone-moon/keystone/auth/controllers.py index 04124696..133230d6 100644 --- a/keystone-moon/keystone/auth/controllers.py +++ b/keystone-moon/keystone/auth/controllers.py @@ -129,9 +129,9 @@ class AuthInfo(object): """Encapsulation of "auth" request.""" @staticmethod - def create(context, auth=None): + def create(context, auth=None, scope_only=False): auth_info = AuthInfo(context, auth=auth) - auth_info._validate_and_normalize_auth_data() + auth_info._validate_and_normalize_auth_data(scope_only) return auth_info def __init__(self, context, auth=None): @@ -272,14 +272,25 @@ class AuthInfo(object): if method_name not in AUTH_METHODS: raise exception.AuthMethodNotSupported() - def _validate_and_normalize_auth_data(self): - """Make sure "auth" is valid.""" + def _validate_and_normalize_auth_data(self, scope_only=False): + """Make sure "auth" is valid. + + :param scope_only: If it is True, auth methods will not be + validated but only the scope data. + :type scope_only: boolean + """ # make sure "auth" exist if not self.auth: raise exception.ValidationError(attribute='auth', target='request body') - self._validate_auth_methods() + # NOTE(chioleong): Tokenless auth does not provide auth methods, + # we only care about using this method to validate the scope + # information. Therefore, validating the auth methods here is + # insignificant and we can skip it when scope_only is set to + # true. + if scope_only is False: + self._validate_auth_methods() self._validate_and_normalize_scope_data() def get_method_names(self): diff --git a/keystone-moon/keystone/catalog/backends/kvs.py b/keystone-moon/keystone/catalog/backends/kvs.py index 30a121d8..fe975d9d 100644 --- a/keystone-moon/keystone/catalog/backends/kvs.py +++ b/keystone-moon/keystone/catalog/backends/kvs.py @@ -18,7 +18,7 @@ from keystone.common import driver_hints from keystone.common import kvs -class Catalog(kvs.Base, catalog.Driver): +class Catalog(kvs.Base, catalog.CatalogDriverV8): # Public interface def get_catalog(self, user_id, tenant_id): return self.db.get('catalog-%s-%s' % (tenant_id, user_id)) diff --git a/keystone-moon/keystone/catalog/backends/sql.py b/keystone-moon/keystone/catalog/backends/sql.py index 0db6d498..fe69db58 100644 --- a/keystone-moon/keystone/catalog/backends/sql.py +++ b/keystone-moon/keystone/catalog/backends/sql.py @@ -86,7 +86,7 @@ class Endpoint(sql.ModelBase, sql.DictBase): extra = sql.Column(sql.JsonBlob()) -class Catalog(catalog.Driver): +class Catalog(catalog.CatalogDriverV8): # Regions def list_regions(self, hints): session = sql.get_session() diff --git a/keystone-moon/keystone/catalog/core.py b/keystone-moon/keystone/catalog/core.py index 6883b024..8bb72619 100644 --- a/keystone-moon/keystone/catalog/core.py +++ b/keystone-moon/keystone/catalog/core.py @@ -278,8 +278,8 @@ class Manager(manager.Manager): @six.add_metaclass(abc.ABCMeta) -class Driver(object): - """Interface description for an Catalog driver.""" +class CatalogDriverV8(object): + """Interface description for the Catalog driver.""" def _get_list_limit(self): return CONF.catalog.list_limit or CONF.list_limit @@ -543,3 +543,6 @@ class Driver(object): v3_catalog.append(service_v3) return v3_catalog + + +Driver = manager.create_legacy_driver(CatalogDriverV8) diff --git a/keystone-moon/keystone/common/authorization.py b/keystone-moon/keystone/common/authorization.py index 8db618df..2c578dfd 100644 --- a/keystone-moon/keystone/common/authorization.py +++ b/keystone-moon/keystone/common/authorization.py @@ -29,13 +29,23 @@ AUTH_CONTEXT_ENV = 'KEYSTONE_AUTH_CONTEXT' Auth context is essentially the user credential used for policy enforcement. It is a dictionary with the following attributes: +* ``token``: Token from the request * ``user_id``: user ID of the principal * ``project_id`` (optional): project ID of the scoped project if auth is project-scoped * ``domain_id`` (optional): domain ID of the scoped domain if auth is domain-scoped +* ``domain_name`` (optional): domain name of the scoped domain if auth is + domain-scoped +* ``is_delegated_auth``: True if this is delegated (via trust or oauth) +* ``trust_id``: Trust ID if trust-scoped, or None +* ``trustor_id``: Trustor ID if trust-scoped, or None +* ``trustee_id``: Trustee ID if trust-scoped, or None +* ``consumer_id``: OAuth consumer ID, or None +* ``access_token_id``: OAuth access token ID, or None * ``roles`` (optional): list of role names for the given scope -* ``group_ids``: list of group IDs for which the API user has membership +* ``group_ids`` (optional): list of group IDs for which the API user has + membership if token was for a federated user """ diff --git a/keystone-moon/keystone/common/config.py b/keystone-moon/keystone/common/config.py index 4966dd9c..fcf05abe 100644 --- a/keystone-moon/keystone/common/config.py +++ b/keystone-moon/keystone/common/config.py @@ -529,8 +529,9 @@ FILE_OPTIONS = { 'token, the origin host must be a member of the ' 'trusted_dashboard list. This configuration ' 'option may be repeated for multiple values. ' - 'For example: trusted_dashboard=http://acme.com ' - 'trusted_dashboard=http://beta.com'), + 'For example: ' + 'trusted_dashboard=http://acme.com/auth/websso ' + 'trusted_dashboard=http://beta.com/auth/websso'), cfg.StrOpt('sso_callback_template', default=_SSO_CALLBACK, help='Location of Single Sign-On callback handler, will ' 'return a token to a trusted dashboard host.'), @@ -894,6 +895,32 @@ FILE_OPTIONS = { help='Entrypoint for the oAuth1.0 auth plugin module in ' 'the keystone.auth.oauth1 namespace.'), ], + 'tokenless_auth': [ + cfg.MultiStrOpt('trusted_issuer', default=[], + help='The list of trusted issuers to further filter ' + 'the certificates that are allowed to ' + 'participate in the X.509 tokenless ' + 'authorization. If the option is absent then ' + 'no certificates will be allowed. ' + 'The naming format for the attributes of a ' + 'Distinguished Name(DN) must be separated by a ' + 'comma and contain no spaces. This configuration ' + 'option may be repeated for multiple values. ' + 'For example: ' + 'trusted_issuer=CN=john,OU=keystone,O=openstack ' + 'trusted_issuer=CN=mary,OU=eng,O=abc'), + cfg.StrOpt('protocol', default='x509', + help='The protocol name for the X.509 tokenless ' + 'authorization along with the option issuer_attribute ' + 'below can look up its corresponding mapping.'), + cfg.StrOpt('issuer_attribute', default='SSL_CLIENT_I_DN', + help='The issuer attribute that is served as an IdP ID ' + 'for the X.509 tokenless authorization along with ' + 'the protocol to look up its corresponding mapping. ' + 'It is the environment variable in the WSGI ' + 'environment that references to the issuer of the ' + 'client certificate.'), + ], 'paste_deploy': [ cfg.StrOpt('config_file', default='keystone-paste.ini', help='Name of the paste configuration file that defines ' diff --git a/keystone-moon/keystone/common/controller.py b/keystone-moon/keystone/common/controller.py index bc7074ac..56bc211a 100644 --- a/keystone-moon/keystone/common/controller.py +++ b/keystone-moon/keystone/common/controller.py @@ -17,6 +17,7 @@ import uuid from oslo_config import cfg from oslo_log import log +from oslo_log import versionutils from oslo_utils import strutils import six @@ -736,6 +737,16 @@ class V3Controller(wsgi.Application): # the current tempest heat tests issue a v3 call without this. # This is raised as bug #1283539. Once this is fixed, we # should remove the line below and replace it with an error. + # + # Ahead of actually changing the code to raise an exception, we + # issue a deprecation warning. + versionutils.report_deprecated_feature( + LOG, + _LW('Not specifying a domain during a create user, group or ' + 'project call, and relying on falling back to the ' + 'default domain, is deprecated as of Liberty and will be ' + 'removed in the N release. Specify the domain explicitly ' + 'or use a domain-scoped token')) return CONF.identity.default_domain_id def _normalize_domain_id(self, context, ref): diff --git a/keystone-moon/keystone/common/manager.py b/keystone-moon/keystone/common/manager.py index 7150fbf3..f98a1763 100644 --- a/keystone-moon/keystone/common/manager.py +++ b/keystone-moon/keystone/common/manager.py @@ -104,3 +104,35 @@ class Manager(object): f = getattr(self.driver, name) setattr(self, name, f) return f + + +def create_legacy_driver(driver_class): + """Helper function to deprecate the original driver classes. + + The keystone.{subsystem}.Driver classes are deprecated in favor of the + new versioned classes. This function creates a new class based on a + versioned class and adds a deprecation message when it is used. + + This will allow existing custom drivers to work when the Driver class is + renamed to include a version. + + Example usage: + + Driver = create_legacy_driver(CatalogDriverV8) + + """ + + module_name = driver_class.__module__ + class_name = driver_class.__name__ + + class Driver(driver_class): + + @versionutils.deprecated( + as_of=versionutils.deprecated.LIBERTY, + what='%s.Driver' % module_name, + in_favor_of='%s.%s' % (module_name, class_name), + remove_in=+2) + def __init__(self, *args, **kwargs): + super(Driver, self).__init__(*args, **kwargs) + + return Driver diff --git a/keystone-moon/keystone/common/sql/migrate_repo/versions/075_confirm_config_registration.py b/keystone-moon/keystone/common/sql/migrate_repo/versions/075_confirm_config_registration.py new file mode 100644 index 00000000..576842c6 --- /dev/null +++ b/keystone-moon/keystone/common/sql/migrate_repo/versions/075_confirm_config_registration.py @@ -0,0 +1,29 @@ +# 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 sqlalchemy as sql + +REGISTRATION_TABLE = 'config_register' + + +def upgrade(migrate_engine): + meta = sql.MetaData() + meta.bind = migrate_engine + + registration_table = sql.Table( + REGISTRATION_TABLE, + meta, + sql.Column('type', sql.String(64), primary_key=True), + sql.Column('domain_id', sql.String(64), nullable=False), + mysql_engine='InnoDB', + mysql_charset='utf8') + registration_table.create(migrate_engine, checkfirst=True) diff --git a/keystone-moon/keystone/common/tokenless_auth.py b/keystone-moon/keystone/common/tokenless_auth.py new file mode 100644 index 00000000..7388b83c --- /dev/null +++ b/keystone-moon/keystone/common/tokenless_auth.py @@ -0,0 +1,193 @@ +# Copyright 2015 Hewlett-Packard +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import hashlib + +from oslo_config import cfg +from oslo_log import log + +from keystone.auth import controllers +from keystone.common import dependency +from keystone.contrib.federation import constants as federation_constants +from keystone.contrib.federation import utils +from keystone import exception +from keystone.i18n import _ + + +CONF = cfg.CONF +LOG = log.getLogger(__name__) + + +@dependency.requires('assignment_api', 'federation_api', + 'identity_api', 'resource_api') +class TokenlessAuthHelper(object): + def __init__(self, env): + """A init class for TokenlessAuthHelper. + + :param env: The HTTP request environment that should contain + client certificate attributes. These attributes should match + with what the mapping defines. Or a user cannot be mapped and + results un-authenticated. The following examples are for the + attributes that reference to the client certificate's Subject's + Common Name and Organization: + SSL_CLIENT_S_DN_CN, SSL_CLIENT_S_DN_O + :type env: dict + """ + + self.env = env + + def _build_scope_info(self): + """Build the token request scope based on the headers. + + :returns: scope data + :rtype: dict + """ + project_id = self.env.get('HTTP_X_PROJECT_ID') + project_name = self.env.get('HTTP_X_PROJECT_NAME') + project_domain_id = self.env.get('HTTP_X_PROJECT_DOMAIN_ID') + project_domain_name = self.env.get('HTTP_X_PROJECT_DOMAIN_NAME') + domain_id = self.env.get('HTTP_X_DOMAIN_ID') + domain_name = self.env.get('HTTP_X_DOMAIN_NAME') + + scope = {} + if project_id: + scope['project'] = {'id': project_id} + elif project_name: + scope['project'] = {'name': project_name} + if project_domain_id: + scope['project']['domain'] = {'id': project_domain_id} + elif project_domain_name: + scope['project']['domain'] = {'name': project_domain_name} + else: + msg = _('Neither Project Domain ID nor Project Domain Name ' + 'was provided.') + raise exception.ValidationError(msg) + elif domain_id: + scope['domain'] = {'id': domain_id} + elif domain_name: + scope['domain'] = {'name': domain_name} + else: + raise exception.ValidationError( + attribute='project or domain', + target='scope') + return scope + + def get_scope(self): + auth = {} + # NOTE(chioleong): auth methods here are insignificant because + # we only care about using auth.controllers.AuthInfo + # to validate the scope information. Therefore, + # we don't provide any identity. + auth['scope'] = self._build_scope_info() + + # NOTE(chioleong): we'll let AuthInfo validate the scope for us + auth_info = controllers.AuthInfo.create({}, auth, scope_only=True) + return auth_info.get_scope() + + def get_mapped_user(self, project_id=None, domain_id=None): + """Map client certificate to an existing user. + + If user is ephemeral, there is no validation on the user himself; + however it will be mapped to a corresponding group(s) and the scope + of this ephemeral user is the same as what is assigned to the group. + + :param project_id: Project scope of the mapped user. + :param domain_id: Domain scope of the mapped user. + :returns: A dictionary that contains the keys, such as + user_id, user_name, domain_id, domain_name + :rtype: dict + """ + idp_id = self._build_idp_id() + LOG.debug('The IdP Id %s and protocol Id %s are used to look up ' + 'the mapping.', idp_id, CONF.tokenless_auth.protocol) + + mapped_properties, mapping_id = self.federation_api.evaluate( + idp_id, CONF.tokenless_auth.protocol, self.env) + + user = mapped_properties.get('user', {}) + user_id = user.get('id') + user_name = user.get('name') + user_type = user.get('type') + if user.get('domain') is not None: + user_domain_id = user.get('domain').get('id') + user_domain_name = user.get('domain').get('name') + else: + user_domain_id = None + user_domain_name = None + + # if user is ephemeral type, we don't care if the user exists + # or not, but just care if the mapped group(s) is valid. + if user_type == utils.UserType.EPHEMERAL: + user_ref = {'type': utils.UserType.EPHEMERAL} + group_ids = mapped_properties['group_ids'] + utils.validate_groups_in_backend(group_ids, + mapping_id, + self.identity_api) + group_ids.extend( + utils.transform_to_group_ids( + mapped_properties['group_names'], mapping_id, + self.identity_api, self.assignment_api)) + roles = self.assignment_api.get_roles_for_groups(group_ids, + project_id, + domain_id) + if roles is not None: + role_names = [role['name'] for role in roles] + user_ref['roles'] = role_names + user_ref['group_ids'] = list(group_ids) + user_ref[federation_constants.IDENTITY_PROVIDER] = idp_id + user_ref[federation_constants.PROTOCOL] = ( + CONF.tokenless_auth.protocol) + return user_ref + + if user_id: + user_ref = self.identity_api.get_user(user_id) + elif user_name and (user_domain_name or user_domain_id): + if user_domain_name: + user_domain = self.resource_api.get_domain_by_name( + user_domain_name) + self.resource_api.assert_domain_enabled(user_domain['id'], + user_domain) + user_domain_id = user_domain['id'] + user_ref = self.identity_api.get_user_by_name(user_name, + user_domain_id) + else: + msg = _('User auth cannot be built due to missing either ' + 'user id, or user name with domain id, or user name ' + 'with domain name.') + raise exception.ValidationError(msg) + self.identity_api.assert_user_enabled( + user_id=user_ref['id'], + user=user_ref) + user_ref['type'] = utils.UserType.LOCAL + return user_ref + + def _build_idp_id(self): + """Build the IdP name from the given config option issuer_attribute. + + The default issuer attribute SSL_CLIENT_I_DN in the environment is + built with the following formula - + + base64_idp = sha1(env['SSL_CLIENT_I_DN']) + + :returns: base64_idp like the above example + :rtype: str + """ + idp = self.env.get(CONF.tokenless_auth.issuer_attribute) + if idp is None: + raise exception.TokenlessAuthConfigError( + issuer_attribute=CONF.tokenless_auth.issuer_attribute) + + hashed_idp = hashlib.sha256(idp) + return hashed_idp.hexdigest() diff --git a/keystone-moon/keystone/common/wsgi.py b/keystone-moon/keystone/common/wsgi.py index 0dee954b..8b99c87d 100644 --- a/keystone-moon/keystone/common/wsgi.py +++ b/keystone-moon/keystone/common/wsgi.py @@ -660,7 +660,8 @@ class RoutersBase(object): get_action=None, head_action=None, get_head_action=None, put_action=None, post_action=None, patch_action=None, delete_action=None, get_post_action=None, - path_vars=None, status=json_home.Status.STABLE): + path_vars=None, status=json_home.Status.STABLE, + new_path=None): if get_head_action: getattr(controller, get_head_action) # ensure the attribute exists mapper.connect(path, controller=controller, action=get_head_action, @@ -697,10 +698,10 @@ class RoutersBase(object): resource_data = dict() if path_vars: - resource_data['href-template'] = path + resource_data['href-template'] = new_path or path resource_data['href-vars'] = path_vars else: - resource_data['href'] = path + resource_data['href'] = new_path or path json_home.Status.update_resource_data(resource_data, status) diff --git a/keystone-moon/keystone/contrib/endpoint_filter/backends/sql.py b/keystone-moon/keystone/contrib/endpoint_filter/backends/sql.py index 53d511e5..cf904268 100644 --- a/keystone-moon/keystone/contrib/endpoint_filter/backends/sql.py +++ b/keystone-moon/keystone/contrib/endpoint_filter/backends/sql.py @@ -53,7 +53,7 @@ class ProjectEndpointGroupMembership(sql.ModelBase, sql.ModelDictMixin): 'project_id'), {}) -class EndpointFilter(endpoint_filter.Driver): +class EndpointFilter(endpoint_filter.EndpointFilterDriverV8): @sql.handle_conflicts(conflict_type='project_endpoint') def add_endpoint_to_project(self, endpoint_id, project_id): diff --git a/keystone-moon/keystone/contrib/endpoint_filter/core.py b/keystone-moon/keystone/contrib/endpoint_filter/core.py index 1cb35b1f..b66465ea 100644 --- a/keystone-moon/keystone/contrib/endpoint_filter/core.py +++ b/keystone-moon/keystone/contrib/endpoint_filter/core.py @@ -65,7 +65,7 @@ class Manager(manager.Manager): @six.add_metaclass(abc.ABCMeta) -class Driver(object): +class EndpointFilterDriverV8(object): """Interface description for an Endpoint Filter driver.""" @abc.abstractmethod @@ -291,3 +291,6 @@ class Driver(object): """ raise exception.NotImplemented() # pragma: no cover + + +Driver = manager.create_legacy_driver(EndpointFilterDriverV8) diff --git a/keystone-moon/keystone/contrib/federation/backends/sql.py b/keystone-moon/keystone/contrib/federation/backends/sql.py index ed07c08f..dbd17025 100644 --- a/keystone-moon/keystone/contrib/federation/backends/sql.py +++ b/keystone-moon/keystone/contrib/federation/backends/sql.py @@ -155,7 +155,7 @@ class ServiceProviderModel(sql.ModelBase, sql.DictBase): return d -class Federation(core.Driver): +class Federation(core.FederationDriverV8): # Identity Provider CRUD @sql.handle_conflicts(conflict_type='identity_provider') diff --git a/keystone-moon/keystone/contrib/federation/controllers.py b/keystone-moon/keystone/contrib/federation/controllers.py index 912d45d5..d0bd2bce 100644 --- a/keystone-moon/keystone/contrib/federation/controllers.py +++ b/keystone-moon/keystone/contrib/federation/controllers.py @@ -316,6 +316,15 @@ class Auth(auth_controllers.Auth): token_id = res.headers['X-Subject-Token'] return self.render_html_response(host, token_id) + def federated_idp_specific_sso_auth(self, context, idp_id, protocol_id): + host = self._get_sso_origin_host(context) + + # NOTE(lbragstad): We validate that the Identity Provider actually + # exists in the Mapped authentication plugin. + res = self.federated_authentication(context, idp_id, protocol_id) + token_id = res.headers['X-Subject-Token'] + return self.render_html_response(host, token_id) + def render_html_response(self, host, token_id): """Forms an HTML Form from a template with autosubmit.""" diff --git a/keystone-moon/keystone/contrib/federation/core.py b/keystone-moon/keystone/contrib/federation/core.py index 2ab75ecb..1595be1d 100644 --- a/keystone-moon/keystone/contrib/federation/core.py +++ b/keystone-moon/keystone/contrib/federation/core.py @@ -92,7 +92,7 @@ class Manager(manager.Manager): @six.add_metaclass(abc.ABCMeta) -class Driver(object): +class FederationDriverV8(object): @abc.abstractmethod def create_idp(self, idp_id, idp): @@ -350,3 +350,6 @@ class Driver(object): """ raise exception.NotImplemented() # pragma: no cover + + +Driver = manager.create_legacy_driver(FederationDriverV8) diff --git a/keystone-moon/keystone/contrib/federation/idp.py b/keystone-moon/keystone/contrib/federation/idp.py index 739fc01a..51689989 100644 --- a/keystone-moon/keystone/contrib/federation/idp.py +++ b/keystone-moon/keystone/contrib/federation/idp.py @@ -12,7 +12,6 @@ import datetime import os -import subprocess import uuid from oslo_config import cfg @@ -32,11 +31,14 @@ xmldsig = importutils.try_import("saml2.xmldsig") if not xmldsig: xmldsig = importutils.try_import("xmldsig") +from keystone.common import environment from keystone.common import utils from keystone import exception from keystone.i18n import _, _LE +subprocess = environment.subprocess + LOG = log.getLogger(__name__) CONF = cfg.CONF @@ -426,11 +428,10 @@ def _sign_assertion(assertion): stdout = subprocess.check_output(command_list, stderr=subprocess.STDOUT) except Exception as e: - msg = _LE('Error when signing assertion, reason: %(reason)s') - msg = msg % {'reason': e} - if hasattr(e, 'output'): - msg += ' output: %(output)s' % {'output': e.output} - LOG.error(msg) + msg = _LE('Error when signing assertion, reason: %(reason)s%(output)s') + LOG.error(msg, + {'reason': e, + 'output': ' ' + e.output if hasattr(e, 'output') else ''}) raise exception.SAMLSigningError(reason=e) finally: try: diff --git a/keystone-moon/keystone/contrib/federation/routers.py b/keystone-moon/keystone/contrib/federation/routers.py index d8fa8175..ddf2f61f 100644 --- a/keystone-moon/keystone/contrib/federation/routers.py +++ b/keystone-moon/keystone/contrib/federation/routers.py @@ -72,6 +72,13 @@ class FederationExtension(wsgi.V3ExtensionRouter): protocols/{protocol}/auth POST /OS-FEDERATION/identity_providers/{identity_provider}/ protocols/{protocol}/auth + GET /auth/OS-FEDERATION/identity_providers/ + {idp_id}/protocols/{protocol_id}/websso + ?origin=https%3A//horizon.example.com + POST /auth/OS-FEDERATION/identity_providers/ + {idp_id}/protocols/{protocol_id}/websso + ?origin=https%3A//horizon.example.com + POST /auth/OS-FEDERATION/saml2 POST /auth/OS-FEDERATION/saml2/ecp @@ -185,11 +192,13 @@ class FederationExtension(wsgi.V3ExtensionRouter): self._add_resource( mapper, domain_controller, path=self._construct_url('domains'), + new_path='/auth/domains', get_action='list_domains_for_groups', rel=build_resource_relation(resource_name='domains')) self._add_resource( mapper, project_controller, path=self._construct_url('projects'), + new_path='/auth/projects', get_action='list_projects_for_groups', rel=build_resource_relation(resource_name='projects')) @@ -223,6 +232,16 @@ class FederationExtension(wsgi.V3ExtensionRouter): path_vars={ 'protocol_id': PROTOCOL_ID_PARAMETER_RELATION, }) + self._add_resource( + mapper, auth_controller, + path='/auth' + self._construct_url( + 'identity_providers/{idp_id}/protocols/{protocol_id}/websso'), + get_post_action='federated_idp_specific_sso_auth', + rel=build_resource_relation(resource_name='identity_providers'), + path_vars={ + 'idp_id': IDP_ID_PARAMETER_RELATION, + 'protocol_id': PROTOCOL_ID_PARAMETER_RELATION, + }) # Keystone-Identity-Provider metadata endpoint self._add_resource( diff --git a/keystone-moon/keystone/contrib/federation/utils.py b/keystone-moon/keystone/contrib/federation/utils.py index b0db3cdd..bde19cfd 100644 --- a/keystone-moon/keystone/contrib/federation/utils.py +++ b/keystone-moon/keystone/contrib/federation/utils.py @@ -672,15 +672,18 @@ class RuleProcessor(object): for requirement in requirements: requirement_type = requirement['type'] + direct_map_values = assertion.get(requirement_type) regex = requirement.get('regex', False) + if not direct_map_values: + return None + any_one_values = requirement.get(self._EvalType.ANY_ONE_OF) if any_one_values is not None: if self._evaluate_requirement(any_one_values, - requirement_type, + direct_map_values, self._EvalType.ANY_ONE_OF, - regex, - assertion): + regex): continue else: return None @@ -688,10 +691,9 @@ class RuleProcessor(object): not_any_values = requirement.get(self._EvalType.NOT_ANY_OF) if not_any_values is not None: if self._evaluate_requirement(not_any_values, - requirement_type, + direct_map_values, self._EvalType.NOT_ANY_OF, - regex, - assertion): + regex): continue else: return None @@ -699,23 +701,21 @@ class RuleProcessor(object): # If 'any_one_of' or 'not_any_of' are not found, then values are # within 'type'. Attempt to find that 'type' within the assertion, # and filter these values if 'whitelist' or 'blacklist' is set. - direct_map_values = assertion.get(requirement_type) - if direct_map_values: - blacklisted_values = requirement.get(self._EvalType.BLACKLIST) - whitelisted_values = requirement.get(self._EvalType.WHITELIST) + blacklisted_values = requirement.get(self._EvalType.BLACKLIST) + whitelisted_values = requirement.get(self._EvalType.WHITELIST) - # If a blacklist or whitelist is used, we want to map to the - # whole list instead of just its values separately. - if blacklisted_values is not None: - direct_map_values = [v for v in direct_map_values - if v not in blacklisted_values] - elif whitelisted_values is not None: - direct_map_values = [v for v in direct_map_values - if v in whitelisted_values] + # If a blacklist or whitelist is used, we want to map to the + # whole list instead of just its values separately. + if blacklisted_values is not None: + direct_map_values = [v for v in direct_map_values + if v not in blacklisted_values] + elif whitelisted_values is not None: + direct_map_values = [v for v in direct_map_values + if v in whitelisted_values] - direct_maps.add(direct_map_values) + direct_maps.add(direct_map_values) - LOG.debug('updating a direct mapping: %s', direct_map_values) + LOG.debug('updating a direct mapping: %s', direct_map_values) return direct_maps @@ -726,8 +726,8 @@ class RuleProcessor(object): return True return False - def _evaluate_requirement(self, values, requirement_type, - eval_type, regex, assertion): + def _evaluate_requirement(self, values, assertion_values, + eval_type, regex): """Evaluate the incoming requirement and assertion. If the requirement type does not exist in the assertion data, then @@ -737,23 +737,16 @@ class RuleProcessor(object): :param values: list of allowed values, defined in the requirement :type values: list - :param requirement_type: key to look for in the assertion - :type requirement_type: string + :param assertion_values: The values from the assertion to evaluate + :type assertion_values: list/string :param eval_type: determine how to evaluate requirements :type eval_type: string :param regex: perform evaluation with regex :type regex: boolean - :param assertion: dict of attributes from the IdP - :type assertion: dict :returns: boolean, whether requirement is valid or not. """ - - assertion_values = assertion.get(requirement_type) - if not assertion_values: - return False - if regex: any_match = self._evaluate_values_by_regex(values, assertion_values) diff --git a/keystone-moon/keystone/contrib/oauth1/core.py b/keystone-moon/keystone/contrib/oauth1/core.py index d7f64dc4..6406a803 100644 --- a/keystone-moon/keystone/contrib/oauth1/core.py +++ b/keystone-moon/keystone/contrib/oauth1/core.py @@ -199,7 +199,7 @@ class Manager(manager.Manager): @six.add_metaclass(abc.ABCMeta) -class Driver(object): +class Oauth1DriverV8(object): """Interface description for an OAuth1 driver.""" @abc.abstractmethod @@ -362,3 +362,6 @@ class Driver(object): """ raise exception.NotImplemented() # pragma: no cover + + +Driver = manager.create_legacy_driver(Oauth1DriverV8) diff --git a/keystone-moon/keystone/contrib/revoke/backends/kvs.py b/keystone-moon/keystone/contrib/revoke/backends/kvs.py index 349ed6e3..086becb0 100644 --- a/keystone-moon/keystone/contrib/revoke/backends/kvs.py +++ b/keystone-moon/keystone/contrib/revoke/backends/kvs.py @@ -27,7 +27,7 @@ _EVENT_KEY = 'os-revoke-events' _KVS_BACKEND = 'openstack.kvs.Memory' -class Revoke(revoke.Driver): +class Revoke(revoke.RevokeDriverV8): @versionutils.deprecated( versionutils.deprecated.JUNO, diff --git a/keystone-moon/keystone/contrib/revoke/backends/sql.py b/keystone-moon/keystone/contrib/revoke/backends/sql.py index dd7fdd19..82e05194 100644 --- a/keystone-moon/keystone/contrib/revoke/backends/sql.py +++ b/keystone-moon/keystone/contrib/revoke/backends/sql.py @@ -38,7 +38,7 @@ class RevocationEvent(sql.ModelBase, sql.ModelDictMixin): audit_chain_id = sql.Column(sql.String(32)) -class Revoke(revoke.Driver): +class Revoke(revoke.RevokeDriverV8): def _flush_batch_size(self, dialect): batch_size = 0 if dialect == 'ibm_db_sa': diff --git a/keystone-moon/keystone/contrib/revoke/core.py b/keystone-moon/keystone/contrib/revoke/core.py index e1ab87c8..3b108c9e 100644 --- a/keystone-moon/keystone/contrib/revoke/core.py +++ b/keystone-moon/keystone/contrib/revoke/core.py @@ -232,7 +232,7 @@ class Manager(manager.Manager): @six.add_metaclass(abc.ABCMeta) -class Driver(object): +class RevokeDriverV8(object): """Interface for recording and reporting revocation events.""" @abc.abstractmethod @@ -257,3 +257,6 @@ class Driver(object): """ raise exception.NotImplemented() # pragma: no cover + + +Driver = manager.create_legacy_driver(RevokeDriverV8) diff --git a/keystone-moon/keystone/contrib/revoke/model.py b/keystone-moon/keystone/contrib/revoke/model.py index 1a23d57d..e677bfb5 100644 --- a/keystone-moon/keystone/contrib/revoke/model.py +++ b/keystone-moon/keystone/contrib/revoke/model.py @@ -220,7 +220,7 @@ class RevokeTree(object): # The last (leaf) level is checked in a special way because we # verify issued_at field differently. try: - return revoke_map['issued_before'] > token_data['issued_at'] + return revoke_map['issued_before'] >= token_data['issued_at'] except KeyError: return False diff --git a/keystone-moon/keystone/credential/backends/sql.py b/keystone-moon/keystone/credential/backends/sql.py index 12daed3f..6dc9cd65 100644 --- a/keystone-moon/keystone/credential/backends/sql.py +++ b/keystone-moon/keystone/credential/backends/sql.py @@ -29,7 +29,7 @@ class CredentialModel(sql.ModelBase, sql.DictBase): extra = sql.Column(sql.JsonBlob()) -class Credential(credential.Driver): +class Credential(credential.CredentialDriverV8): # credential crud diff --git a/keystone-moon/keystone/credential/controllers.py b/keystone-moon/keystone/credential/controllers.py index 65c17278..321acc48 100644 --- a/keystone-moon/keystone/credential/controllers.py +++ b/keystone-moon/keystone/credential/controllers.py @@ -81,7 +81,7 @@ class CredentialV3(controller.V3Controller): else: return ref - @controller.filterprotected('user_id') + @controller.filterprotected('user_id', 'type') def list_credentials(self, context, filters): hints = CredentialV3.build_driver_hints(context, filters) refs = self.credential_api.list_credentials(hints) diff --git a/keystone-moon/keystone/credential/core.py b/keystone-moon/keystone/credential/core.py index 2368439e..d72856df 100644 --- a/keystone-moon/keystone/credential/core.py +++ b/keystone-moon/keystone/credential/core.py @@ -51,7 +51,7 @@ class Manager(manager.Manager): @six.add_metaclass(abc.ABCMeta) -class Driver(object): +class CredentialDriverV8(object): # credential crud @abc.abstractmethod @@ -140,3 +140,6 @@ class Driver(object): except exception.CredentialNotFound: LOG.debug('Deletion of credential is not required: %s', cr['id']) + + +Driver = manager.create_legacy_driver(CredentialDriverV8) diff --git a/keystone-moon/keystone/endpoint_policy/core.py b/keystone-moon/keystone/endpoint_policy/core.py index 3e8026e6..e176ac1c 100644 --- a/keystone-moon/keystone/endpoint_policy/core.py +++ b/keystone-moon/keystone/endpoint_policy/core.py @@ -264,7 +264,7 @@ class Manager(manager.Manager): @six.add_metaclass(abc.ABCMeta) -class Driver(object): +class EndpointPolicyDriverV8(object): """Interface description for an Endpoint Policy driver.""" @abc.abstractmethod @@ -431,3 +431,6 @@ class Driver(object): """ raise exception.NotImplemented() # pragma: no cover + + +Driver = manager.create_legacy_driver(EndpointPolicyDriverV8) diff --git a/keystone-moon/keystone/exception.py b/keystone-moon/keystone/exception.py index 8e573c4c..f5c0e1cd 100644 --- a/keystone-moon/keystone/exception.py +++ b/keystone-moon/keystone/exception.py @@ -357,6 +357,13 @@ class DomainConfigNotFound(NotFound): 'configuration for domain %(domain_id)s') +class ConfigRegistrationNotFound(Exception): + # This is used internally between the domain config backend and the + # manager, so should not escape to the client. If it did, it is a coding + # error on our part, and would end up, appropriately, as a 500 error. + pass + + class Conflict(Error): message_format = _("Conflict occurred attempting to store %(type)s -" " %(details)s") @@ -480,3 +487,9 @@ class OAuthHeadersMissingError(UnexpectedError): 'HTTPd or Apache, ensure WSGIPassAuthorization ' 'is set to On.') title = 'Error retrieving OAuth headers' + + +class TokenlessAuthConfigError(ValidationError): + message_format = _('Could not determine Identity Provider ID. The ' + 'configuration option %(issuer_attribute)s ' + 'was not found in the request environment.') diff --git a/keystone-moon/keystone/identity/backends/ldap.py b/keystone-moon/keystone/identity/backends/ldap.py index 7a3cb03b..1f33bacb 100644 --- a/keystone-moon/keystone/identity/backends/ldap.py +++ b/keystone-moon/keystone/identity/backends/ldap.py @@ -32,7 +32,7 @@ CONF = cfg.CONF LOG = log.getLogger(__name__) -class Identity(identity.Driver): +class Identity(identity.IdentityDriverV8): def __init__(self, conf=None): super(Identity, self).__init__() if conf is None: diff --git a/keystone-moon/keystone/identity/backends/sql.py b/keystone-moon/keystone/identity/backends/sql.py index 8bda9a1b..d37240eb 100644 --- a/keystone-moon/keystone/identity/backends/sql.py +++ b/keystone-moon/keystone/identity/backends/sql.py @@ -70,7 +70,7 @@ class UserGroupMembership(sql.ModelBase, sql.DictBase): primary_key=True) -class Identity(identity.Driver): +class Identity(identity.IdentityDriverV8): # NOTE(henry-nash): Override the __init__() method so as to take a # config parameter to enable sql to be used as a domain-specific driver. def __init__(self, conf=None): diff --git a/keystone-moon/keystone/identity/core.py b/keystone-moon/keystone/identity/core.py index 612a1859..061b82e1 100644 --- a/keystone-moon/keystone/identity/core.py +++ b/keystone-moon/keystone/identity/core.py @@ -44,6 +44,14 @@ MEMOIZE = cache.get_memoization_decorator(section='identity') DOMAIN_CONF_FHEAD = 'keystone.' DOMAIN_CONF_FTAIL = '.conf' +# The number of times we will attempt to register a domain to use the SQL +# driver, if we find that another process is in the middle of registering or +# releasing at the same time as us. +REGISTRATION_ATTEMPTS = 10 + +# Config Registration Types +SQL_DRIVER = 'SQL' + def filter_user(user_ref): """Filter out private items in a user dict. @@ -67,7 +75,7 @@ def filter_user(user_ref): return user_ref -@dependency.requires('domain_config_api') +@dependency.requires('domain_config_api', 'resource_api') class DomainConfigs(dict): """Discover, store and provide access to domain specific configs. @@ -95,7 +103,7 @@ class DomainConfigs(dict): def _assert_no_more_than_one_sql_driver(self, domain_id, new_config, config_file=None): - """Ensure there is more than one sql driver. + """Ensure there is no more than one sql driver. Check to see if the addition of the driver in this new config would cause there to now be more than one sql driver. @@ -108,6 +116,13 @@ class DomainConfigs(dict): (self.driver.is_sql or self._any_sql)): # The addition of this driver would cause us to have more than # one sql driver, so raise an exception. + + # TODO(henry-nash): This method is only used in the file-based + # case, so has no need to worry about the database/API case. The + # code that overrides config_file below is therefore never used + # and should be removed, and this method perhaps moved inside + # _load_config_from_file(). This is raised as bug #1466772. + if not config_file: config_file = _('Database at /domains/%s/config') % domain_id raise exception.MultipleSQLDriversInConfig(source=config_file) @@ -177,24 +192,99 @@ class DomainConfigs(dict): def _load_config_from_database(self, domain_id, specific_config): - def _assert_not_sql_driver(domain_id, new_config): - """Ensure this is not an sql driver. + def _assert_no_more_than_one_sql_driver(domain_id, new_config): + """Ensure adding driver doesn't push us over the limit of 1 - Due to multi-threading safety concerns, we do not currently support - the setting of a specific identity driver to sql via the Identity - API. + The checks we make in this method need to take into account that + we may be in a multiple process configuration and ensure that + any race conditions are avoided. """ - if new_config['driver'].is_sql: - reason = _('Domain specific sql drivers are not supported via ' - 'the Identity API. One is specified in ' - '/domains/%s/config') % domain_id - raise exception.InvalidDomainConfig(reason=reason) + if not new_config['driver'].is_sql: + self.domain_config_api.release_registration(domain_id) + return + + # To ensure the current domain is the only SQL driver, we attempt + # to register our use of SQL. If we get it we know we are good, + # if we fail to register it then we should: + # + # - First check if another process has registered for SQL for our + # domain, in which case we are fine + # - If a different domain has it, we should check that this domain + # is still valid, in case, for example, domain deletion somehow + # failed to remove its registration (i.e. we self heal for these + # kinds of issues). + + domain_registered = 'Unknown' + for attempt in range(REGISTRATION_ATTEMPTS): + if self.domain_config_api.obtain_registration( + domain_id, SQL_DRIVER): + LOG.debug('Domain %s successfully registered to use the ' + 'SQL driver.', domain_id) + return + + # We failed to register our use, let's find out who is using it + try: + domain_registered = ( + self.domain_config_api.read_registration( + SQL_DRIVER)) + except exception.ConfigRegistrationNotFound: + msg = ('While attempting to register domain %(domain)s to ' + 'use the SQL driver, another process released it, ' + 'retrying (attempt %(attempt)s).') + LOG.debug(msg, {'domain': domain_id, + 'attempt': attempt + 1}) + continue + + if domain_registered == domain_id: + # Another process already registered it for us, so we are + # fine. In the race condition when another process is + # in the middle of deleting this domain, we know the domain + # is already disabled and hence telling the caller that we + # are registered is benign. + LOG.debug('While attempting to register domain %s to use ' + 'the SQL driver, found that another process had ' + 'already registered this domain. This is normal ' + 'in multi-process configurations.', domain_id) + return + + # So we don't have it, but someone else does...let's check that + # this domain is still valid + try: + self.resource_api.get_domain(domain_registered) + except exception.DomainNotFound: + msg = ('While attempting to register domain %(domain)s to ' + 'use the SQL driver, found that it was already ' + 'registered to a domain that no longer exists ' + '(%(old_domain)s). Removing this stale ' + 'registration and retrying (attempt %(attempt)s).') + LOG.debug(msg, {'domain': domain_id, + 'old_domain': domain_registered, + 'attempt': attempt + 1}) + self.domain_config_api.release_registration( + domain_registered, type=SQL_DRIVER) + continue + + # The domain is valid, so we really do have an attempt at more + # than one SQL driver. + details = ( + _('Config API entity at /domains/%s/config') % domain_id) + raise exception.MultipleSQLDriversInConfig(source=details) + + # We fell out of the loop without either registering our domain or + # being able to find who has it...either we were very very very + # unlucky or something is awry. + msg = _('Exceeded attempts to register domain %(domain)s to use ' + 'the SQL driver, the last domain that appears to have ' + 'had it is %(last_domain)s, giving up') % { + 'domain': domain_id, 'last_domain': domain_registered} + raise exception.UnexpectedError(msg) domain_config = {} domain_config['cfg'] = cfg.ConfigOpts() config.configure(conf=domain_config['cfg']) - domain_config['cfg'](args=[], project='keystone') + domain_config['cfg'](args=[], project='keystone', + default_config_files=[]) # Override any options that have been passed in as specified in the # database. @@ -206,7 +296,7 @@ class DomainConfigs(dict): domain_config['cfg_overrides'] = specific_config domain_config['driver'] = self._load_driver(domain_config) - _assert_not_sql_driver(domain_id, domain_config) + _assert_no_more_than_one_sql_driver(domain_id, domain_config) self[domain_id] = domain_config def _setup_domain_drivers_from_database(self, standard_driver, @@ -1066,7 +1156,7 @@ class Manager(manager.Manager): @six.add_metaclass(abc.ABCMeta) -class Driver(object): +class IdentityDriverV8(object): """Interface description for an Identity driver.""" def _get_list_limit(self): @@ -1280,6 +1370,9 @@ class Driver(object): # end of identity +Driver = manager.create_legacy_driver(IdentityDriverV8) + + @dependency.provider('id_mapping_api') class MappingManager(manager.Manager): """Default pivot point for the ID Mapping backend.""" @@ -1291,7 +1384,7 @@ class MappingManager(manager.Manager): @six.add_metaclass(abc.ABCMeta) -class MappingDriver(object): +class MappingDriverV8(object): """Interface description for an ID Mapping driver.""" @abc.abstractmethod @@ -1350,3 +1443,6 @@ class MappingDriver(object): """ raise exception.NotImplemented() # pragma: no cover + + +MappingDriver = manager.create_legacy_driver(MappingDriverV8) diff --git a/keystone-moon/keystone/identity/mapping_backends/sql.py b/keystone-moon/keystone/identity/mapping_backends/sql.py index b2f9cb95..7ab4ef52 100644 --- a/keystone-moon/keystone/identity/mapping_backends/sql.py +++ b/keystone-moon/keystone/identity/mapping_backends/sql.py @@ -36,7 +36,7 @@ class IDMapping(sql.ModelBase, sql.ModelDictMixin): @dependency.requires('id_generator_api') -class Mapping(identity.MappingDriver): +class Mapping(identity.MappingDriverV8): def get_public_id(self, local_entity): # NOTE(henry-nash): Since the Public ID is regeneratable, rather diff --git a/keystone-moon/keystone/locale/de/LC_MESSAGES/keystone.po b/keystone-moon/keystone/locale/de/LC_MESSAGES/keystone.po new file mode 100644 index 00000000..1b2f1094 --- /dev/null +++ b/keystone-moon/keystone/locale/de/LC_MESSAGES/keystone.po @@ -0,0 +1,1303 @@ +# German translations for keystone. +# Copyright (C) 2015 OpenStack Foundation +# This file is distributed under the same license as the keystone project. +# +# Translators: +# Ettore Atalan <atalanttore@googlemail.com>, 2014 +# Robert Simai, 2014 +# Reik Keutterling <spielkind@gmail.com>, 2015 +# OpenStack Infra <zanata@openstack.org>, 2015. #zanata +# Tom Cocozzello <tjcocozz@us.ibm.com>, 2015. #zanata +msgid "" +msgstr "" +"Project-Id-Version: keystone 9.0.0.dev14\n" +"Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" +"POT-Creation-Date: 2015-09-24 06:09+0000\n" +"PO-Revision-Date: 2015-09-03 12:54+0000\n" +"Last-Translator: openstackjenkins <jenkins@openstack.org>\n" +"Language-Team: German (http://www.transifex.com/openstack/keystone/language/" +"de/)\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\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" + +#, python-format +msgid "%(detail)s" +msgstr "%(detail)s" + +#, python-format +msgid "" +"%(event)s is not a valid notification event, must be one of: %(actions)s" +msgstr "" +"%(event)s ist kein gültiges Benachrichtigungsereignis; erforderlich ist " +"%(actions)s" + +#, python-format +msgid "%(host)s is not a trusted dashboard host" +msgstr "%(host)s ist kein vertrauenswürdiger Dashboard-Host" + +#, python-format +msgid "%(message)s %(amendment)s" +msgstr "%(message)s %(amendment)s" + +#, python-format +msgid "" +"%(mod_name)s doesn't provide database migrations. The migration repository " +"path at %(path)s doesn't exist or isn't a directory." +msgstr "" +"%(mod_name)s bietet keine Datenbankmigrationen. Der Migrations-Repository-" +"Pfad unter %(path)s ist nicht vorhanden oder ist kein Verzeichnis." + +#, python-format +msgid "%(property_name)s cannot be less than %(min_length)s characters." +msgstr "%(property_name)s darf nicht kleiner als %(min_length)s Zeichen sein." + +#, python-format +msgid "%(property_name)s is not a %(display_expected_type)s" +msgstr "%(property_name)s ist nicht %(display_expected_type)s" + +#, python-format +msgid "%(property_name)s should not be greater than %(max_length)s characters." +msgstr "%(property_name)s sollte nicht größer als %(max_length)s Zeichen sein." + +#, python-format +msgid "%s cannot be empty." +msgstr "%s darf nicht leer sein." + +#, python-format +msgid "%s extension does not exist." +msgstr "Erweiterung %s ist nicht vorhanden." + +#, python-format +msgid "%s field is required and cannot be empty" +msgstr "%s-Feld ist erforderlich und darf nicht leer sein" + +#, python-format +msgid "%s field(s) cannot be empty" +msgstr "%s-Felder können nicht leer sein" + +msgid "(Disable debug mode to suppress these details.)" +msgstr "(Debugmodus inaktivieren, um diese Details zu unterdrücken.)" + +msgid "--all option cannot be mixed with other options" +msgstr "--all-Option kann nicht zusammen mit anderen Optionen verwendet werden" + +msgid "A project-scoped token is required to produce a service catalog." +msgstr "" +"Ein projektorientiertes Token ist zum Produzieren eines Servicekatalogs " +"erforderlich." + +msgid "Access token is expired" +msgstr "Zugriffstoken ist abgelaufen" + +msgid "Access token not found" +msgstr "Zugriffstoken nicht gefunden" + +msgid "Additional authentications steps required." +msgstr "Zusätzliche Authentifizierungsschritte sind notwendig." + +msgid "An unexpected error occurred when retrieving domain configs" +msgstr "" +"Beim Abrufen der Domänenkonfigurationen ist ein unerwarteter Fehler " +"aufgetreten" + +#, python-format +msgid "An unexpected error occurred when trying to store %s" +msgstr "Beim Versuch, %s zu speichern, ist ein unerwarteter Fehler aufgetreten" + +msgid "An unexpected error prevented the server from fulfilling your request." +msgstr "" +"Wegen eines unerwarteten Fehlers konnte der Server Ihre Anforderung nicht " +"ausführen." + +#, python-format +msgid "" +"An unexpected error prevented the server from fulfilling your request: " +"%(exception)s" +msgstr "" +"Wegen eines unerwarteten Fehlers konnte der Server Ihre Anforderung nicht " +"ausführen: %(exception)s" + +msgid "An unhandled exception has occurred: Could not find metadata." +msgstr "" +"Eine nicht behandelte Ausnahme ist aufgetreten: Metadaten konnten nicht " +"gefunden werden." + +msgid "At least one option must be provided" +msgstr "Mindestens eine Option muss angegeben werden" + +msgid "At least one option must be provided, use either --all or --domain-name" +msgstr "" +"Mindestens eine Option muss angegeben werden. Verwenden Sie entweder --all " +"oder --domain-name" + +msgid "At least one role should be specified." +msgstr "Mindestens eine Rolle sollte angegeben werden." + +msgid "Attempted to authenticate with an unsupported method." +msgstr "Versuch einer Authentifizierung mit einer nicht unterstützten Methode." + +msgid "" +"Attempting to use OS-FEDERATION token with V2 Identity Service, use V3 " +"Authentication" +msgstr "" +"Versuch, OS-FEDERATION-Token mit V2 Identity Service zu verwenden, verwenden " +"Sie v3- Authentifizierung" + +msgid "Authentication plugin error." +msgstr "Authentifizierung-Plug-in-Fehler" + +msgid "Cannot authorize a request token with a token issued via delegation." +msgstr "" +"Anforderungstoken kann mit einem per Delegierung ausgegebenen Token nicht " +"autorisiert werden." + +#, python-format +msgid "Cannot change %(option_name)s %(attr)s" +msgstr "%(option_name)s %(attr)s kann nicht geändert werden" + +msgid "Cannot change Domain ID" +msgstr "Die Domänen-ID kann nicht geändert werden" + +msgid "Cannot change consumer secret" +msgstr "Konsumentengeheimnis kann nicht geändert werden" + +msgid "Cannot change user ID" +msgstr "Benutzer-ID kann nicht geändert werden" + +msgid "Cannot change user name" +msgstr "Benutzername kann nicht geändert werden" + +#, python-format +msgid "Cannot create project with parent: %(project_id)s" +msgstr "" +"Projekt kann nicht mit dem übergeordneten Element %(project_id)s erstellt " +"werden" + +#, python-format +msgid "Cannot duplicate name %s" +msgstr "Der Name %s kann nicht dupliziert werden." + +msgid "Cannot list request tokens with a token issued via delegation." +msgstr "" +"Anforderungstokens können mit einem per Delegierung ausgegebenen Token nicht " +"aufgelistet werden." + +#, python-format +msgid "Cannot open certificate %(cert_file)s. Reason: %(reason)s" +msgstr "" +"Zertifikat %(cert_file)s kann nicht geöffnet werden. Ursache: %(reason)s" + +#, python-format +msgid "Cannot remove role that has not been granted, %s" +msgstr "Nicht gewährte Rolle kann nicht entfernt werden, %s" + +msgid "" +"Cannot truncate a driver call without hints list as first parameter after " +"self " +msgstr "" +"Abschneiden eines Treiberaufrufs ohne Hinweisliste als erstem Parameter nach " +"dem Treiber nicht möglich " + +msgid "" +"Cannot use parents_as_list and parents_as_ids query params at the same time." +msgstr "" +"Die Abfrageparameter parents_as_list und parents_as_ids können nicht " +"gleichzeitig verwendet werden." + +msgid "" +"Cannot use subtree_as_list and subtree_as_ids query params at the same time." +msgstr "" +"Die Abfrageparameter subtree_as_list und subtree_as_ids können nicht " +"gleichzeitig verwendet werden." + +msgid "" +"Combining effective and group filter will always result in an empty list." +msgstr "" +"Die Kombination von effektivem Filter und Gruppenfilter führt immer zu einer " +"leeren Liste." + +msgid "" +"Combining effective, domain and inherited filters will always result in an " +"empty list." +msgstr "" +"Die Kombination von effektivem Filter, Domänenfilter und vererbten Filtern " +"führt immer zu einer leeren Liste." + +#, python-format +msgid "Conflict occurred attempting to store %(type)s - %(details)s" +msgstr "Konflikt beim Versuch, %(type)s zu speichern - %(details)s" + +#, python-format +msgid "Conflicting region IDs specified: \"%(url_id)s\" != \"%(ref_id)s\"" +msgstr "" +"Angabe von Regions-IDs, die miteinander im Konflikt stehen: \"%(url_id)s\" !" +"= \"%(ref_id)s\"" + +msgid "Consumer not found" +msgstr "Kunde nicht gefunden" + +#, python-format +msgid "" +"Could not change immutable attribute(s) '%(attributes)s' in target %(target)s" +msgstr "" +"Unveränderliche Attribute '%(attributes)s' konnten nicht geändert werden in " +"Ziel %(target)s" + +#, python-format +msgid "" +"Could not find %(group_or_option)s in domain configuration for domain " +"%(domain_id)s" +msgstr "" +"%(group_or_option)s konnte in der Domänenkonfiguration für Domäne " +"%(domain_id)s nicht gefunden werden" + +#, python-format +msgid "Could not find Endpoint Group: %(endpoint_group_id)s" +msgstr "Endpunktgruppe konnte nicht gefunden werden: %(endpoint_group_id)s" + +msgid "Could not find Identity Provider identifier in environment" +msgstr "Identitätsprovider-ID konnte in der Umgebung nicht gefunden werden" + +#, python-format +msgid "Could not find Identity Provider: %(idp_id)s" +msgstr "Identitätsprovider %(idp_id)s konnte nicht gefunden werden" + +#, python-format +msgid "Could not find Service Provider: %(sp_id)s" +msgstr "Service-Provider %(sp_id)s konnte nicht gefunden werden" + +#, python-format +msgid "Could not find credential: %(credential_id)s" +msgstr "Berechtigungsnachweis %(credential_id)s konnte nicht gefunden werden" + +#, python-format +msgid "Could not find domain: %(domain_id)s" +msgstr "Domäne %(domain_id)s konnte nicht gefunden werden" + +#, python-format +msgid "Could not find endpoint: %(endpoint_id)s" +msgstr "Endpunkt %(endpoint_id)s konnte nicht gefunden werden" + +#, python-format +msgid "" +"Could not find federated protocol %(protocol_id)s for Identity Provider: " +"%(idp_id)s" +msgstr "" +"Föderiertes Protokoll %(protocol_id)s konnte nicht gefunden werden für " +"Identitätsprovider: %(idp_id)s" + +#, python-format +msgid "Could not find group: %(group_id)s" +msgstr "Gruppe %(group_id)s konnte nicht gefunden werden" + +#, python-format +msgid "Could not find mapping: %(mapping_id)s" +msgstr "Zuordnung %(mapping_id)s konnte nicht gefunden werden" + +msgid "Could not find policy association" +msgstr "Richtlinienzuordnung konnte nicht gefunden werden" + +#, python-format +msgid "Could not find policy: %(policy_id)s" +msgstr "Richtlinie %(policy_id)s konnte nicht gefunden werden" + +#, python-format +msgid "Could not find project: %(project_id)s" +msgstr "Projekt %(project_id)s konnte nicht gefunden werden" + +#, python-format +msgid "Could not find region: %(region_id)s" +msgstr "Region %(region_id)s konnte nicht gefunden werden" + +msgid "Could not find role" +msgstr "Rolle konnte nicht gefunden werden" + +#, python-format +msgid "" +"Could not find role assignment with role: %(role_id)s, user or group: " +"%(actor_id)s, project or domain: %(target_id)s" +msgstr "" +"Rollenzuordnung mit Rolle: %(role_id)s, Benutzer oder Gruppe: %(actor_id)s, " +"Projekt oder Domäne: %(target_id)s, konnte nicht gefunden werden" + +#, python-format +msgid "Could not find role: %(role_id)s" +msgstr "Rolle %(role_id)s konnte nicht gefunden werden" + +#, python-format +msgid "Could not find service: %(service_id)s" +msgstr "Service %(service_id)s konnte nicht gefunden werden" + +#, python-format +msgid "Could not find token: %(token_id)s" +msgstr "Token %(token_id)s konnte nicht gefunden werden" + +#, python-format +msgid "Could not find trust: %(trust_id)s" +msgstr "Vertrauensbeziehung %(trust_id)s konnte nicht gefunden werden" + +#, python-format +msgid "Could not find user: %(user_id)s" +msgstr "Benutzer %(user_id)s konnte nicht gefunden werden" + +#, python-format +msgid "Could not find version: %(version)s" +msgstr "Version %(version)s konnte nicht gefunden werden" + +#, python-format +msgid "Could not find: %(target)s" +msgstr "Konnte nicht gefunden werden: %(target)s" + +msgid "Could not validate the access token" +msgstr "Das Zugriffstoken konnte nicht geprüft werden" + +msgid "Credential belongs to another user" +msgstr "Berechtigungsnachweis gehört einem anderen Benutzer" + +#, python-format +msgid "Database at /domains/%s/config" +msgstr "Datenbank unter /domains/%s/config" + +msgid "" +"Disabling an entity where the 'enable' attribute is ignored by configuration." +msgstr "" +"Eine Entität inaktivieren, in der das Attribut 'enable' ignoriert wird von " + +#, python-format +msgid "Domain (%s)" +msgstr "Domain (%s)" + +#, python-format +msgid "Domain cannot be named %s" +msgstr "Domäne kann nicht mit %s benannt werden" + +#, python-format +msgid "Domain cannot have ID %s" +msgstr "Domäne kann nicht die ID %s haben" + +#, python-format +msgid "Domain is disabled: %s" +msgstr "Domäne ist inaktiviert: %s" + +msgid "Domain metadata not supported by LDAP" +msgstr "Domänenmetadaten werden von LDAP nicht unterstützt" + +msgid "Domain scoped token is not supported" +msgstr "Bereichsorientiertes Token der Domäne wird nicht unterstützt" + +#, python-format +msgid "" +"Domain: %(domain)s already has a configuration defined - ignoring file: " +"%(file)s." +msgstr "" +"Domäne: für %(domain)s ist bereits eine Konfiguration definiert - Datei wird " +"ignoriert: %(file)s." + +msgid "Domains are read-only against LDAP" +msgstr "Domänen sind für LDAP schreibgeschützt" + +msgid "Duplicate Entry" +msgstr "Doppelter Eintrag" + +#, python-format +msgid "Duplicate ID, %s." +msgstr "Doppelte ID, %s." + +#, python-format +msgid "Duplicate name, %s." +msgstr "Doppelter Name, %s." + +msgid "Enabled field must be a boolean" +msgstr "Das Feld 'Aktiviert' muss ein boolescher Wert sein" + +msgid "Enabled field should be a boolean" +msgstr "Das Feld 'Aktiviert' sollte ein boolescher Wert sein" + +#, python-format +msgid "Endpoint %(endpoint_id)s not found in project %(project_id)s" +msgstr "Endpunkt %(endpoint_id)s nicht gefunden in Projekt %(project_id)s" + +msgid "Endpoint Group Project Association not found" +msgstr "Projektzuordnung für Endpunktgruppe nicht gefunden" + +msgid "Ensure configuration option idp_entity_id is set." +msgstr "" +"Stellen Sie sicher, dass die Konfigurationsoption idp_entity_id gesetzt ist. " + +msgid "Ensure configuration option idp_sso_endpoint is set." +msgstr "" +"Stellen Sie sicher, dass die Konfigurationsoption idp_sso_endpoint gesetzt " +"ist. " + +#, python-format +msgid "" +"Error parsing configuration file for domain: %(domain)s, file: %(file)s." +msgstr "" +"Fehler bei der Auswertung der Konfigurationsdatei für Domäne: %(domain)s, " +"Datei: %(file)s." + +#, python-format +msgid "Error while reading metadata file, %(reason)s" +msgstr "Fehler beim Lesen der Metadatendatei, %(reason)s" + +#, python-format +msgid "Expected dict or list: %s" +msgstr "Verzeichnis oder Liste erwartet: %s" + +msgid "" +"Expected signing certificates are not available on the server. Please check " +"Keystone configuration." +msgstr "" +"Erwartete Signierzertifikate sind auf dem Server nicht verfügbar. Überprüfen " +"Sie die Keystone-Konfiguration." + +#, python-format +msgid "" +"Expecting to find %(attribute)s in %(target)s - the server could not comply " +"with the request since it is either malformed or otherwise incorrect. The " +"client is assumed to be in error." +msgstr "" +"Es wurde erwartet, %(attribute)s in %(target)s zu finden. Der Server konnte " +"die Anforderung nicht erfüllen, da ein fehlerhaftes Format oder ein anderer " +"Fehler vorliegt. Es wird angenommen, dass der Fehler beim Client liegt." + +#, python-format +msgid "Failed to start the %(name)s server" +msgstr "Fehler beim Starten des %(name)s-Servers" + +msgid "Failed to validate token" +msgstr "Token konnte nicht geprüft werden" + +msgid "Federation token is expired" +msgstr "Föderationstoken ist abgelaufen" + +#, python-format +msgid "" +"Field \"remaining_uses\" is set to %(value)s while it must not be set in " +"order to redelegate a trust" +msgstr "" +"Feld \"remaining_uses\" ist auf %(value)s festgelegt, es darf jedoch nicht " +"festgelegt werden, um eine Vertrauensbeziehung zu übertragen" + +msgid "Found invalid token: scoped to both project and domain." +msgstr "" +"Ungültiges Token gefunden. Es ist sowohl projekt- als auch domänenorientiert." + +#, python-format +msgid "Group %(group)s is not supported for domain specific configurations" +msgstr "" +"Gruppe %(group)s wird für domänenspezifische Konfigurationen nicht " +"unterstützt" + +#, python-format +msgid "" +"Group %(group_id)s returned by mapping %(mapping_id)s was not found in the " +"backend." +msgstr "" +"Die von der Zuordnung %(mapping_id)s zurückgegebene Gruppe %(group_id)s " +"konnte im Back-End nicht gefunden werden." + +#, python-format +msgid "" +"Group membership across backend boundaries is not allowed, group in question " +"is %(group_id)s, user is %(user_id)s" +msgstr "" +"Back-End-übergreifende Gruppenmitgliedschaft ist nicht zulässig, betroffene " +"Gruppe ist %(group_id)s, Benutzer ist %(user_id)s" + +#, python-format +msgid "ID attribute %(id_attr)s not found in LDAP object %(dn)s" +msgstr "ID-Attribut %(id_attr)s wurde in LDAP-Objekt %(dn)s nicht gefunden" + +#, python-format +msgid "Identity Provider %(idp)s is disabled" +msgstr "Identitätsprovider %(idp)s ist inaktiviert" + +msgid "" +"Incoming identity provider identifier not included among the accepted " +"identifiers." +msgstr "" +"Eingehende Identitätsprovider-ID ist nicht in den akzeptierten IDs enthalten." + +#, python-format +msgid "Invalid LDAP TLS certs option: %(option)s. Choose one of: %(options)s" +msgstr "" +"Ungültige LDAP-TLS-Zertifikatsoption: %(option)s. Wählen Sie aus: %(options)s" + +#, python-format +msgid "Invalid LDAP TLS_AVAIL option: %s. TLS not available" +msgstr "Ungültige LDAP TLS_AVAIL Option: %s. TLS nicht verfügbar" + +#, python-format +msgid "Invalid LDAP deref option: %(option)s. Choose one of: %(options)s" +msgstr "" +"Ungültige LDAP-TLS-deref-Option: %(option)s. Wählen Sie aus: %(options)s" + +#, python-format +msgid "Invalid LDAP scope: %(scope)s. Choose one of: %(options)s" +msgstr "Ungültiger LDAP Bereich: %(scope)s. Wählen Sie aus: %(options)s" + +msgid "Invalid TLS / LDAPS combination" +msgstr "Ungültige TLS /LDAPS Kombination" + +#, python-format +msgid "Invalid audit info data type: %(data)s (%(type)s)" +msgstr "Ungültiger Datentyp für Prüfungsinformationen: %(data)s (%(type)s)" + +msgid "Invalid blob in credential" +msgstr "Ungültiges Blob-Objekt im Berechtigungsnachweis" + +#, python-format +msgid "" +"Invalid domain name: %(domain)s found in config file name: %(file)s - " +"ignoring this file." +msgstr "" +"Ungültiger Domänenname: %(domain)s im Konfigurationsdateinamen gefunden: " +"%(file)s - diese Datei wird ignoriert." + +#, python-format +msgid "Invalid domain specific configuration: %(reason)s" +msgstr "Ungültige domänenspezifische Konfiguration: %(reason)s" + +#, python-format +msgid "Invalid input for field '%(path)s'. The value is '%(value)s'." +msgstr "Ungültige Eingabe für Feld '%(path)s'. Der Wert lautet '%(value)s'." + +msgid "Invalid limit value" +msgstr "Ungültiger Grenzwert" + +#, python-format +msgid "" +"Invalid mix of entities for policy association - only Endpoint, Service or " +"Region+Service allowed. Request was - Endpoint: %(endpoint_id)s, Service: " +"%(service_id)s, Region: %(region_id)s" +msgstr "" +"Ungültige Mischung von Entitäten für Richtlinienzuordnung - nur Endpunkt, " +"Service oder Region+Service zulässig. Anforderung war - Endpunkt: " +"%(endpoint_id)s, Service: %(service_id)s, Region: %(region_id)s" + +#, python-format +msgid "" +"Invalid rule: %(identity_value)s. Both 'groups' and 'domain' keywords must " +"be specified." +msgstr "" +"Ungültige Regel: %(identity_value)s. Die Suchbegriffe 'groups' und 'domain' " +"müssen angegeben sein." + +msgid "Invalid signature" +msgstr "Ungültige Signatur" + +#, python-format +msgid "" +"Invalid ssl_cert_reqs value of %s, must be one of \"NONE\", \"OPTIONAL\", " +"\"REQUIRED\"" +msgstr "" +"Ungültiger Wert %s für ssl_cert_reqs, muss lauten \"NONE\", \"OPTIONAL\", " +"\"REQUIRED\"" + +msgid "Invalid user / password" +msgstr "Ungültiger Benutzer / Passwort" + +msgid "Invalid username or password" +msgstr "Ungültiger Benutzername oder ungültiges Passwort." + +#, python-format +msgid "KVS region %s is already configured. Cannot reconfigure." +msgstr "KVS-Region %s ist bereits konfiguriert. Rekonfiguration nicht möglich." + +#, python-format +msgid "Key Value Store not configured: %s" +msgstr "Schlüsselwertspeicher nicht konfiguriert: %s" + +#, python-format +msgid "LDAP %s create" +msgstr "LDAP %s erstellen" + +#, python-format +msgid "LDAP %s delete" +msgstr "LDAP %s löschen" + +#, python-format +msgid "LDAP %s update" +msgstr "LDAP %s aktualisieren" + +#, python-format +msgid "Lock Timeout occurred for key, %(target)s" +msgstr "Überschreitung der Sperrzeit aufgetreten für Schlüssel %(target)s" + +#, python-format +msgid "Lock key must match target key: %(lock)s != %(target)s" +msgstr "" +"Sperrschlüssel muss mit Zielschlüssel übereinstimmen: %(lock)s != %(target)s" + +#, python-format +msgid "Malformed endpoint URL (%(endpoint)s), see ERROR log for details." +msgstr "" +"Fehlerhafte Endpunkt-URL (%(endpoint)s), siehe Details im FEHLER-Protokoll. " + +msgid "Marker could not be found" +msgstr "Marker konnte nicht gefunden werden" + +#, python-format +msgid "Maximum lock attempts on %s occurred." +msgstr "Maximale Anzahl an Sperrversuchen auf %s erfolgt." + +#, python-format +msgid "Member %(member)s is already a member of group %(group)s" +msgstr "Mitglied %(member)s ist bereits Mitglied der Gruppe %(group)s" + +#, python-format +msgid "Method not callable: %s" +msgstr "Methode kann nicht aufgerufen werden: %s" + +msgid "Missing entity ID from environment" +msgstr "Fehlende Entitäts-ID von Umgebung" + +msgid "" +"Modifying \"redelegation_count\" upon redelegation is forbidden. Omitting " +"this parameter is advised." +msgstr "" +"Das Ändern von \"redelegation_count\" ist bei der Redelegation nicht " +"zulässig. Es wird empfohlen, diesen Parameter auszulassen." + +msgid "Multiple domains are not supported" +msgstr "Mehrere Domänen werden nicht unterstützt" + +msgid "Must be called within an active lock context." +msgstr "Aufruf innerhalb des Kontexts einer aktiven Sperre erforderlich." + +msgid "Must specify either domain or project" +msgstr "Entweder Domäne oder Projekt muss angegeben werden" + +msgid "Name field is required and cannot be empty" +msgstr "Namensfeld ist erforderlich und darf nicht leer sein" + +msgid "" +"No Authorization headers found, cannot proceed with OAuth related calls, if " +"running under HTTPd or Apache, ensure WSGIPassAuthorization is set to On." +msgstr "" +"Keine Autorisierungskopfzeilen gefunden, zu OAuth zugehörige Aufrufe können " +"nicht fortgesetzt werden. Stellen Sie bei Ausführung unter HTTPd oder Apache " +"sicher, dass WSGIPassAuthorization auf 'On' gesetzt ist." + +msgid "No authenticated user" +msgstr "Kein authentifizierter Benutzer" + +msgid "" +"No encryption keys found; run keystone-manage fernet_setup to bootstrap one." +msgstr "" +"Keine Chiffrierschlüssel gefunden; Führen Sie keystone-manage fernet_setup " +"aus, um über Bootstrapping einen Schlüssel zu erhalten." + +msgid "No options specified" +msgstr "Keine Optionen angegeben" + +#, python-format +msgid "No policy is associated with endpoint %(endpoint_id)s." +msgstr "Endpunkt %(endpoint_id)s ist keine Richtlinie zugeordnet. " + +#, python-format +msgid "No remaining uses for trust: %(trust_id)s" +msgstr "Keine verbleibende Verwendung für Vertrauensbeziehung %(trust_id)s" + +msgid "Non-default domain is not supported" +msgstr "Nicht-Standard-Domäne wird nicht unterstützt" + +msgid "One of the trust agents is disabled or deleted" +msgstr "Einer der Vertrauensagenten wurde inaktiviert oder gelöscht" + +#, python-format +msgid "" +"Option %(option)s found with no group specified while checking domain " +"configuration request" +msgstr "" +"Option %(option)s ohne angegebene Gruppe gefunden, während die Domänen- " +"Konfigurationsanforderung geprüft wurde" + +#, python-format +msgid "" +"Option %(option)s in group %(group)s is not supported for domain specific " +"configurations" +msgstr "" +"Option %(option)s in Gruppe %(group)s wird für domänenspezifische " +"Konfigurationen nicht unterstützt" + +#, python-format +msgid "Project (%s)" +msgstr "Projekt (%s)" + +#, python-format +msgid "Project is disabled: %s" +msgstr "Projekt ist inaktiviert: %s" + +msgid "Redelegation allowed for delegated by trust only" +msgstr "Redelegation nur zulässig für im Vertrauen redelegierte" + +#, python-format +msgid "" +"Remaining redelegation depth of %(redelegation_depth)d out of allowed range " +"of [0..%(max_count)d]" +msgstr "" +"Verbleibende Redelegationstiefe von %(redelegation_depth)d aus dem " +"zulässigen Bereich von [0..%(max_count)d]" + +msgid "Request Token does not have an authorizing user id" +msgstr "Anforderungstoken weist keine autorisierte Benutzer-ID auf" + +#, python-format +msgid "" +"Request attribute %(attribute)s must be less than or equal to %(size)i. The " +"server could not comply with the request because the attribute size is " +"invalid (too large). The client is assumed to be in error." +msgstr "" +"Anforderungsattribut %(attribute)s muss kleiner-gleich %(size)i sein. Der " +"Server konnte die Anforderung nicht erfüllen, da die Attributgröße ungültig " +"ist (zu groß). Es wird angenommen, dass der Fehler beim Client liegt." + +msgid "Request must have an origin query parameter" +msgstr "Anforderung muss über einen ursprünglichen Abfrageparameter verfügen" + +msgid "Request token is expired" +msgstr "Anforderungstoken ist abgelaufen" + +msgid "Request token not found" +msgstr "Anforderungstoken nicht gefunden" + +msgid "Requested expiration time is more than redelegated trust can provide" +msgstr "" +"Angeforderte Ablaufzeit übersteigt die, die von der redelegierten " +"Vertrauensbeziehung bereitgestellt werden kann" + +#, python-format +msgid "" +"Requested redelegation depth of %(requested_count)d is greater than allowed " +"%(max_count)d" +msgstr "" +"Die angeforderte Redelegationstiefe von %(requested_count)d übersteigt den " +"zulässigen Wert von %(max_count)d" + +#, python-format +msgid "Role %s not found" +msgstr "Rolle %s nicht gefunden" + +msgid "" +"Running keystone via eventlet is deprecated as of Kilo in favor of running " +"in a WSGI server (e.g. mod_wsgi). Support for keystone under eventlet will " +"be removed in the \"M\"-Release." +msgstr "" +"Die Ausführung von Keystone über eventlet ist seit Kilo veraltet. " +"Stattdessen wird ein WSGI-Server (z. B. mod_wsgi) für die Ausführung " +"verwendet. Unterstützung für Keystone unter eventlet wird im \"M\"-Release " +"entfernt." + +msgid "Scoping to both domain and project is not allowed" +msgstr "Scoping sowohl auf 'domain' als auch auf 'project' ist nicht zulässig" + +msgid "Scoping to both domain and trust is not allowed" +msgstr "Scoping sowohl auf 'domain' als auch auf 'trust' ist nicht zulässig" + +msgid "Scoping to both project and trust is not allowed" +msgstr "Scoping sowohl auf 'project' als auch auf 'trust' ist nicht zulässig" + +#, python-format +msgid "Service Provider %(sp)s is disabled" +msgstr "Service-Provider %(sp)s ist inaktiviert" + +msgid "Some of requested roles are not in redelegated trust" +msgstr "" +"Einige angeforderte Rollen befinden sich nicht in einer redelegierten " +"Vertrauensbeziehung" + +msgid "Specify a domain or project, not both" +msgstr "Geben Sie eine Domäne oder ein Projekt an, nicht beides" + +msgid "Specify a user or group, not both" +msgstr "Geben Sie einen Benutzer oder eine Gruppe an, nicht beides" + +msgid "Specify one of domain or project" +msgstr "Entweder eine Domäne oder ein Projekt muss angegeben werden" + +msgid "Specify one of user or group" +msgstr "Entweder ein Benutzer oder eine Gruppe muss angegeben werden" + +#, python-format +msgid "" +"String length exceeded.The length of string '%(string)s' exceeded the limit " +"of column %(type)s(CHAR(%(length)d))." +msgstr "" +"Zeichenfolgelänge überschritten. Die Länge der Zeichenfolge '%(string)s' hat " +"den Grenzwert von Spalte %(type)s(CHAR(%(length)d)) überschritten." + +msgid "The --all option cannot be used with the --domain-name option" +msgstr "" +"Die Option --all kann nicht zusammen mit der Option --domain-name verwendet " +"werden" + +#, python-format +msgid "The Keystone configuration file %(config_file)s could not be found." +msgstr "" +"Die Keystone-Konfigurationsdatei %(config_file)s konnte nicht gefunden " +"werden." + +#, python-format +msgid "" +"The Keystone domain-specific configuration has specified more than one SQL " +"driver (only one is permitted): %(source)s." +msgstr "" +"Die domänenspezifische Keystone-Konfiguration hat mehrere SQL-Treiber " +"angegeben (nur einer ist zulässig): %(source)s." + +msgid "The action you have requested has not been implemented." +msgstr "Die von Ihnen angeforderte Aktion wurde nicht implementiert." + +msgid "The authenticated user should match the trustor." +msgstr "Der authentifizierte Benutzer sollte dem Trustor entsprechen." + +msgid "" +"The certificates you requested are not available. It is likely that this " +"server does not use PKI tokens otherwise this is the result of " +"misconfiguration." +msgstr "" +"Die Zertifikate, die Sie angefordert haben, sind nicht verfügbar. Es ist " +"wahrscheinlich, dass dieser Server keine PKI-Tokens verwendet; andernfalls " +"ist dies die Folge einer fehlerhaften Konfiguration." + +#, python-format +msgid "" +"The password length must be less than or equal to %(size)i. The server could " +"not comply with the request because the password is invalid." +msgstr "" +"Die Kennwortlänge muss kleiner-gleich %(size)i sein. Der Server konnte die " +"Anforderung nicht erfüllen, da das Kennwort ungültig ist." + +msgid "The request you have made requires authentication." +msgstr "Die von Ihnen gestellte Anfrage erfoderdert eine Authentifizierung." + +msgid "The resource could not be found." +msgstr "Die Ressource konnte nicht gefunden werden." + +msgid "" +"The revoke call must not have both domain_id and project_id. This is a bug " +"in the Keystone server. The current request is aborted." +msgstr "" +"Der Aufruf zum Entziehen darf nicht sowohl domain_id als auch project_id " +"aufweisen. Dies ist ein Fehler im Keystone-Server. Die aktuelle Anforderung " +"wird abgebrochen. " + +msgid "The service you have requested is no longer available on this server." +msgstr "" +"Der Service, den Sie angefordert haben, ist auf diesem Server nicht mehr " +"verfügbar." + +#, python-format +msgid "" +"The specified parent region %(parent_region_id)s would create a circular " +"region hierarchy." +msgstr "" +"Die angegebene übergeordnete Region %(parent_region_id)s würde eine " +"zirkuläre Regionshierarchie erstellen." + +#, python-format +msgid "" +"The value of group %(group)s specified in the config should be a dictionary " +"of options" +msgstr "" +"Der Wert der Gruppe %(group)s, der in der Konfiguration angegeben ist, muss " +"ein Verzeichnis mit Optionen sein" + +msgid "There should not be any non-oauth parameters" +msgstr "Es sollten keine non-oauth-Parameter vorhanden sein" + +#, python-format +msgid "This is not a recognized Fernet payload version: %s" +msgstr "Dies ist keine anerkannte Fernet-Nutzdatenversion: %s" + +msgid "" +"This is not a v2.0 Fernet token. Use v3 for trust, domain, or federated " +"tokens." +msgstr "" +"Dies ist kein v2.0-Fernet-Token. Verwenden Sie v3 für Vertrauensbeziehungs-, " +"Domänen- oder föderierte Tokens." + +msgid "" +"Timestamp not in expected format. The server could not comply with the " +"request since it is either malformed or otherwise incorrect. The client is " +"assumed to be in error." +msgstr "" +"Zeitmarke nicht im erwarteten Format. Der Server konnte der Anforderung " +"nicht nachkommen, da ein fehlerhaftes Format oder ein anderer Fehler " +"vorliegt. Es wird angenommen, dass der Fehler beim Client liegt." + +#, python-format +msgid "" +"To get a more detailed information on this error, re-run this command for " +"the specific domain, i.e.: keystone-manage domain_config_upload --domain-" +"name %s" +msgstr "" +"Um ausführliche Informationen zu diesem Fehler zu erhalten, führen Sie " +"diesen Befehl für die angegebene Domäne erneut durch: keystone-manage " +"domain_config_upload --domain-name %s" + +msgid "Token belongs to another user" +msgstr "Token gehört einem anderen Benutzer" + +msgid "Token does not belong to specified tenant." +msgstr "Token gehört nicht zu angegebenem Nutzer." + +msgid "Trustee has no delegated roles." +msgstr "Trustee hat keine beauftragten Rollen." + +msgid "Trustor is disabled." +msgstr "Trustor ist inaktiviert." + +#, python-format +msgid "" +"Trying to update group %(group)s, so that, and only that, group must be " +"specified in the config" +msgstr "" +"Es wird versucht, Gruppe %(group)s zu aktualisieren, damit nur diese Gruppe " +"in der Konfiguration angegeben werden muss" + +#, python-format +msgid "" +"Trying to update option %(option)s in group %(group)s, but config provided " +"contains option %(option_other)s instead" +msgstr "" +"Es wird versucht, Option %(option)s in Gruppe %(group)s zu aktualisieren, " +"die angegebene Konfiguration enthält jedoch stattdessen Option " +"%(option_other)s" + +#, python-format +msgid "" +"Trying to update option %(option)s in group %(group)s, so that, and only " +"that, option must be specified in the config" +msgstr "" +"Es wird versucht, Option %(option)s in Gruppe %(group)s zu aktualisieren, " +"damit nur diese Option in der Konfiguration angegeben werden muss" + +msgid "" +"Unable to access the keystone database, please check it is configured " +"correctly." +msgstr "" +"Auf die Keystone-Datenbank kann nicht zugegriffen werden, überprüfen Sie, ob " +"sie ordnungsgemäß konfiguriert ist. " + +#, python-format +msgid "Unable to consume trust %(trust_id)s, unable to acquire lock." +msgstr "" +"Vertrauensbeziehung %(trust_id)s kann nicht verarbeitet werden, Sperre kann " +"nicht angefordert werden." + +#, python-format +msgid "" +"Unable to delete region %(region_id)s because it or its child regions have " +"associated endpoints." +msgstr "" +"Region %(region_id)s kann nicht gelöscht werden, da sie oder ihr " +"untergeordnete Regionen über zugeordnete Endpunkte verfügen. " + +#, python-format +msgid "Unable to find valid groups while using mapping %(mapping_id)s" +msgstr "" +"Beim Verwenden der Zuordnung %(mapping_id)s können keine gültigen Gruppen " +"gefunden werden" + +#, python-format +msgid "" +"Unable to get a connection from pool id %(id)s after %(seconds)s seconds." +msgstr "" +"Verbindung konnte von Pool-ID %(id)s nach %(seconds)s nicht abgerufen " +"werden. " + +#, python-format +msgid "Unable to locate domain config directory: %s" +msgstr "Domänenkonfigurationsverzeichnis wurde nicht gefunden: %s" + +#, python-format +msgid "Unable to lookup user %s" +msgstr "Suche nach Benutzer %s nicht möglich" + +#, python-format +msgid "" +"Unable to reconcile identity attribute %(attribute)s as it has conflicting " +"values %(new)s and %(old)s" +msgstr "" +"Identitätsattribut %(attribute)s kann nicht abgeglichen werden, da es die " +"kollidierenden Werte %(new)s und %(old)s aufweist" + +#, python-format +msgid "" +"Unable to sign SAML assertion. It is likely that this server does not have " +"xmlsec1 installed, or this is the result of misconfiguration. Reason " +"%(reason)s" +msgstr "" +"SAML-Zusicherung kann nicht signiert werden. Wahrscheinlich ist auf dem " +"Server xmlsec1 nicht installiert oder dies liegt an einer fehlerhaften " +"Konfiguration. Ursache: %(reason)s" + +msgid "Unable to sign token." +msgstr "Token kann nicht unterzeichnet werden." + +#, python-format +msgid "Unexpected assignment type encountered, %s" +msgstr "Unerwarteter Zuordnungstyp: %s" + +#, python-format +msgid "" +"Unexpected combination of grant attributes - User: %(user_id)s, Group: " +"%(group_id)s, Project: %(project_id)s, Domain: %(domain_id)s" +msgstr "" +"Unerwartete Kombination von Grant-Attributen - Benutzer: %(user_id)s, " +"Gruppe: %(group_id)s, Projekt: %(project_id)s, Domäne: %(domain_id)s" + +#, python-format +msgid "Unexpected status requested for JSON Home response, %s" +msgstr "Unerwarteter Status für JSON-Home-Antwort angefordert, %s" + +msgid "Unknown Target" +msgstr "Unbekanntes Ziel" + +#, python-format +msgid "Unknown domain '%(name)s' specified by --domain-name" +msgstr "Unbekannte Domäne '%(name)s' angegeben durch --domain-name" + +#, python-format +msgid "Unknown token version %s" +msgstr "Unbekannte Tokenversion %s" + +#, python-format +msgid "Unregistered dependency: %(name)s for %(targets)s" +msgstr "Nicht registrierte Abhängigkeit: %(name)s für %(targets)s" + +msgid "Update of `parent_id` is not allowed." +msgstr "Das Aktualisieren von 'parent_id' ist nicht zulässig." + +msgid "Use a project scoped token when attempting to create a SAML assertion" +msgstr "" +"Verwenden Sie ein Projektumfangstoken, wenn Sie versuchen, eine SAML-" +"Zusicherung zu erstellen" + +#, python-format +msgid "User %(u_id)s is unauthorized for tenant %(t_id)s" +msgstr "Benutzer %(u_id)s ist nicht berechtigt für Nutzer %(t_id)s" + +#, python-format +msgid "User %(user_id)s already has role %(role_id)s in tenant %(tenant_id)s" +msgstr "" +"Benutzer %(user_id)s verfügt bereits über die Rolle %(role_id)s in Nutzer " +"%(tenant_id)s" + +#, python-format +msgid "User %(user_id)s has no access to domain %(domain_id)s" +msgstr "Benutzer %(user_id)s hat keinen Zugriff auf Domäne %(domain_id)s" + +#, python-format +msgid "User %(user_id)s has no access to project %(project_id)s" +msgstr "Benutzer %(user_id)s hat keinen Zugriff auf Projekt %(project_id)s" + +#, python-format +msgid "User %(user_id)s is already a member of group %(group_id)s" +msgstr "Benutzer %(user_id)s ist bereits Mitglied der Gruppe %(group_id)s." + +#, python-format +msgid "User '%(user_id)s' not found in group '%(group_id)s'" +msgstr "Benutzer '%(user_id)s' nicht gefunden in Gruppe '%(group_id)s'" + +msgid "User IDs do not match" +msgstr "Benutzerkennungen stimmen nicht überein" + +#, python-format +msgid "User is disabled: %s" +msgstr "Benutzer ist inaktiviert: %s" + +msgid "User is not a member of the requested project" +msgstr "Benutzer ist kein Mitglied des angeforderten Projekts" + +msgid "User is not a trustee." +msgstr "Benutzer ist kein Trustee." + +msgid "User not found" +msgstr "Benutzer nicht gefunden" + +#, python-format +msgid "User type %s not supported" +msgstr "Benutzertyp %s nicht unterstützt" + +msgid "You are not authorized to perform the requested action." +msgstr "" +"Sie sind nicht dazu autorisiert, die angeforderte Aktion durchzuführen." + +#, python-format +msgid "You are not authorized to perform the requested action: %(action)s" +msgstr "" +"Sie sind nicht berechtigt, die angeforderte Aktion %(action)s auszuführen" + +msgid "`key_mangler` functions must be callable." +msgstr "`key_mangler`-Funktionen müssen aufrufbar sein." + +msgid "`key_mangler` option must be a function reference" +msgstr "Option `key_mangler` muss eine Funktionsreferenz sein" + +msgid "any options" +msgstr "beliebige Optionen" + +msgid "auth_type is not Negotiate" +msgstr "auth_type ist nicht 'Negotiate'" + +msgid "authorizing user does not have role required" +msgstr "Der autorisierte Benutzer verfügt nicht über die erforderliche Rolle" + +msgid "cache_collection name is required" +msgstr "Ein Name für cache_collection ist erforderlich" + +#, python-format +msgid "cannot create a project in a branch containing a disabled project: %s" +msgstr "" +"kann kein Projekt in einer Niederlassung erstellen, die ein inaktiviertes " +"Projekt enthält: %s" + +msgid "cannot create a project within a different domain than its parents." +msgstr "" +"kann kein Projekt innerhalb einer anderen Domäne als der der übergeordneten " +"Projekte erstellen." + +msgid "cannot delete a domain that is enabled, please disable it first." +msgstr "" +"Eine aktivierte Domäne kann nicht gelöscht werden; inaktivieren Sie sie " +"zuerst." + +#, python-format +msgid "cannot delete the project %s since it is not a leaf in the hierarchy." +msgstr "" +"kann das Projekt %s nicht löschen, da es kein Blattelement in der Hierarchie " +"darstellt." + +#, python-format +msgid "cannot disable project %s since its subtree contains enabled projects" +msgstr "" +"kann Projekt %s nicht inaktivieren, da die zugehörige untergeordnete " +"Baumstruktur aktivierte Projekte enthält" + +#, python-format +msgid "cannot enable project %s since it has disabled parents" +msgstr "" +"kann Projekt %s nicht aktivieren, da es über inaktivierte übergeordnete " +"Projekte verfügt" + +msgid "database db_name is required" +msgstr "Die Datenbank db_name ist erforderlich" + +msgid "db_hosts value is required" +msgstr "Ein Wert für db_hosts ist erforderlich" + +msgid "delete the default domain" +msgstr "Standarddomäne löschen" + +#, python-format +msgid "group %(group)s" +msgstr "Gruppe %(group)s" + +msgid "" +"idp_contact_type must be one of: [technical, other, support, administrative " +"or billing." +msgstr "" +"idp_contact_type muss einer der folgenden Werte sein: technical, other, " +"support, administrative oder billing." + +msgid "integer value expected for mongo_ttl_seconds" +msgstr "Ganzzahlwert für mongo_ttl_seconds erwartet" + +msgid "integer value expected for w (write concern attribute)" +msgstr "Ganzzahlwert für Attribut 'w' ('write concern'-Attribut) erwartet" + +#, python-format +msgid "invalid date format %s" +msgstr "ungültiges Datumsformat %s" + +#, python-format +msgid "max hierarchy depth reached for %s branch." +msgstr "für die %s-Niederlassung wurde die maximale Hierarchietiefe erreicht." + +msgid "no ssl support available" +msgstr "Keine SSL-Unterstützung verfügbar" + +#, python-format +msgid "option %(option)s in group %(group)s" +msgstr "Option %(option)s in Gruppe %(group)s" + +msgid "pad must be single character" +msgstr "Pad muss ein einzelnes Zeichen sein" + +msgid "padded base64url text must be multiple of 4 characters" +msgstr "base64url-Pad-Text muss ein Vielfaches von 4 Zeichen enthalten" + +msgid "provided consumer key does not match stored consumer key" +msgstr "" +"bereitgestellter Konsumentenschlüssel stimmt nicht mit dem gespeicherten " +"Konsumentenschlüssel überein" + +msgid "provided request key does not match stored request key" +msgstr "" +"bereitgestellter Anforderungsschlüssel stimmt nicht mit dem gespeicherten " +"Anforderungsschlüssel überein" + +msgid "provided verifier does not match stored verifier" +msgstr "" +"bereitgestellte Prüffunktion stimmt nicht mit gespeicherter Prüffunktion " +"überein" + +msgid "region not type dogpile.cache.CacheRegion" +msgstr "Region weist nicht den Typ 'dogpile.cache.CacheRegion' auf" + +msgid "remaining_uses must be a positive integer or null." +msgstr "remaining_uses muss eine positive Ganzzahl oder null sein." + +msgid "remaining_uses must not be set if redelegation is allowed" +msgstr "" +"remaining_uses darf nicht festgelegt werden, wenn eine Redelegation zulässig " +"ist" + +msgid "replicaset_name required when use_replica is True" +msgstr "replicaset_name erforderlich, wenn use_replica 'True' ist" + +#, python-format +msgid "" +"request to update group %(group)s, but config provided contains group " +"%(group_other)s instead" +msgstr "" +"Anforderung zur Aktualisierung von Gruppe %(group)s, die angegebene " +"Konfiguration enthält jedoch stattdessen Gruppe %(group_other)s" + +msgid "rescope a scoped token" +msgstr "Bereich für bereichsorientierten Token ändern" + +#, python-format +msgid "text is multiple of 4, but pad \"%s\" occurs before 2nd to last char" +msgstr "" +"Text ist ein Vielfaches von 4, aber Pad \"%s\" steht vor dem zweitletzten " +"Zeichen" + +#, python-format +msgid "text is multiple of 4, but pad \"%s\" occurs before non-pad last char" +msgstr "" +"Text ist ein Vielfaches von 4, aber Pad \"%s\" steht vor dem letzten Nicht-" +"Pad-Zeichen" + +#, python-format +msgid "text is not a multiple of 4, but contains pad \"%s\"" +msgstr "Text ist kein Vielfaches von 4, aber enthält Pad \"%s\"" + +#, python-format +msgid "tls_cacertdir %s not found or is not a directory" +msgstr "tls_cacertdir %s nicht gefunden oder ist kein Verzeichnis" + +#, python-format +msgid "tls_cacertfile %s not found or is not a file" +msgstr "tls_cacertfile %s wurde nicht gefunden oder ist keine Datei" + +#, python-format +msgid "token reference must be a KeystoneToken type, got: %s" +msgstr "Tokenreferenz muss vom Typ 'KeystoneToken' sein. Abgerufen wurde: %s" diff --git a/keystone-moon/keystone/locale/el/LC_MESSAGES/keystone-log-critical.po b/keystone-moon/keystone/locale/el/LC_MESSAGES/keystone-log-critical.po new file mode 100644 index 00000000..1dffb340 --- /dev/null +++ b/keystone-moon/keystone/locale/el/LC_MESSAGES/keystone-log-critical.po @@ -0,0 +1,25 @@ +# Translations template for keystone. +# Copyright (C) 2015 OpenStack Foundation +# This file is distributed under the same license as the keystone project. +# +# Translators: +# Efstathios Iosifidis <iefstathios@gmail.com>, 2015 +msgid "" +msgstr "" +"Project-Id-Version: Keystone\n" +"Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" +"POT-Creation-Date: 2015-09-08 06:08+0000\n" +"PO-Revision-Date: 2015-09-05 13:09+0000\n" +"Last-Translator: Efstathios Iosifidis <iefstathios@gmail.com>\n" +"Language-Team: Greek (http://www.transifex.com/openstack/keystone/language/" +"el/)\n" +"Language: el\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" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#, python-format +msgid "Unable to open template file %s" +msgstr "Αδυναμία ανοίγματος αρχείου προτύπου %s" diff --git a/keystone-moon/keystone/locale/en_AU/LC_MESSAGES/keystone-log-critical.po b/keystone-moon/keystone/locale/en_AU/LC_MESSAGES/keystone-log-critical.po index 289fa43d..99590953 100644 --- a/keystone-moon/keystone/locale/en_AU/LC_MESSAGES/keystone-log-critical.po +++ b/keystone-moon/keystone/locale/en_AU/LC_MESSAGES/keystone-log-critical.po @@ -3,21 +3,23 @@ # This file is distributed under the same license as the keystone project. # # Translators: +# OpenStack Infra <zanata@openstack.org>, 2015. #zanata msgid "" msgstr "" -"Project-Id-Version: Keystone\n" +"Project-Id-Version: keystone 8.0.0.0b4.dev56\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" -"POT-Creation-Date: 2015-08-06 06:28+0000\n" -"PO-Revision-Date: 2014-08-31 15:19+0000\n" +"POT-Creation-Date: 2015-09-21 06:08+0000\n" +"PO-Revision-Date: 2014-08-31 03:19+0000\n" "Last-Translator: openstackjenkins <jenkins@openstack.org>\n" "Language-Team: English (Australia) (http://www.transifex.com/openstack/" "keystone/language/en_AU/)\n" -"Language: en_AU\n" +"Language: en-AU\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" "Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Zanata 3.7.1\n" #, python-format msgid "Unable to open template file %s" diff --git a/keystone-moon/keystone/locale/en_AU/LC_MESSAGES/keystone-log-error.po b/keystone-moon/keystone/locale/en_AU/LC_MESSAGES/keystone-log-error.po index 65b59aa3..7fd91ea1 100644 --- a/keystone-moon/keystone/locale/en_AU/LC_MESSAGES/keystone-log-error.po +++ b/keystone-moon/keystone/locale/en_AU/LC_MESSAGES/keystone-log-error.po @@ -3,21 +3,23 @@ # This file is distributed under the same license as the keystone project. # # Translators: +# OpenStack Infra <zanata@openstack.org>, 2015. #zanata msgid "" msgstr "" -"Project-Id-Version: Keystone\n" +"Project-Id-Version: keystone 8.0.0.0b4.dev56\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" -"POT-Creation-Date: 2015-08-06 06:28+0000\n" -"PO-Revision-Date: 2015-06-26 17:13+0000\n" +"POT-Creation-Date: 2015-09-21 06:08+0000\n" +"PO-Revision-Date: 2015-06-26 05:13+0000\n" "Last-Translator: openstackjenkins <jenkins@openstack.org>\n" "Language-Team: English (Australia) (http://www.transifex.com/openstack/" "keystone/language/en_AU/)\n" -"Language: en_AU\n" +"Language: en-AU\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" "Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Zanata 3.7.1\n" msgid "" "Error setting up the debug environment. Verify that the option --debug-url " diff --git a/keystone-moon/keystone/locale/es/LC_MESSAGES/keystone.po b/keystone-moon/keystone/locale/es/LC_MESSAGES/keystone.po new file mode 100644 index 00000000..70752350 --- /dev/null +++ b/keystone-moon/keystone/locale/es/LC_MESSAGES/keystone.po @@ -0,0 +1,1292 @@ +# Spanish translations for keystone. +# Copyright (C) 2015 OpenStack Foundation +# This file is distributed under the same license as the keystone project. +# +# Translators: +# Alberto Molina Coballes <alb.molina@gmail.com>, 2014 +# dario hereñu <magallania@gmail.com>, 2015 +# Guillermo Vitas Gil <gvitgo@gmail.com>, 2014 +# Jose Enrique Ruiz Navarro <joseenriquernavarro@gmail.com>, 2014 +# Jose Ramirez Garcia <jose.ramirez.rk@gmail.com>, 2014 +# Pablo Sanchez <furybeat@gmail.com>, 2015 +# OpenStack Infra <zanata@openstack.org>, 2015. #zanata +# Tom Cocozzello <tjcocozz@us.ibm.com>, 2015. #zanata +msgid "" +msgstr "" +"Project-Id-Version: keystone 9.0.0.dev14\n" +"Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" +"POT-Creation-Date: 2015-09-24 06:09+0000\n" +"PO-Revision-Date: 2015-09-03 12:54+0000\n" +"Last-Translator: openstackjenkins <jenkins@openstack.org>\n" +"Language-Team: Spanish (http://www.transifex.com/openstack/keystone/language/" +"es/)\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\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" + +#, python-format +msgid "%(detail)s" +msgstr "%(detail)s" + +#, python-format +msgid "" +"%(event)s is not a valid notification event, must be one of: %(actions)s" +msgstr "" +"%(event)s no es u suceso de notificación válido, debe ser uno de: %(actions)s" + +#, python-format +msgid "%(host)s is not a trusted dashboard host" +msgstr "%(host)s no es un host de panel de control de confianza" + +#, python-format +msgid "%(message)s %(amendment)s" +msgstr "%(message)s %(amendment)s" + +#, python-format +msgid "" +"%(mod_name)s doesn't provide database migrations. The migration repository " +"path at %(path)s doesn't exist or isn't a directory." +msgstr "" +"%(mod_name)s no proporciona migración de base de datos. La vía de acceso de " +"repositorio de migración en %(path)s no existe o no es un directorio." + +#, python-format +msgid "%(property_name)s cannot be less than %(min_length)s characters." +msgstr "%(property_name)s no puede tener menos de %(min_length)s caracteres." + +#, python-format +msgid "%(property_name)s is not a %(display_expected_type)s" +msgstr "%(property_name)s no es una %(display_expected_type)s" + +#, python-format +msgid "%(property_name)s should not be greater than %(max_length)s characters." +msgstr "%(property_name)s no debe tener más de %(max_length)s caracteres." + +#, python-format +msgid "%s cannot be empty." +msgstr "%s no puede estar vacío." + +#, python-format +msgid "%s extension does not exist." +msgstr "La extensión %s no existe." + +#, python-format +msgid "%s field is required and cannot be empty" +msgstr "campo %s es necesario y no puede estar vacío" + +#, python-format +msgid "%s field(s) cannot be empty" +msgstr "%s campo(s) no puede estar vacío" + +msgid "(Disable debug mode to suppress these details.)" +msgstr "(Inhabilite la modalidad de depuración para suprimir estos detalles.)" + +msgid "--all option cannot be mixed with other options" +msgstr "La opción --all no puede mezclarse con otras opciones" + +msgid "A project-scoped token is required to produce a service catalog." +msgstr "" +"Se necesita una señal con ámbito de proyecto para producir un catálogo de " +"servicio." + +msgid "Access token is expired" +msgstr "El token de acceso ha expirado" + +msgid "Access token not found" +msgstr "No se ha encontrado el token de acceso" + +msgid "Additional authentications steps required." +msgstr "Se precisan pasos adicionales de autenticación." + +msgid "An unexpected error occurred when retrieving domain configs" +msgstr "" +"Se ha producido un error inesperado al recuperar configuraciones de dominio" + +#, python-format +msgid "An unexpected error occurred when trying to store %s" +msgstr "Un error inesperado ocurrió cuando se intentaba almacenar %s" + +msgid "An unexpected error prevented the server from fulfilling your request." +msgstr "" +"El servidor no ha podido completar su petición debido a un error inesperado." + +#, python-format +msgid "" +"An unexpected error prevented the server from fulfilling your request: " +"%(exception)s" +msgstr "" +"Un error inesperado a impedido que el servidor complete su solicitud: " +"%(exception)s" + +msgid "An unhandled exception has occurred: Could not find metadata." +msgstr "" +"Se ha producido una excepción no manejada: no se han podido encontrar los " +"metadatos." + +msgid "At least one option must be provided" +msgstr "Debe especificar al menos una opción" + +msgid "At least one option must be provided, use either --all or --domain-name" +msgstr "Debe proporcionarse al menos una opción, utilice --all o --domain-name" + +msgid "At least one role should be specified." +msgstr "Al menos debe especificarse un rol" + +msgid "Attempted to authenticate with an unsupported method." +msgstr "Se ha intentado autenticar con un método no compatible." + +msgid "" +"Attempting to use OS-FEDERATION token with V2 Identity Service, use V3 " +"Authentication" +msgstr "" +"Intentando utilizar la señal OS-FEDERATION con el servicio de identidad V2, " +"utilice la autenticación V3 ." + +msgid "Authentication plugin error." +msgstr "Error en el complemento de autenticación " + +msgid "Cannot authorize a request token with a token issued via delegation." +msgstr "" +"No se puede autorizar una señal de solicitud con una señal emitida mediante " +"delegación." + +#, python-format +msgid "Cannot change %(option_name)s %(attr)s" +msgstr "No se puede cambiar %(option_name)s %(attr)s" + +msgid "Cannot change Domain ID" +msgstr "No se puede cambiar el ID del Dominio" + +msgid "Cannot change consumer secret" +msgstr "No se puede cambiar el secreto de consumidor" + +msgid "Cannot change user ID" +msgstr "No se puede cambiar el ID de usuario" + +msgid "Cannot change user name" +msgstr "No se puede cambiar el nombre de usuario" + +#, python-format +msgid "Cannot create project with parent: %(project_id)s" +msgstr "No se puede crear el proyecto con padre: %(project_id)s" + +#, python-format +msgid "Cannot duplicate name %s" +msgstr "No se puede duplicar nombre %s" + +msgid "Cannot list request tokens with a token issued via delegation." +msgstr "" +"No se pueden listar las señales de solicitud con una señal emitida mediante " +"delegación." + +#, python-format +msgid "Cannot open certificate %(cert_file)s. Reason: %(reason)s" +msgstr "No se puede abrir el certificado %(cert_file)s. Razón: %(reason)s" + +#, python-format +msgid "Cannot remove role that has not been granted, %s" +msgstr "No se puede eliminar un rol que no se ha otorgado, %s" + +msgid "" +"Cannot truncate a driver call without hints list as first parameter after " +"self " +msgstr "" +"No se puede truncar una llamada de controlador si lista de sugerencias como " +"primer parámetro después de self " + +msgid "" +"Cannot use parents_as_list and parents_as_ids query params at the same time." +msgstr "" +"No se pueden utilizar los parámetros de consulta parents_as_list y " +"parents_as_ids al mismo tiempo." + +msgid "" +"Cannot use subtree_as_list and subtree_as_ids query params at the same time." +msgstr "" +"No se pueden utilizar los parámetros de consulta subtree_as_list y " +"subtree_as_ids al mismo tiempo." + +msgid "" +"Combining effective and group filter will always result in an empty list." +msgstr "" +"La combinación de filtro de grupo y efectivo dará siempre como resultado una " +"lista vacía." + +msgid "" +"Combining effective, domain and inherited filters will always result in an " +"empty list." +msgstr "" +"La combinación de filtros heredados, de dominio y efectivos dará siempre " +"como resultado una lista vacía." + +#, python-format +msgid "Conflict occurred attempting to store %(type)s - %(details)s" +msgstr "Ha ocurrido un conflicto al intentar almacenar %(type)s - %(details)s" + +#, python-format +msgid "Conflicting region IDs specified: \"%(url_id)s\" != \"%(ref_id)s\"" +msgstr "" +"Se han especificado ID de región conflictivos: \"%(url_id)s\" != \"%(ref_id)s" +"\"" + +msgid "Consumer not found" +msgstr "No se ha encontrado el consumidor" + +#, python-format +msgid "" +"Could not change immutable attribute(s) '%(attributes)s' in target %(target)s" +msgstr "" +"No se pueden cambiar atributos inalterables '%(attributes)s' en el destino " +"%(target)s" + +#, python-format +msgid "" +"Could not find %(group_or_option)s in domain configuration for domain " +"%(domain_id)s" +msgstr "" +"No se ha podido encontrar %(group_or_option)s en la configuración de dominio " +"para el dominio %(domain_id)s" + +#, python-format +msgid "Could not find Endpoint Group: %(endpoint_group_id)s" +msgstr "No se ha encontrado un grupo de puntos finales: %(endpoint_group_id)s" + +msgid "Could not find Identity Provider identifier in environment" +msgstr "" +"No se ha podido encontrar el identificador del proveedor de identidad en el " +"entorno" + +#, python-format +msgid "Could not find Identity Provider: %(idp_id)s" +msgstr "No se ha podido encontrar el proveedor de identidad: %(idp_id)s" + +#, python-format +msgid "Could not find Service Provider: %(sp_id)s" +msgstr "No se ha podido encontrar el proveedor de servicios: %(sp_id)s" + +#, python-format +msgid "Could not find credential: %(credential_id)s" +msgstr "No se ha podido encontrar la credencial: %(credential_id)s" + +#, python-format +msgid "Could not find domain: %(domain_id)s" +msgstr "No se ha podido encontrar el dominio: %(domain_id)s" + +#, python-format +msgid "Could not find endpoint: %(endpoint_id)s" +msgstr "No se ha podido encontrar : %(endpoint_id)s" + +#, python-format +msgid "" +"Could not find federated protocol %(protocol_id)s for Identity Provider: " +"%(idp_id)s" +msgstr "" +"No se ha podido encontrar el protocolo federado %(protocol_id)s para el " +"proveedor de identidad: %(idp_id)s" + +#, python-format +msgid "Could not find group: %(group_id)s" +msgstr "No se ha podido encontrar el grupo: %(group_id)s" + +#, python-format +msgid "Could not find mapping: %(mapping_id)s" +msgstr "No se ha podido encontrar la correlación: %(mapping_id)s" + +msgid "Could not find policy association" +msgstr "No se ha encontrado una asociación de política" + +#, python-format +msgid "Could not find policy: %(policy_id)s" +msgstr "No se ha podido encontrar : %(policy_id)s" + +#, python-format +msgid "Could not find project: %(project_id)s" +msgstr "No se ha podido encontrar el proyecto: %(project_id)s" + +#, python-format +msgid "Could not find region: %(region_id)s" +msgstr "No se ha podido encontrar la región: %(region_id)s" + +msgid "Could not find role" +msgstr "No se puede encontrar la función" + +#, python-format +msgid "" +"Could not find role assignment with role: %(role_id)s, user or group: " +"%(actor_id)s, project or domain: %(target_id)s" +msgstr "" +"No se ha podido encontrar la asignación de roles con el rol: %(role_id)s, " +"usuario o grupo: %(actor_id)s, proyecto o dominio: %(target_id)s" + +#, python-format +msgid "Could not find role: %(role_id)s" +msgstr "No se ha podido encontrar el rol: %(role_id)s" + +#, python-format +msgid "Could not find service: %(service_id)s" +msgstr "No se ha podido encontrar el servicio: %(service_id)s" + +#, python-format +msgid "Could not find token: %(token_id)s" +msgstr "No se ha podido encontrar la señal: %(token_id)s" + +#, python-format +msgid "Could not find trust: %(trust_id)s" +msgstr "No se ha podido encontrar la confianza: %(trust_id)s" + +#, python-format +msgid "Could not find user: %(user_id)s" +msgstr "No se ha podido encontrar el usuario: %(user_id)s" + +#, python-format +msgid "Could not find version: %(version)s" +msgstr "No se ha podido encontrar la versión: %(version)s" + +#, python-format +msgid "Could not find: %(target)s" +msgstr "No se ha podido encontrar : %(target)s" + +msgid "Could not validate the access token" +msgstr "No se ha podido validar la señal de acceso" + +msgid "Credential belongs to another user" +msgstr "La credencial pertenece a otro usuario" + +#, python-format +msgid "Database at /domains/%s/config" +msgstr "Base de datos en /domains/%s/config" + +msgid "" +"Disabling an entity where the 'enable' attribute is ignored by configuration." +msgstr "" +"Inhabilitando una entidad donde el atributo 'enable' se omite en la " +"configuración." + +#, python-format +msgid "Domain (%s)" +msgstr "Dominio (%s)" + +#, python-format +msgid "Domain cannot be named %s" +msgstr "El dominio no se puede llamar %s" + +#, python-format +msgid "Domain cannot have ID %s" +msgstr "El dominio no puede tener el ID %s" + +#, python-format +msgid "Domain is disabled: %s" +msgstr "El dominio está inhabilitado: %s" + +msgid "Domain metadata not supported by LDAP" +msgstr "Metadatos de dominio no soportados por LDAP" + +msgid "Domain scoped token is not supported" +msgstr "La señal con ámbito de dominio no está soportada" + +#, python-format +msgid "" +"Domain: %(domain)s already has a configuration defined - ignoring file: " +"%(file)s." +msgstr "" +"Dominio: %(domain)s ya tiene definida una configuración - ignorando el " +"archivo: %(file)s." + +msgid "Domains are read-only against LDAP" +msgstr "Los dominios son de sólo lectura para LDAP" + +msgid "Duplicate Entry" +msgstr "Entrada Duplicada " + +#, python-format +msgid "Duplicate ID, %s." +msgstr "ID duplicado, %s." + +#, python-format +msgid "Duplicate name, %s." +msgstr "Nombre duplicado, %s." + +msgid "Enabled field must be a boolean" +msgstr "El campo habilitado debe ser un booleano" + +msgid "Enabled field should be a boolean" +msgstr "El campo habilitado debe ser un booleano" + +#, python-format +msgid "Endpoint %(endpoint_id)s not found in project %(project_id)s" +msgstr "" +"No se ha encontrado el punto final %(endpoint_id)s en el proyecto " +"%(project_id)s" + +msgid "Endpoint Group Project Association not found" +msgstr "" +"No se ha encontrado la asociación del proyecto del grupo de puntos finales" + +msgid "Ensure configuration option idp_entity_id is set." +msgstr "" +"Compruebe que se haya establecido la opción de configuración idp_entity_id." + +msgid "Ensure configuration option idp_sso_endpoint is set." +msgstr "" +"Compruebe que se haya establecido la opción de configuración " +"idp_sso_endpoint." + +#, python-format +msgid "" +"Error parsing configuration file for domain: %(domain)s, file: %(file)s." +msgstr "" +"Error al analizar el archivo de configuración para el dominio: %(domain)s, " +"archivo: %(file)s." + +#, python-format +msgid "Error while reading metadata file, %(reason)s" +msgstr "Error al leer el archivo de metadatos, %(reason)s" + +#, python-format +msgid "Expected dict or list: %s" +msgstr "Se espera un diccionario o una lista: %s" + +msgid "" +"Expected signing certificates are not available on the server. Please check " +"Keystone configuration." +msgstr "" +"Los certificados para firmas esperados no están disponibles en el servidor. " +"Compruebe la configuración de Keystone." + +#, python-format +msgid "" +"Expecting to find %(attribute)s in %(target)s - the server could not comply " +"with the request since it is either malformed or otherwise incorrect. The " +"client is assumed to be in error." +msgstr "" +"Esperando encontrar %(attribute)s en %(target)s - el servidor no pudo " +"cumplir la solicitud porque está formada incorrectamente o de otra forma es " +"incorrecta. El cliente se asume en error." + +#, python-format +msgid "Failed to start the %(name)s server" +msgstr "No se ha podido iniciar el servidor %(name)s" + +msgid "Failed to validate token" +msgstr "Ha fallado la validación del token" + +msgid "Federation token is expired" +msgstr "La señal de federación ha caducado" + +#, python-format +msgid "" +"Field \"remaining_uses\" is set to %(value)s while it must not be set in " +"order to redelegate a trust" +msgstr "" +"El campo \"remaining_uses\" está establecido en %(value)s, pero no debe " +"estar establecido para poder redelegar una confianza" + +msgid "Found invalid token: scoped to both project and domain." +msgstr "" +"Se ha encontrado una señal no válida: tiene un ámbito de proyecto y dominio." + +#, python-format +msgid "Group %(group)s is not supported for domain specific configurations" +msgstr "" +"El grupo %(group)s no se admite para las configuraciones específicas de " +"dominio" + +#, python-format +msgid "" +"Group %(group_id)s returned by mapping %(mapping_id)s was not found in the " +"backend." +msgstr "" +"El grupo %(group_id)s devuelto por la correlación %(mapping_id)s no se ha " +"encontrado en el programa de fondo." + +#, python-format +msgid "" +"Group membership across backend boundaries is not allowed, group in question " +"is %(group_id)s, user is %(user_id)s" +msgstr "" +"La pertenencia a grupos en los límites del programa de fondo no está " +"permitida, el grupo en cuestión es %(group_id)s, el usuario es %(user_id)s" + +#, python-format +msgid "ID attribute %(id_attr)s not found in LDAP object %(dn)s" +msgstr "" +"No se ha encontrado el ID de atributo %(id_attr)s en el objeto LDAP %(dn)s" + +#, python-format +msgid "Identity Provider %(idp)s is disabled" +msgstr "El proveedor de identidad %(idp)s está inhabilitado" + +msgid "" +"Incoming identity provider identifier not included among the accepted " +"identifiers." +msgstr "" +"No se ha incluido el identificador del proveedor de identidad de entrada " +"entre los identificadores aceptados." + +#, python-format +msgid "Invalid LDAP TLS certs option: %(option)s. Choose one of: %(options)s" +msgstr "Opción de LDAP TLS no válida: %(option)s. Elegir uno de: %(options)s" + +#, python-format +msgid "Invalid LDAP TLS_AVAIL option: %s. TLS not available" +msgstr "Opción LDAP TLS_AVAIL inválida: %s. TLS no disponible" + +#, python-format +msgid "Invalid LDAP deref option: %(option)s. Choose one of: %(options)s" +msgstr "Opción deref LDAP no válida: %(option)s. Elija una de: %(options)s" + +#, python-format +msgid "Invalid LDAP scope: %(scope)s. Choose one of: %(options)s" +msgstr "" +"Ámbito LDAP incorrecto: %(scope)s. Selecciones una de las siguientes " +"opciones: %(options)s" + +msgid "Invalid TLS / LDAPS combination" +msgstr "Combinación TLS/LDAPS no válida" + +#, python-format +msgid "Invalid audit info data type: %(data)s (%(type)s)" +msgstr "" +"Tipo de datos de información de auditoría no válido: %(data)s (%(type)s)" + +msgid "Invalid blob in credential" +msgstr "Blob no válido en credencial" + +#, python-format +msgid "" +"Invalid domain name: %(domain)s found in config file name: %(file)s - " +"ignoring this file." +msgstr "" +"Nombre de dominio no válido: %(domain)s encontrado en el nombre de archivo " +"de configuración: %(file)s - ignorando este archivo." + +#, python-format +msgid "Invalid domain specific configuration: %(reason)s" +msgstr "Configuración específica de dominio no válida: %(reason)s" + +#, python-format +msgid "Invalid input for field '%(path)s'. The value is '%(value)s'." +msgstr "Entrada no válida para el campo '%(path)s'. El valor es '%(value)s'." + +msgid "Invalid limit value" +msgstr "Valor de límite no válido" + +#, python-format +msgid "" +"Invalid mix of entities for policy association - only Endpoint, Service or " +"Region+Service allowed. Request was - Endpoint: %(endpoint_id)s, Service: " +"%(service_id)s, Region: %(region_id)s" +msgstr "" +"Combinación no válida de entidades para la asociación de políticas: solo se " +"permite Punto final, Servicio o Región + Servicio. La solicitud fue: Punto " +"final: %(endpoint_id)s, Servicio: %(service_id)s, Región: %(region_id)s" + +#, python-format +msgid "" +"Invalid rule: %(identity_value)s. Both 'groups' and 'domain' keywords must " +"be specified." +msgstr "" +"Regla no válida: %(identity_value)s. Se deben especificar las palabras clave " +"'grupos' y 'dominio ." + +msgid "Invalid signature" +msgstr "Firma no válida" + +#, python-format +msgid "" +"Invalid ssl_cert_reqs value of %s, must be one of \"NONE\", \"OPTIONAL\", " +"\"REQUIRED\"" +msgstr "" +"Valor ssl_cert_reqs no válido de %s, debe ser uno de \"NONE\", \"OPTIONAL\", " +"\"REQUIRED\"" + +msgid "Invalid user / password" +msgstr "Usuario / contraseña no válidos" + +msgid "Invalid username or password" +msgstr "Usuario o contraseña no válidos" + +#, python-format +msgid "KVS region %s is already configured. Cannot reconfigure." +msgstr "La región KVS %s ya se ha configurado. No se puede reconfigurar." + +#, python-format +msgid "Key Value Store not configured: %s" +msgstr "Almacén de valor de clave no configurado: %s" + +#, python-format +msgid "LDAP %s create" +msgstr "Creación de LDAP %s" + +#, python-format +msgid "LDAP %s delete" +msgstr "Supresión de LDAP %s" + +#, python-format +msgid "LDAP %s update" +msgstr "Actualización de LDAP %s" + +#, python-format +msgid "Lock Timeout occurred for key, %(target)s" +msgstr "Se ha producido tiempo de espera de bloqueo para la clave, %(target)s" + +#, python-format +msgid "Lock key must match target key: %(lock)s != %(target)s" +msgstr "" +"La clave de bloqueo debe coincidir con la clave de destino: %(lock)s != " +"%(target)s" + +#, python-format +msgid "Malformed endpoint URL (%(endpoint)s), see ERROR log for details." +msgstr "" +"URL de punto final formado incorrectamente (%(endpoint)s), vea el registro " +"de ERROR para obtener detalles." + +msgid "Marker could not be found" +msgstr "No se ha podido encontrar el marcador" + +#, python-format +msgid "Maximum lock attempts on %s occurred." +msgstr "Se han producido los intentos de bloqueo máximos en %s." + +#, python-format +msgid "Member %(member)s is already a member of group %(group)s" +msgstr "El miembro %(member)s ya es miembro del grupo %(group)s" + +#, python-format +msgid "Method not callable: %s" +msgstr "Método no invocable: %s" + +msgid "Missing entity ID from environment" +msgstr "Falta el ID de entidad del entorno" + +msgid "" +"Modifying \"redelegation_count\" upon redelegation is forbidden. Omitting " +"this parameter is advised." +msgstr "" +"La modificación de \"redelegation_count\" tras la redelegación está " +"prohibida. Se recomienda omitir este parámetro." + +msgid "Multiple domains are not supported" +msgstr "No se admiten varios dominios" + +msgid "Must be called within an active lock context." +msgstr "Se debe llamar dentro de un contexto de bloqueo activo." + +msgid "Must specify either domain or project" +msgstr "Debe especificar dominio o proyecto" + +msgid "Name field is required and cannot be empty" +msgstr "El nombre de campo es necesario y no puede estar vacío" + +msgid "" +"No Authorization headers found, cannot proceed with OAuth related calls, if " +"running under HTTPd or Apache, ensure WSGIPassAuthorization is set to On." +msgstr "" +"No se han encontrado cabeceras de autorización, no se puede continuar con " +"las llamadas relacionadas OAuth, si se están ejecutando bajo HTTPd o Apache, " +"asegúrese de que WSGIPassAuthorization se establece en activada." + +msgid "No authenticated user" +msgstr "Ningún usuario autenticado " + +msgid "" +"No encryption keys found; run keystone-manage fernet_setup to bootstrap one." +msgstr "" +"No se han encontrado claves de cifrado; ejecute keystone-manage fernet_setup " +"para el programa de arranque uno." + +msgid "No options specified" +msgstr "No se especificaron opciones" + +#, python-format +msgid "No policy is associated with endpoint %(endpoint_id)s." +msgstr "No hay ninguna política asociada con el punto final %(endpoint_id)s." + +#, python-format +msgid "No remaining uses for trust: %(trust_id)s" +msgstr "No quedan usos para la confianza: %(trust_id)s" + +msgid "Non-default domain is not supported" +msgstr "El dominio no predeterminado no está soportado" + +msgid "One of the trust agents is disabled or deleted" +msgstr "Uno de los agentes de confianza está inhabilitado o se ha suprimido" + +#, python-format +msgid "" +"Option %(option)s found with no group specified while checking domain " +"configuration request" +msgstr "" +"Se ha encontrado la opción %(option)s sin grupo especificado al comprobar la " +"solicitud de configuración del dominio" + +#, python-format +msgid "" +"Option %(option)s in group %(group)s is not supported for domain specific " +"configurations" +msgstr "" +"La opción %(option)s del grupo %(group)s no se admite para las " +"configuraciones específicas del dominio" + +#, python-format +msgid "Project (%s)" +msgstr "Proyecto (%s)" + +#, python-format +msgid "Project is disabled: %s" +msgstr "El proyecto está inhabilitado: %s" + +msgid "Redelegation allowed for delegated by trust only" +msgstr "Sólo se permite volver a delegar un delegado por confianza" + +#, python-format +msgid "" +"Remaining redelegation depth of %(redelegation_depth)d out of allowed range " +"of [0..%(max_count)d]" +msgstr "" +"La profundidad de redelegación restante de %(redelegation_depth)d está fuera " +"del rango permitido de [0..%(max_count)d]" + +msgid "Request Token does not have an authorizing user id" +msgstr "El token de solicitud no tiene un id de usuario de autorización" + +#, python-format +msgid "" +"Request attribute %(attribute)s must be less than or equal to %(size)i. The " +"server could not comply with the request because the attribute size is " +"invalid (too large). The client is assumed to be in error." +msgstr "" +"El atributo de solicitud %(attribute)s debe ser menor que o igual a " +"%(size)i. El servidor no pudo cumplir con la solicitud debido al tamaño del " +"atributo no es válido (demasiado grande). El cliente se asume en error." + +msgid "Request must have an origin query parameter" +msgstr "La solicitud debe tener un parámetro de consulta de origen" + +msgid "Request token is expired" +msgstr "El token solicitado ha expirado" + +msgid "Request token not found" +msgstr "No se ha encontrado el token solicitado" + +msgid "Requested expiration time is more than redelegated trust can provide" +msgstr "" +"El tiempo de caducidad solicitado es mayor que el que puede proporcionar la " +"confianza redelegada" + +#, python-format +msgid "" +"Requested redelegation depth of %(requested_count)d is greater than allowed " +"%(max_count)d" +msgstr "" +"La profundidad de redelegación solicitada de %(requested_count)d es mayor " +"que la permitida %(max_count)d" + +#, python-format +msgid "Role %s not found" +msgstr "No se ha encontrado el rol %s" + +msgid "" +"Running keystone via eventlet is deprecated as of Kilo in favor of running " +"in a WSGI server (e.g. mod_wsgi). Support for keystone under eventlet will " +"be removed in the \"M\"-Release." +msgstr "" +"La ejecución de keystone a través de eventlet está en desuso a partir de " +"Kilo sustituyéndose por la ejecución en un servidor WSGI (por ejemplo, " +"mod_wsgi). El soporte para keystone bajo eventlet se eliminará en \"M\"-" +"Release." + +msgid "Scoping to both domain and project is not allowed" +msgstr "El ámbito para dominio y proyecto no está permitido" + +msgid "Scoping to both domain and trust is not allowed" +msgstr "El ámbito para dominio y confianza no está permitido" + +msgid "Scoping to both project and trust is not allowed" +msgstr "El ámbito para proyecto y confianza no está permitido" + +#, python-format +msgid "Service Provider %(sp)s is disabled" +msgstr "El proveedor de servicios %(sp)s está inhabilitado" + +msgid "Some of requested roles are not in redelegated trust" +msgstr "Algunos roles solicitados no están en la confianza redelegada" + +msgid "Specify a domain or project, not both" +msgstr "Especifique un dominio o proyecto, no ambos" + +msgid "Specify a user or group, not both" +msgstr "Especifique un usuario o grupo, no ambos" + +msgid "Specify one of domain or project" +msgstr "Especifique un dominio o proyecto" + +msgid "Specify one of user or group" +msgstr "Especifique un usuario o grupo" + +#, python-format +msgid "" +"String length exceeded.The length of string '%(string)s' exceeded the limit " +"of column %(type)s(CHAR(%(length)d))." +msgstr "" +"La longitud de la serie se ha excedido. La longitud de la serie '%(string)s' " +"ha excedido el límite de la columna %(type)s(CHAR(%(length)d))." + +msgid "The --all option cannot be used with the --domain-name option" +msgstr "La opción --all no se puede utilizar con la opción --domain-name" + +#, python-format +msgid "The Keystone configuration file %(config_file)s could not be found." +msgstr "" +"El archivo de configuración de Keystone %(config_file)s no se ha podido " +"encontrar." + +#, python-format +msgid "" +"The Keystone domain-specific configuration has specified more than one SQL " +"driver (only one is permitted): %(source)s." +msgstr "" +"La configuración específica del dominio Keystone ha especificado más de un " +"controlador SQL (sólo se permite uno): %(source)s." + +msgid "The action you have requested has not been implemented." +msgstr "La acción que ha solicitado no ha sido implemento" + +msgid "The authenticated user should match the trustor." +msgstr "El usuario autenticado debe coincidir con el fideicomitente." + +msgid "" +"The certificates you requested are not available. It is likely that this " +"server does not use PKI tokens otherwise this is the result of " +"misconfiguration." +msgstr "" +"Los certificados que ha solicitado no están disponibles. Es probable que " +"este servidor no utilice señales PKI, de lo contrario este es el resultado " +"de una configuración incorrecta." + +#, python-format +msgid "" +"The password length must be less than or equal to %(size)i. The server could " +"not comply with the request because the password is invalid." +msgstr "" +"La longitud de la contraseña debe ser menor o igual que %(size)i. El " +"servidor no pudo cumplir la solicitud porque la contraseña no es válida." + +msgid "The request you have made requires authentication." +msgstr "La solicitud que ha hecho requiere autenticación." + +msgid "The resource could not be found." +msgstr "El recurso no se ha podido encontrar." + +msgid "" +"The revoke call must not have both domain_id and project_id. This is a bug " +"in the Keystone server. The current request is aborted." +msgstr "" +"La llamada de revocación debe tener un id_dominio y un id_proyecto. Esto es " +"un error del servidor de Keystone. La solicitud actual ha terminado " +"anormalmente." + +msgid "The service you have requested is no longer available on this server." +msgstr "El servicio que ha solicitado ya no está disponible en este servidor." + +#, python-format +msgid "" +"The specified parent region %(parent_region_id)s would create a circular " +"region hierarchy." +msgstr "" +"La región padre %(parent_region_id)s especificada crearía una jerarquía de " +"regiones circular." + +#, python-format +msgid "" +"The value of group %(group)s specified in the config should be a dictionary " +"of options" +msgstr "" +"El valor de grupo %(group)s especificado en la configuración debe ser un " +"diccionario de opciones" + +msgid "There should not be any non-oauth parameters" +msgstr "Solo puede haber parámetros de oauth" + +#, python-format +msgid "This is not a recognized Fernet payload version: %s" +msgstr "Esta no es una versión de carga útil Fernet reconocida: %s" + +msgid "" +"This is not a v2.0 Fernet token. Use v3 for trust, domain, or federated " +"tokens." +msgstr "" +"Esta no es una señal v2.0 Fernet. Utilice v3 para señales de confianza, " +"dominio o federadas ." + +msgid "" +"Timestamp not in expected format. The server could not comply with the " +"request since it is either malformed or otherwise incorrect. The client is " +"assumed to be in error." +msgstr "" +"La indicación de fecha y hora no está en el formato esperado. El servidor no " +"ha podido satisfacer la solicitud porque tiene un formato incorrecto o es " +"incorrecta de alguna otra forma. Se supone que el cliente es erróneo." + +#, python-format +msgid "" +"To get a more detailed information on this error, re-run this command for " +"the specific domain, i.e.: keystone-manage domain_config_upload --domain-" +"name %s" +msgstr "" +"Para obtener información más detallada sobre este error, vuelva a ejecutar " +"este mandato para el dominio especificado, por ejemplo: keystone-manage " +"domain_config_upload --domain-name %s" + +msgid "Token belongs to another user" +msgstr "El token pertenece a otro usuario" + +msgid "Token does not belong to specified tenant." +msgstr "La señal no pertenece al arrendatario especificado." + +msgid "Trustee has no delegated roles." +msgstr "La entidad de confianza no tiene roles delegados." + +msgid "Trustor is disabled." +msgstr "Trustor está deshabilitado." + +#, python-format +msgid "" +"Trying to update group %(group)s, so that, and only that, group must be " +"specified in the config" +msgstr "" +"Intentando actualizar el grupo %(group)s, para que ese, y sólo ese grupo se " +"especifique en la configuración" + +#, python-format +msgid "" +"Trying to update option %(option)s in group %(group)s, but config provided " +"contains option %(option_other)s instead" +msgstr "" +"Intentando actualizar la opción %(option)s en el grupo %(group)s, pero la " +"configuración proporcionada contiene la opción %(option_other)s en su lugar" + +#, python-format +msgid "" +"Trying to update option %(option)s in group %(group)s, so that, and only " +"that, option must be specified in the config" +msgstr "" +"Intentando actualizar la opción %(option)s en el grupo %(group)s, para que " +"esa, y solo esa opción, se especifique en la configuración" + +msgid "" +"Unable to access the keystone database, please check it is configured " +"correctly." +msgstr "" +"No se puede acceder a la base de datos de keystone, compruebe si está " +"configurada correctamente." + +#, python-format +msgid "Unable to consume trust %(trust_id)s, unable to acquire lock." +msgstr "" +"No se puede consumir la confianza %(trust_id)s, no se puede adquirir el " +"bloqueo." + +#, python-format +msgid "" +"Unable to delete region %(region_id)s because it or its child regions have " +"associated endpoints." +msgstr "" +"No se puede suprimir la región %(region_id)s porque sus regiones secundarias " +"tienen puntos finales asociados." + +#, python-format +msgid "Unable to find valid groups while using mapping %(mapping_id)s" +msgstr "" +"No se pueden encontrar grupos válidos mientras se utiliza la correlación " +"%(mapping_id)s" + +#, python-format +msgid "" +"Unable to get a connection from pool id %(id)s after %(seconds)s seconds." +msgstr "" +"No se puede obtener una conexión del ID de agrupación %(id)s después de " +"%(seconds)s segundos." + +#, python-format +msgid "Unable to locate domain config directory: %s" +msgstr "No se ha podido localizar el directorio config de dominio: %s" + +#, python-format +msgid "Unable to lookup user %s" +msgstr "No se ha podido buscar el usuario %s" + +#, python-format +msgid "" +"Unable to reconcile identity attribute %(attribute)s as it has conflicting " +"values %(new)s and %(old)s" +msgstr "" +"No se puede reconciliar el atributo de identidad %(attribute)s porque tiene " +"los valores en conflicto %(new)s y %(old)s" + +#, python-format +msgid "" +"Unable to sign SAML assertion. It is likely that this server does not have " +"xmlsec1 installed, or this is the result of misconfiguration. Reason " +"%(reason)s" +msgstr "" +"No se puede firmar la aserción SAML. Es probable que este servidor no tenga " +"xmlsec1 instalado o que sea el resultado de una configuración incorrecta. " +"Razón %(reason)s" + +msgid "Unable to sign token." +msgstr "No se ha podido firmar la señal." + +#, python-format +msgid "Unexpected assignment type encountered, %s" +msgstr "Se ha encontrado un tipo de asignación inesperado, %s" + +#, python-format +msgid "" +"Unexpected combination of grant attributes - User: %(user_id)s, Group: " +"%(group_id)s, Project: %(project_id)s, Domain: %(domain_id)s" +msgstr "" +"Combinación no esperada de atributos de otorgamiento - Usuario: %(user_id)s, " +"Grupo: %(group_id)s, Proyecto: %(project_id)s, Dominio: %(domain_id)s" + +#, python-format +msgid "Unexpected status requested for JSON Home response, %s" +msgstr "Estado inesperado solicitado para la respuesta de JSON Home, %s" + +msgid "Unknown Target" +msgstr "Destino desconocido" + +#, python-format +msgid "Unknown domain '%(name)s' specified by --domain-name" +msgstr "Dominio desconocido '%(name)s' especificado por --domain-name" + +#, python-format +msgid "Unknown token version %s" +msgstr "Versión de señal desconocida %s" + +#, python-format +msgid "Unregistered dependency: %(name)s for %(targets)s" +msgstr "Dependencia no registrada: %(name)s para %(targets)s" + +msgid "Update of `parent_id` is not allowed." +msgstr "No se permite la actualización de `parent_id`." + +msgid "Use a project scoped token when attempting to create a SAML assertion" +msgstr "" +"Utilice un token de ámbito de proyecto cuando intente crear una aserción SAML" + +#, python-format +msgid "User %(u_id)s is unauthorized for tenant %(t_id)s" +msgstr "El usuario %(u_id)s no está autorizado en el proyecto %(t_id)s" + +#, python-format +msgid "User %(user_id)s already has role %(role_id)s in tenant %(tenant_id)s" +msgstr "" +"El usuario %(user_id)s ya tiene el rol %(role_id)s en el arrendatario " +"%(tenant_id)s" + +#, python-format +msgid "User %(user_id)s has no access to domain %(domain_id)s" +msgstr "El usuario %(user_id)s no tiene acceso al Dominio %(domain_id)s" + +#, python-format +msgid "User %(user_id)s has no access to project %(project_id)s" +msgstr "El usuario %(user_id)s no tiene acceso al proyecto %(project_id)s" + +#, python-format +msgid "User %(user_id)s is already a member of group %(group_id)s" +msgstr "El usuario %(user_id)s ya es miembro del grupo %(group_id)s" + +#, python-format +msgid "User '%(user_id)s' not found in group '%(group_id)s'" +msgstr "Usuario '%(user_id)s' no encontrado en el grupo '%(group_id)s'" + +msgid "User IDs do not match" +msgstr "ID de usuario no coinciden" + +#, python-format +msgid "User is disabled: %s" +msgstr "El usuario está inhabilitado: %s" + +msgid "User is not a member of the requested project" +msgstr "El usuario no es miembro del proyecto solicitado" + +msgid "User is not a trustee." +msgstr "El usuario no es de confianza." + +msgid "User not found" +msgstr "Usuario no encontrado" + +#, python-format +msgid "User type %s not supported" +msgstr "El tipo de usuario %s no está soportado" + +msgid "You are not authorized to perform the requested action." +msgstr "No está autorizado para realizar la acción solicitada." + +#, python-format +msgid "You are not authorized to perform the requested action: %(action)s" +msgstr "No está autorizado para realizar la acción solicitada: %(action)s" + +msgid "`key_mangler` functions must be callable." +msgstr "Las funciones `key_mangler` se deben poder llamar." + +msgid "`key_mangler` option must be a function reference" +msgstr "La opción `key_mangler` debe ser una referencia de función" + +msgid "any options" +msgstr "cualquier opción" + +msgid "auth_type is not Negotiate" +msgstr "auth_type no es Negotiate" + +msgid "authorizing user does not have role required" +msgstr "el usuario de autorización no tiene la función requerida" + +msgid "cache_collection name is required" +msgstr "el nombre de cache_collection es necesario" + +#, python-format +msgid "cannot create a project in a branch containing a disabled project: %s" +msgstr "" +"No se puede crear un proyecto en una rama que contiene un proyecto " +"inhabilitado: %s" + +msgid "cannot create a project within a different domain than its parents." +msgstr "" +"No se puede crear un proyecto dentro de un dominio distinto al de sus padres." + +msgid "cannot delete a domain that is enabled, please disable it first." +msgstr "" +"no se puede suprimir un dominio que está habilitado, inhabilítelo primero." + +#, python-format +msgid "cannot delete the project %s since it is not a leaf in the hierarchy." +msgstr "" +"No se puede suprimir el proyecto %s, ya que no es una hoja en la jerarquía." + +#, python-format +msgid "cannot disable project %s since its subtree contains enabled projects" +msgstr "" +"No se puede inhabilitar el proyecto %s, ya que su subárbol contiene " +"proyectos habilitados" + +#, python-format +msgid "cannot enable project %s since it has disabled parents" +msgstr "" +"No se puede habilitar el proyecto %s, ya que tiene padres inhabilitados" + +msgid "database db_name is required" +msgstr "base de datos db_name es necesario" + +msgid "db_hosts value is required" +msgstr "El valor db_hosts es necesario" + +msgid "delete the default domain" +msgstr "suprimir el dominio predeterminado" + +#, python-format +msgid "group %(group)s" +msgstr "grupo %(group)s" + +msgid "" +"idp_contact_type must be one of: [technical, other, support, administrative " +"or billing." +msgstr "" +"idp_contact_type debe ser una de estas opciones: [técnico, otros, soporte, " +"administrativo o facturación." + +msgid "integer value expected for mongo_ttl_seconds" +msgstr "se esperaba un valor entero para mongo_ttl_seconds" + +msgid "integer value expected for w (write concern attribute)" +msgstr "se esperaba un valor entero para w (atributo en cuestión write)" + +#, python-format +msgid "invalid date format %s" +msgstr "formato de fecha no válido %s" + +#, python-format +msgid "max hierarchy depth reached for %s branch." +msgstr "Se ha alcanzado la profundidad máxima de jerarquía en la rama %s." + +msgid "no ssl support available" +msgstr "Soporte SSL no disponible." + +#, python-format +msgid "option %(option)s in group %(group)s" +msgstr "opción %(option)s en el grupo %(group)s" + +msgid "pad must be single character" +msgstr "el relleno debe ser un único carácter" + +msgid "padded base64url text must be multiple of 4 characters" +msgstr "el texto base64url rellenado debe ser múltiplo de 4 caracteres" + +msgid "provided consumer key does not match stored consumer key" +msgstr "" +"la clave de consumidor proporcionada no coincide con la clave de consumidor " +"almacenada" + +msgid "provided request key does not match stored request key" +msgstr "" +"la clave de solicitud proporcionada no coincide con la clave de solicitud " +"almacenada" + +msgid "provided verifier does not match stored verifier" +msgstr "el verificador proporcionado no coincide con el verificador almacenado" + +msgid "region not type dogpile.cache.CacheRegion" +msgstr "región no tipo dogpile.cache.CacheRegion" + +msgid "remaining_uses must be a positive integer or null." +msgstr "remaining_uses debe ser un entero positivo o nulo." + +msgid "remaining_uses must not be set if redelegation is allowed" +msgstr "remaining_uses no se debe establecer si se permite la redelegación" + +msgid "replicaset_name required when use_replica is True" +msgstr "se necesita replicaset_name cuando use_replica es True (verdadero)" + +#, python-format +msgid "" +"request to update group %(group)s, but config provided contains group " +"%(group_other)s instead" +msgstr "" +"solicitud para actualizar el grupo %(group)s, pero la configuración " +"proporcionada contiene el grupo %(group_other)s en su lugar" + +msgid "rescope a scoped token" +msgstr "Volver a establecer el ámbito de una señal con ámbito" + +#, python-format +msgid "text is multiple of 4, but pad \"%s\" occurs before 2nd to last char" +msgstr "" +"el texto es múltiplo de 4, pero el relleno \"%s\" aparece antes del " +"penúltimo carácter" + +#, python-format +msgid "text is multiple of 4, but pad \"%s\" occurs before non-pad last char" +msgstr "" +"el texto es múltiplo de 4, pero el relleno \"%s\" aparece antes del último " +"carácter no de relleno" + +#, python-format +msgid "text is not a multiple of 4, but contains pad \"%s\"" +msgstr "el texto no es un múltiplo de 4, pero contiene el relleno \"%s\"" + +#, python-format +msgid "tls_cacertdir %s not found or is not a directory" +msgstr "No se ha encontrado o no es un directorio tls_cacertdir %s" + +#, python-format +msgid "tls_cacertfile %s not found or is not a file" +msgstr "No se ha encontrado o no es un fichero tls_cacertfile %s" + +#, python-format +msgid "token reference must be a KeystoneToken type, got: %s" +msgstr "" +"la referencia de señal debe ser un tipo KeystoneToken, se ha obtenido: %s" diff --git a/keystone-moon/keystone/locale/fr/LC_MESSAGES/keystone-log-error.po b/keystone-moon/keystone/locale/fr/LC_MESSAGES/keystone-log-error.po index ba787ee3..5ddd639a 100644 --- a/keystone-moon/keystone/locale/fr/LC_MESSAGES/keystone-log-error.po +++ b/keystone-moon/keystone/locale/fr/LC_MESSAGES/keystone-log-error.po @@ -5,12 +5,13 @@ # Translators: # Bruno Cornec <bruno.cornec@hp.com>, 2014 # Maxime COQUEREL <max.coquerel@gmail.com>, 2014 +# OpenStack Infra <zanata@openstack.org>, 2015. #zanata msgid "" msgstr "" -"Project-Id-Version: Keystone\n" +"Project-Id-Version: keystone 8.0.0.0b4.dev56\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" -"POT-Creation-Date: 2015-08-06 06:28+0000\n" -"PO-Revision-Date: 2015-06-26 17:13+0000\n" +"POT-Creation-Date: 2015-09-21 06:08+0000\n" +"PO-Revision-Date: 2015-06-26 05:13+0000\n" "Last-Translator: openstackjenkins <jenkins@openstack.org>\n" "Language-Team: French (http://www.transifex.com/openstack/keystone/language/" "fr/)\n" @@ -20,6 +21,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.0\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" +"X-Generator: Zanata 3.7.1\n" #, python-format msgid "" @@ -45,10 +47,6 @@ msgstr "" "l'option --debug-url a le format <host>:<port> et que le processus de " "débogage écoute sur ce port." -#, python-format -msgid "Error when signing assertion, reason: %(reason)s" -msgstr "Erreur lors de la signature d'une assertion : %(reason)s" - msgid "Failed to construct notifier" msgstr "Échec de construction de la notification" diff --git a/keystone-moon/keystone/locale/fr/LC_MESSAGES/keystone.po b/keystone-moon/keystone/locale/fr/LC_MESSAGES/keystone.po new file mode 100644 index 00000000..b2aff55b --- /dev/null +++ b/keystone-moon/keystone/locale/fr/LC_MESSAGES/keystone.po @@ -0,0 +1,1291 @@ +# French translations for keystone. +# Copyright (C) 2015 OpenStack Foundation +# This file is distributed under the same license as the keystone project. +# +# Translators: +# Fries <laurent.fries@wanadoo.fr>, 2014 +# Maxime COQUEREL <max.coquerel@gmail.com>, 2014 +# Andrew Melim <nokostya.translation@gmail.com>, 2014 +# Olivier Perrin <operrin@heliostech.fr>, 2013 +# Rémi Le Trocquer <remi.letrocquer@orange.com>, 2014 +# OpenStack Infra <zanata@openstack.org>, 2015. #zanata +# Tom Cocozzello <tjcocozz@us.ibm.com>, 2015. #zanata +msgid "" +msgstr "" +"Project-Id-Version: keystone 9.0.0.dev14\n" +"Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" +"POT-Creation-Date: 2015-09-24 06:09+0000\n" +"PO-Revision-Date: 2015-09-03 12:54+0000\n" +"Last-Translator: openstackjenkins <jenkins@openstack.org>\n" +"Language-Team: French (http://www.transifex.com/openstack/keystone/language/" +"fr/)\n" +"Plural-Forms: nplurals=2; plural=(n > 1)\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" + +#, python-format +msgid "%(detail)s" +msgstr "%(detail)s" + +#, python-format +msgid "" +"%(event)s is not a valid notification event, must be one of: %(actions)s" +msgstr "" +"%(event)s n'est pas un événement de notification valide, doit être l'une des " +"options suivantes : %(actions)s" + +#, python-format +msgid "%(host)s is not a trusted dashboard host" +msgstr "%(host)s n'est pas un hôte de tableau de bord digne de confiance" + +#, python-format +msgid "%(message)s %(amendment)s" +msgstr "%(message)s %(amendment)s" + +#, python-format +msgid "" +"%(mod_name)s doesn't provide database migrations. The migration repository " +"path at %(path)s doesn't exist or isn't a directory." +msgstr "" +"%(mod_name)s ne permet pas les migrations de base de données. Le chemin du " +"référentiel de migration %(path)s n'existe pas ou n'est pas un répertoire." + +#, python-format +msgid "%(property_name)s cannot be less than %(min_length)s characters." +msgstr "" +"%(property_name)s ne peut pas contenir moins de %(min_length)s caractères." + +#, python-format +msgid "%(property_name)s is not a %(display_expected_type)s" +msgstr "%(property_name)s n'est pas du type %(display_expected_type)s" + +#, python-format +msgid "%(property_name)s should not be greater than %(max_length)s characters." +msgstr "" +"%(property_name)s ne doit pas contenir plus de %(max_length)s caractères." + +#, python-format +msgid "%s cannot be empty." +msgstr "%s ne peut pas être vide." + +#, python-format +msgid "%s extension does not exist." +msgstr "extension %s n'existe pas." + +#, python-format +msgid "%s field is required and cannot be empty" +msgstr "La zone %s est obligatoire et ne peut pas être vide" + +#, python-format +msgid "%s field(s) cannot be empty" +msgstr "%s zone(s) ne peut(peuvent) pas être vide(s)" + +msgid "(Disable debug mode to suppress these details.)" +msgstr "(Désactivez le mode de débogage pour supprimer ces informations.)" + +msgid "--all option cannot be mixed with other options" +msgstr "-all option ne peut pas être mélanger avec d'autres options." + +msgid "A project-scoped token is required to produce a service catalog." +msgstr "Un jeton de projet est requis pour produire un catalogue de service." + +msgid "Access token is expired" +msgstr "Token d'accès est expiré" + +msgid "Access token not found" +msgstr "Token d'accès non trouvé" + +msgid "Additional authentications steps required." +msgstr "Authentifications étapes supplémentaires sont nécessaires ." + +msgid "An unexpected error occurred when retrieving domain configs" +msgstr "" +"Une erreur inattendue est survenue lors de l'extraction des configurations " +"de domaine" + +#, python-format +msgid "An unexpected error occurred when trying to store %s" +msgstr "" +"Une erreur inattendue est survenue lors de la tentative de stockage de %s" + +msgid "An unexpected error prevented the server from fulfilling your request." +msgstr "Une erreur inattendue a empêché le serveur de traiter votre requête." + +#, python-format +msgid "" +"An unexpected error prevented the server from fulfilling your request: " +"%(exception)s" +msgstr "" +"Une erreur inattendue a empêché le serveur de traiter votre requête: " +"%(exception)s" + +msgid "An unhandled exception has occurred: Could not find metadata." +msgstr "Une exception non gérée s'est produite : métadonnées introuvables." + +msgid "At least one option must be provided" +msgstr "Au moins une option doit être fourni" + +msgid "At least one option must be provided, use either --all or --domain-name" +msgstr "" +"Au moins une option doit être indiquée. Utilisez --all ou --domain-name" + +msgid "At least one role should be specified." +msgstr "Au moins un rôle doit être indiqué." + +msgid "Attempted to authenticate with an unsupported method." +msgstr "Tentative d'authentification avec une méthode non prise en charge ." + +msgid "" +"Attempting to use OS-FEDERATION token with V2 Identity Service, use V3 " +"Authentication" +msgstr "" +"Tentative d'utilisation du jeton OS-FEDERATION avec V2 Identity Service, " +"utilisez l'authentification V3" + +msgid "Authentication plugin error." +msgstr "Erreur d'authentification du plugin." + +msgid "Cannot authorize a request token with a token issued via delegation." +msgstr "" +"Impossible d'autoriser un jeton de requête avec un jeton émis par " +"l'intermédiaire de la délégation." + +#, python-format +msgid "Cannot change %(option_name)s %(attr)s" +msgstr "Impossible de modifier %(option_name)s %(attr)s" + +msgid "Cannot change Domain ID" +msgstr "Ne peut pas changer l'identifiant du domaine" + +msgid "Cannot change consumer secret" +msgstr "Impossible de changer le secret du client" + +msgid "Cannot change user ID" +msgstr "Impossible de modifier l'id de l'utilisateur" + +msgid "Cannot change user name" +msgstr "Impossible de changer le nom d'utilisateur" + +#, python-format +msgid "Cannot create project with parent: %(project_id)s" +msgstr "Impossible de créer le projet %(project_id)s avec le parent" + +#, python-format +msgid "Cannot duplicate name %s" +msgstr "Impossible de dupliquer le nom %s" + +msgid "Cannot list request tokens with a token issued via delegation." +msgstr "" +"Impossible de répertorier des jetons de requête avec un jeton émis par " +"l'intermédiaire de la délégation." + +#, python-format +msgid "Cannot open certificate %(cert_file)s. Reason: %(reason)s" +msgstr "Impossible d'ouvrir le certificat %(cert_file)s. Raison: %(reason)s" + +#, python-format +msgid "Cannot remove role that has not been granted, %s" +msgstr "Impossible de retirer le rôle qui n'est pas accordé, %s" + +msgid "" +"Cannot truncate a driver call without hints list as first parameter after " +"self " +msgstr "" +"Impossible de tronquer un appel de pilote sans avoir hints list comme " +"premier paramètre après self " + +msgid "" +"Cannot use parents_as_list and parents_as_ids query params at the same time." +msgstr "" +"Impossible d'utiliser les paramètres d'interrogation parents_as_list et " +"parents_as_ids en même temps." + +msgid "" +"Cannot use subtree_as_list and subtree_as_ids query params at the same time." +msgstr "" +"Impossible d'utiliser les paramètres d'interrogation subtree_as_list et " +"subtree_as_ids en même temps." + +msgid "" +"Combining effective and group filter will always result in an empty list." +msgstr "" +"Le fait de combiner un filtre effectif et un filtre de groupes donnera " +"toujours une liste vide." + +msgid "" +"Combining effective, domain and inherited filters will always result in an " +"empty list." +msgstr "" +"Le fait de combiner des filtres effectifs, de domaine et hérités donnera " +"toujours une liste vide." + +#, python-format +msgid "Conflict occurred attempting to store %(type)s - %(details)s" +msgstr "" +"Un conflit s'est produit lors de la tentative de stockage de %(type)s - " +"%(details)s" + +#, python-format +msgid "Conflicting region IDs specified: \"%(url_id)s\" != \"%(ref_id)s\"" +msgstr "" +"ID de région contradictoires indiqués : \"%(url_id)s\" != \"%(ref_id)s\"" + +msgid "Consumer not found" +msgstr "Client non trouvé" + +#, python-format +msgid "" +"Could not change immutable attribute(s) '%(attributes)s' in target %(target)s" +msgstr "" +"Impossible de modifier le(s) attribut(s) non modifiable(s) '%(attributes)s' " +"dans la cible %(target)s" + +#, python-format +msgid "" +"Could not find %(group_or_option)s in domain configuration for domain " +"%(domain_id)s" +msgstr "" +"%(group_or_option)s introuvable dans la configuration de domaine pour le " +"domaine %(domain_id)s" + +#, python-format +msgid "Could not find Endpoint Group: %(endpoint_group_id)s" +msgstr "Groupe de points finals introuvable : %(endpoint_group_id)s" + +msgid "Could not find Identity Provider identifier in environment" +msgstr "" +"L'identificateur de fournisseur d'identité est introuvable dans " +"l'environnement." + +#, python-format +msgid "Could not find Identity Provider: %(idp_id)s" +msgstr "Impossible de trouver l'identité du Provider: %(idp_id)s" + +#, python-format +msgid "Could not find Service Provider: %(sp_id)s" +msgstr "Le fournisseur de services %(sp_id)s est introuvable" + +#, python-format +msgid "Could not find credential: %(credential_id)s" +msgstr "Impossible de trouver les paramètres du compte: %(credential_id)s" + +#, python-format +msgid "Could not find domain: %(domain_id)s" +msgstr "Impossible de trouver le domaine: %(domain_id)s" + +#, python-format +msgid "Could not find endpoint: %(endpoint_id)s" +msgstr "Noeud final %(endpoint_id)s introuvable." + +#, python-format +msgid "" +"Could not find federated protocol %(protocol_id)s for Identity Provider: " +"%(idp_id)s" +msgstr "" +"Protocole fédéré %(protocol_id)s introuvable pour le fournisseur " +"d'identité : %(idp_id)s" + +#, python-format +msgid "Could not find group: %(group_id)s" +msgstr "Impossible de trouver le groupe: %(group_id)s" + +#, python-format +msgid "Could not find mapping: %(mapping_id)s" +msgstr "Mappage %(mapping_id)s introuvable." + +msgid "Could not find policy association" +msgstr "Association de règle introuvable." + +#, python-format +msgid "Could not find policy: %(policy_id)s" +msgstr "Règle %(policy_id)s introuvable." + +#, python-format +msgid "Could not find project: %(project_id)s" +msgstr "Impossible de trouver le projet: %(project_id)s" + +#, python-format +msgid "Could not find region: %(region_id)s" +msgstr "Impossible de trouver la région: %(region_id)s" + +msgid "Could not find role" +msgstr "Ne peut pas trouvé le role" + +#, python-format +msgid "" +"Could not find role assignment with role: %(role_id)s, user or group: " +"%(actor_id)s, project or domain: %(target_id)s" +msgstr "" +"Affectation de rôle avec le rôle : %(role_id)s, l'utilisateur ou le groupe : " +"%(actor_id)s, le projet ou le domaine : %(target_id)s introuvable" + +#, python-format +msgid "Could not find role: %(role_id)s" +msgstr "Impossible de trouver le rôle: %(role_id)s" + +#, python-format +msgid "Could not find service: %(service_id)s" +msgstr "Impossible de trouver le service: %(service_id)s" + +#, python-format +msgid "Could not find token: %(token_id)s" +msgstr "Impossible de trouver le token: %(token_id)s" + +#, python-format +msgid "Could not find trust: %(trust_id)s" +msgstr "Confiance %(trust_id)s introuvable." + +#, python-format +msgid "Could not find user: %(user_id)s" +msgstr "Impossible de trouver l'utilisateur: %(user_id)s" + +#, python-format +msgid "Could not find version: %(version)s" +msgstr "Impossible de trouver la version: %(version)s" + +#, python-format +msgid "Could not find: %(target)s" +msgstr "N'est pas trouvé: %(target)s" + +msgid "Could not validate the access token" +msgstr "Ne peut pas valider l'acces du token" + +msgid "Credential belongs to another user" +msgstr "Les données d'identification appartiennent à un autre utilisateur" + +#, python-format +msgid "Database at /domains/%s/config" +msgstr "Base de données dans /domains/%s/config" + +msgid "" +"Disabling an entity where the 'enable' attribute is ignored by configuration." +msgstr "" +"Désactivation d'une entité dont l'attribut 'enable' est ignoré par la " +"configuration." + +#, python-format +msgid "Domain (%s)" +msgstr "Domaine (%s)" + +#, python-format +msgid "Domain cannot be named %s" +msgstr "Le domaine ne peut pas s'appeler %s" + +#, python-format +msgid "Domain cannot have ID %s" +msgstr "Le domaine ne peut pas posséder l'ID %s" + +#, python-format +msgid "Domain is disabled: %s" +msgstr "Domaine désactivé : %s" + +msgid "Domain metadata not supported by LDAP" +msgstr "Les métadata du domaine ne sont pas supporté par LDAP" + +msgid "Domain scoped token is not supported" +msgstr "Le jeton de périmètre du domaine n'est pas pris en charge" + +#, python-format +msgid "" +"Domain: %(domain)s already has a configuration defined - ignoring file: " +"%(file)s." +msgstr "" +"Le domaine : %(domain)s possède déjà une configuration définie - ce fichier " +"sera ignoré : %(file)s." + +msgid "Domains are read-only against LDAP" +msgstr "Les domaines sont en lecture seule pour LDAP" + +msgid "Duplicate Entry" +msgstr "Entrée en double" + +#, python-format +msgid "Duplicate ID, %s." +msgstr "ID en double, %s." + +#, python-format +msgid "Duplicate name, %s." +msgstr "Nom en double, %s." + +msgid "Enabled field must be a boolean" +msgstr "La zone activée doit être un booléen" + +msgid "Enabled field should be a boolean" +msgstr "La zone activée devrait être un booléen" + +#, python-format +msgid "Endpoint %(endpoint_id)s not found in project %(project_id)s" +msgstr "Noeud final %(endpoint_id)s introuvable dans le projet %(project_id)s" + +msgid "Endpoint Group Project Association not found" +msgstr "Association de projets du groupe de points finals introuvable" + +msgid "Ensure configuration option idp_entity_id is set." +msgstr "Assurez-vous que l'option de configuration idp_entity_id est définie." + +msgid "Ensure configuration option idp_sso_endpoint is set." +msgstr "" +"Assurez-vous que l'option de configuration idp_sso_endpoint est définie." + +#, python-format +msgid "" +"Error parsing configuration file for domain: %(domain)s, file: %(file)s." +msgstr "" +"Erreur lors de l'analyse syntaxique du fichier de configuration pour le " +"domaine : %(domain)s, fichier : %(file)s." + +#, python-format +msgid "Error while reading metadata file, %(reason)s" +msgstr "Erreur durant la lecteur des méta data du fichier, %(reason)s" + +#, python-format +msgid "Expected dict or list: %s" +msgstr "Type dictionnaire ou liste attendu: %s" + +msgid "" +"Expected signing certificates are not available on the server. Please check " +"Keystone configuration." +msgstr "" +"Les certificats signataires attendus sont indisponibles sur le serveur. " +"Veuillez vérifier la configuration de Keystone." + +#, python-format +msgid "" +"Expecting to find %(attribute)s in %(target)s - the server could not comply " +"with the request since it is either malformed or otherwise incorrect. The " +"client is assumed to be in error." +msgstr "" +"%(attribute)s recherché dans %(target)s - le serveur n'a pas pu se conformer " +"à la requête puisqu'elle est mal formée ou incorrecte. Par défaut, le client " +"est en erreur." + +#, python-format +msgid "Failed to start the %(name)s server" +msgstr "Impossible de démarrer le serveur %(name)s" + +msgid "Failed to validate token" +msgstr "Echec de validation du token" + +msgid "Federation token is expired" +msgstr "La fédération du toke est expiré" + +#, python-format +msgid "" +"Field \"remaining_uses\" is set to %(value)s while it must not be set in " +"order to redelegate a trust" +msgstr "" +"La zone \"remaining_uses\" est définie sur %(value)s alors qu'elle ne doit " +"pas être définie pour redéléguer une fiducie" + +msgid "Found invalid token: scoped to both project and domain." +msgstr "Jeton non valide trouvé : portée de projet et de domaine." + +#, python-format +msgid "Group %(group)s is not supported for domain specific configurations" +msgstr "" +"Le groupe %(group)s n'est pas pris en charge pour les configurations " +"spécifiques au domaine" + +#, python-format +msgid "" +"Group %(group_id)s returned by mapping %(mapping_id)s was not found in the " +"backend." +msgstr "" +"Groupe %(group_id)s renvoyé par le mappage %(mapping_id)s introuvable dans " +"le backend." + +#, python-format +msgid "" +"Group membership across backend boundaries is not allowed, group in question " +"is %(group_id)s, user is %(user_id)s" +msgstr "" +"Appartenance au groupe entre frontières dorsales interdite, le groupe en " +"question est %(group_id)s, l'utilisateur est %(user_id)s" + +#, python-format +msgid "ID attribute %(id_attr)s not found in LDAP object %(dn)s" +msgstr "L'attribut ID %(id_attr)s est introuvable dans l'objet LDAP %(dn)s" + +#, python-format +msgid "Identity Provider %(idp)s is disabled" +msgstr "Le fournisseur d'identité %(idp)s est désactivé" + +msgid "" +"Incoming identity provider identifier not included among the accepted " +"identifiers." +msgstr "" +"L'identificateur entrant du fournisseur d'identité ne fait pas partie des " +"identificateurs acceptés." + +#, python-format +msgid "Invalid LDAP TLS certs option: %(option)s. Choose one of: %(options)s" +msgstr "" +"Option de certificat TLS LDAP non valide : %(option)s. Choisissez l'une des " +"options : %(options)s" + +#, python-format +msgid "Invalid LDAP TLS_AVAIL option: %s. TLS not available" +msgstr "Mauvaise option LDAP TLS_AVAIL: %s. TLS n'est pas disponible" + +#, python-format +msgid "Invalid LDAP deref option: %(option)s. Choose one of: %(options)s" +msgstr "" +"Option déréférencée LDAP non valide : %(option)s. Choisir l'une des options " +"suivantes : %(options)s" + +#, python-format +msgid "Invalid LDAP scope: %(scope)s. Choose one of: %(options)s" +msgstr "Portée LDAP invalide: %(scope)s. Choisissez parmi: %(options)s" + +msgid "Invalid TLS / LDAPS combination" +msgstr "Combinaison TLS / LDAPS invalide" + +#, python-format +msgid "Invalid audit info data type: %(data)s (%(type)s)" +msgstr "Type de données d'information d'audit non valide : %(data)s (%(type)s)" + +msgid "Invalid blob in credential" +msgstr "Blob non valide dans les informations d'identification" + +#, python-format +msgid "" +"Invalid domain name: %(domain)s found in config file name: %(file)s - " +"ignoring this file." +msgstr "" +"Nom de domaine non valide : %(domain)s trouvé dans le nom du fichier de " +"configuration : %(file)s - ce fichier sera ignoré." + +#, python-format +msgid "Invalid domain specific configuration: %(reason)s" +msgstr "Configuration spécifique au domaine non valide : %(reason)s" + +#, python-format +msgid "Invalid input for field '%(path)s'. The value is '%(value)s'." +msgstr "" +"Valeur d'entrée incorrecte pour la zone '%(path)s'. La valeur est " +"'%(value)s'." + +msgid "Invalid limit value" +msgstr "Limite de valeur non valide" + +#, python-format +msgid "" +"Invalid mix of entities for policy association - only Endpoint, Service or " +"Region+Service allowed. Request was - Endpoint: %(endpoint_id)s, Service: " +"%(service_id)s, Region: %(region_id)s" +msgstr "" +"Combinaison non valide d'entités pour l'association de règle. Seules les " +"entités Point final, Service ou Région+Service sont autorisées. La demande " +"était Point final : %(endpoint_id)s, Service : %(service_id)s, Région : " +"%(region_id)s" + +#, python-format +msgid "" +"Invalid rule: %(identity_value)s. Both 'groups' and 'domain' keywords must " +"be specified." +msgstr "" +"Règle non valide : %(identity_value)s. Les mots clés 'groups' et 'domain' " +"doivent être spécifiés." + +msgid "Invalid signature" +msgstr "Signature non valide" + +#, python-format +msgid "" +"Invalid ssl_cert_reqs value of %s, must be one of \"NONE\", \"OPTIONAL\", " +"\"REQUIRED\"" +msgstr "" +"Valeur de ssl_cert_reqs non valide (%s), doit être l'une des valeurs " +"suivantes : \"NONE\", \"OPTIONAL\", \"REQUIRED\"" + +msgid "Invalid user / password" +msgstr "Login / Mot de passe non valide" + +msgid "Invalid username or password" +msgstr "Nom d'utilisateur ou mot de passe invalide" + +#, python-format +msgid "KVS region %s is already configured. Cannot reconfigure." +msgstr "KVS region %s est déjà configuré. Ne peut pas être reconfiguré." + +#, python-format +msgid "Key Value Store not configured: %s" +msgstr "La valeur de la clé du magasin n'est pas configuré: %s" + +#, python-format +msgid "LDAP %s create" +msgstr "Création LDAP %s" + +#, python-format +msgid "LDAP %s delete" +msgstr "Suppression LDAP %s" + +#, python-format +msgid "LDAP %s update" +msgstr "Mise à jour LDAP %s" + +#, python-format +msgid "Lock Timeout occurred for key, %(target)s" +msgstr "Le délai de verrouillage s'est produit pour la clé, %(target)s" + +#, python-format +msgid "Lock key must match target key: %(lock)s != %(target)s" +msgstr "" +"La clé de verrouillage doit correspondre à la clé cible : %(lock)s != " +"%(target)s" + +#, python-format +msgid "Malformed endpoint URL (%(endpoint)s), see ERROR log for details." +msgstr "" +"Un caractère est mal formé dans URL (%(endpoint)s), regarder le log d'erreur " +"pour plus de détails." + +msgid "Marker could not be found" +msgstr "Le marqueur ne peut pas être trouvé" + +#, python-format +msgid "Maximum lock attempts on %s occurred." +msgstr "Le nombre maximal de tentatives de verrouillage sur %s est atteint." + +#, python-format +msgid "Member %(member)s is already a member of group %(group)s" +msgstr "Le membre %(member)s est déjà membre du groupe %(group)s" + +#, python-format +msgid "Method not callable: %s" +msgstr "Impossible d'appeler la méthode %s" + +msgid "Missing entity ID from environment" +msgstr "IP d'entité manquant de l'environnement" + +msgid "" +"Modifying \"redelegation_count\" upon redelegation is forbidden. Omitting " +"this parameter is advised." +msgstr "" +"La modification de \"redelegation_count\" lors de la redélégation est " +"interdite. Il est conseillé d'omettre ce paramètre." + +msgid "Multiple domains are not supported" +msgstr "Les multiples domaines ne sont pas supporté" + +msgid "Must be called within an active lock context." +msgstr "Doit être appelé dans un contexte de verrou actif." + +msgid "Must specify either domain or project" +msgstr "Indiquer obligatoirement un domaine ou un projet" + +msgid "Name field is required and cannot be empty" +msgstr "La zone de nom est requise et ne peut pas être vide" + +msgid "" +"No Authorization headers found, cannot proceed with OAuth related calls, if " +"running under HTTPd or Apache, ensure WSGIPassAuthorization is set to On." +msgstr "" +"Aucun en-tête d'autorisation trouvé, impossible de procéder aux appels liés " +"à OAuth, en cas d'exécution sous HTTPd ou Apache, vérifiez que " +"WSGIPassAuthorization est défini sur Activé." + +msgid "No authenticated user" +msgstr "Aucun utilisateur authentifié" + +msgid "" +"No encryption keys found; run keystone-manage fernet_setup to bootstrap one." +msgstr "" +"Aucune clé de chiffrement trouvée ; exécutez keystone-manage fernet_setup " +"pour en amorcer une." + +msgid "No options specified" +msgstr "Aucune option spécifiée" + +#, python-format +msgid "No policy is associated with endpoint %(endpoint_id)s." +msgstr "Aucune règle n'est associée au point final %(endpoint_id)s." + +#, python-format +msgid "No remaining uses for trust: %(trust_id)s" +msgstr "Aucune utilisation restante pour la confiance : %(trust_id)s" + +msgid "Non-default domain is not supported" +msgstr "Le domaine non par défaut n'est pas pris en charge" + +msgid "One of the trust agents is disabled or deleted" +msgstr "L'un des agents de confiance est désactivé ou supprimé" + +#, python-format +msgid "" +"Option %(option)s found with no group specified while checking domain " +"configuration request" +msgstr "" +"Option %(option)s trouvée avec aucun groupe spécifié lors de la vérification " +"de la demande de configuration du domaine" + +#, python-format +msgid "" +"Option %(option)s in group %(group)s is not supported for domain specific " +"configurations" +msgstr "" +"L'option %(option)s dans le groupe %(group)s n'est pas prise en charge pour " +"les configurations spécifiques au domaine" + +#, python-format +msgid "Project (%s)" +msgstr "Projet (%s)" + +#, python-format +msgid "Project is disabled: %s" +msgstr "Projet désactivé : %s" + +msgid "Redelegation allowed for delegated by trust only" +msgstr "Redélégation autorisée pour une délégation par fiducie uniquement" + +#, python-format +msgid "" +"Remaining redelegation depth of %(redelegation_depth)d out of allowed range " +"of [0..%(max_count)d]" +msgstr "" +"Profondeur de redélégation restante %(redelegation_depth)d par rapport à la " +"plage admise [0..%(max_count)d]" + +msgid "Request Token does not have an authorizing user id" +msgstr "Le jeton de la demande ne possède pas d'ID utilisateur d'autorisation" + +#, python-format +msgid "" +"Request attribute %(attribute)s must be less than or equal to %(size)i. The " +"server could not comply with the request because the attribute size is " +"invalid (too large). The client is assumed to be in error." +msgstr "" +"La valeur de l'attribut %(attribute)s de la demande doit être inférieure ou " +"égale à %(size)i. Il se peut que le serveur ne soit pas conforme à la " +"demande car la taille de l'attribut est incorrecte (excessive). Par défaut, " +"le client est en erreur." + +msgid "Request must have an origin query parameter" +msgstr "La demande doit avoir un paramètre de requête d'origine" + +msgid "Request token is expired" +msgstr "La requete du token est expiré" + +msgid "Request token not found" +msgstr "Token de requete non trouvé" + +msgid "Requested expiration time is more than redelegated trust can provide" +msgstr "" +"Le délai d'expiration demandé dépasse celui que la fiducie redéléguée peut " +"fournir" + +#, python-format +msgid "" +"Requested redelegation depth of %(requested_count)d is greater than allowed " +"%(max_count)d" +msgstr "" +"La profondeur de redélégation demandée %(requested_count)d est supérieure à " +"la limite autorisée %(max_count)d" + +#, python-format +msgid "Role %s not found" +msgstr "Rôle %s non trouvé" + +msgid "" +"Running keystone via eventlet is deprecated as of Kilo in favor of running " +"in a WSGI server (e.g. mod_wsgi). Support for keystone under eventlet will " +"be removed in the \"M\"-Release." +msgstr "" +"Exécution de keystone via eventlet est obsolète depuis Kilo et remplacée par " +"l'exécution dans un serveur WSGI (par exemple, mod_wsgi). La prise en charge " +"pour keystone sous l'eventlet sera supprimée dans \"M\"-Release." + +msgid "Scoping to both domain and project is not allowed" +msgstr "La configuration du domaine et du projet n'est pas autorisée" + +msgid "Scoping to both domain and trust is not allowed" +msgstr "" +"La configuration du domaine et du certificat de confiance n'est pas autorisée" + +msgid "Scoping to both project and trust is not allowed" +msgstr "" +"La configuration du projet et du certificat de confiance n'est pas autorisée" + +#, python-format +msgid "Service Provider %(sp)s is disabled" +msgstr "Le fournisseur de services %(sp)s est désactivé" + +msgid "Some of requested roles are not in redelegated trust" +msgstr "Certains rôles demandés ne font pas partie de la fiducie redéléguée" + +msgid "Specify a domain or project, not both" +msgstr "Spécifier un domaine ou un projet, pas les deux" + +msgid "Specify a user or group, not both" +msgstr "Spécifier un utilisateur ou groupe, pas les deux" + +msgid "Specify one of domain or project" +msgstr "Indiquez un domaine ou un projet" + +msgid "Specify one of user or group" +msgstr "Indiquez un utilisateur ou un groupe" + +#, python-format +msgid "" +"String length exceeded.The length of string '%(string)s' exceeded the limit " +"of column %(type)s(CHAR(%(length)d))." +msgstr "" +"Longueur de chaîne dépassée. La longueur de la chaîne '%(string)s a dépassé " +"la valeur maximale de colonne %(type)s(CHAR(%(length)d))." + +msgid "The --all option cannot be used with the --domain-name option" +msgstr "L'option --all ne peut pas être utilisée avec l'option --domain-name" + +#, python-format +msgid "The Keystone configuration file %(config_file)s could not be found." +msgstr "" +"Le fichier de configuration Keystone %(config_file)s ne peut pas être trouvé." + +#, python-format +msgid "" +"The Keystone domain-specific configuration has specified more than one SQL " +"driver (only one is permitted): %(source)s." +msgstr "" +"La configuration spécifique au domaine keystone a spécifié plusieurs pilotes " +"SQL (un seul est autorisé) : %(source)s." + +msgid "The action you have requested has not been implemented." +msgstr "L'action que vous avez demandée n'a pas été implémentée." + +msgid "The authenticated user should match the trustor." +msgstr "L'utilisateur authentifié doit correspondre au fiduciant." + +msgid "" +"The certificates you requested are not available. It is likely that this " +"server does not use PKI tokens otherwise this is the result of " +"misconfiguration." +msgstr "" +"Les certificats que vous avez demandés sont indisponibles. Il est probable " +"que ce serveur n'utilise pas les jetons PKI ; sinon, c'est le résultat d'un " +"problème de configuration." + +#, python-format +msgid "" +"The password length must be less than or equal to %(size)i. The server could " +"not comply with the request because the password is invalid." +msgstr "" +"La longueur du mot de passe doit être inférieure ou égale à %(size)i. n'est " +"pas conforme à la demande car le mot de passe est incorrect." + +msgid "The request you have made requires authentication." +msgstr "La demande que vous avez fait requiert une authentification." + +msgid "The resource could not be found." +msgstr "La ressource est introuvable." + +msgid "" +"The revoke call must not have both domain_id and project_id. This is a bug " +"in the Keystone server. The current request is aborted." +msgstr "" +"L'appel de révocation ne doit pas contenir à la fois domain_id et " +"project_id. Il s'agit d'un bogue dans le serveur Keystone. La demande en " +"cours est abandonnée." + +msgid "The service you have requested is no longer available on this server." +msgstr "Le service que vous avez demandé n'est plus disponible sur ce serveur." + +#, python-format +msgid "" +"The specified parent region %(parent_region_id)s would create a circular " +"region hierarchy." +msgstr "" +"La région parent spécifiée %(parent_region_id)s risque de créer une " +"hiérarchie de région circulaire." + +#, python-format +msgid "" +"The value of group %(group)s specified in the config should be a dictionary " +"of options" +msgstr "" +"La valeur du groupe %(group)s spécifié dans la configuration doit être un " +"dictionnaire d'options" + +msgid "There should not be any non-oauth parameters" +msgstr "Aucun paramètre non-oauth ne doit être utilisé" + +#, python-format +msgid "This is not a recognized Fernet payload version: %s" +msgstr "Il ne s'agit pas d'une version de contenu Fernet reconnue : %s" + +msgid "" +"This is not a v2.0 Fernet token. Use v3 for trust, domain, or federated " +"tokens." +msgstr "" +"Il ne s'agit pas d'un jeton v2.0 Fernet. Utilisez v3 pour les jetons de " +"confiance, de domaine ou fédérés." + +msgid "" +"Timestamp not in expected format. The server could not comply with the " +"request since it is either malformed or otherwise incorrect. The client is " +"assumed to be in error." +msgstr "" +"Horodatage n'est pas au format attendu. Le serveur n'a pas pu se conformer à " +"la demande car elle est incorrectement formée ou incorrecte. Le client est " +"considéré comme étant à l'état d'erreur." + +#, python-format +msgid "" +"To get a more detailed information on this error, re-run this command for " +"the specific domain, i.e.: keystone-manage domain_config_upload --domain-" +"name %s" +msgstr "" +"Pour obtenir des informations plus détaillées sur cette erreur, réexécutez " +"cette commande pour le domaine spécifique, par exemple : keystone-manage " +"domain_config_upload --domain-name %s" + +msgid "Token belongs to another user" +msgstr "Le jeton appartient à un autre utilisateur" + +msgid "Token does not belong to specified tenant." +msgstr "Le jeton n'appartient pas au titulaire spécifié." + +msgid "Trustee has no delegated roles." +msgstr "Le fiduciaire n'a aucun rôle délégué." + +msgid "Trustor is disabled." +msgstr "Trustor est désactivé. " + +#, python-format +msgid "" +"Trying to update group %(group)s, so that, and only that, group must be " +"specified in the config" +msgstr "" +"Tentative de mise à jour du groupe %(group)s, de sorte que le groupe soit " +"spécifié dans la configuration uniquement" + +#, python-format +msgid "" +"Trying to update option %(option)s in group %(group)s, but config provided " +"contains option %(option_other)s instead" +msgstr "" +"Tentative de mise à jour de l'option %(option)s dans le groupe %(group)s, " +"mais la configuration fournie contient l'option %(option_other)s à la place" + +#, python-format +msgid "" +"Trying to update option %(option)s in group %(group)s, so that, and only " +"that, option must be specified in the config" +msgstr "" +"Tentative de mise à jour de l'option %(option)s dans le groupe %(group)s, de " +"sorte que l'option soit spécifiée dans la configuration uniquement" + +msgid "" +"Unable to access the keystone database, please check it is configured " +"correctly." +msgstr "" +"Impossible d'accéder à la base de données keystone, vérifiez qu'elle est " +"configurée correctement." + +#, python-format +msgid "Unable to consume trust %(trust_id)s, unable to acquire lock." +msgstr "" +"Impossible de consommer la confiance %(trust_id)s et d'acquérir un verrou." + +#, python-format +msgid "" +"Unable to delete region %(region_id)s because it or its child regions have " +"associated endpoints." +msgstr "" +"Impossible de supprimer la région %(region_id)s car la région ou ses régions " +"enfant ont des noeuds finals associés." + +#, python-format +msgid "Unable to find valid groups while using mapping %(mapping_id)s" +msgstr "" +"Impossible de trouver des groupes valides en utilisant le mappage " +"%(mapping_id)s" + +#, python-format +msgid "" +"Unable to get a connection from pool id %(id)s after %(seconds)s seconds." +msgstr "" +"Impossible d'établir une connexion à partir de l'ID de pool %(id)s après " +"%(seconds)s secondes." + +#, python-format +msgid "Unable to locate domain config directory: %s" +msgstr "Impossible de localiser le répertoire de configuration domaine: %s" + +#, python-format +msgid "Unable to lookup user %s" +msgstr "Impossible de rechercher l'utilisateur %s" + +#, python-format +msgid "" +"Unable to reconcile identity attribute %(attribute)s as it has conflicting " +"values %(new)s and %(old)s" +msgstr "" +"Impossible de rapprocher l'attribut d'identité %(attribute)s car il possède " +"des valeurs en conflit : %(new)s et %(old)s" + +#, python-format +msgid "" +"Unable to sign SAML assertion. It is likely that this server does not have " +"xmlsec1 installed, or this is the result of misconfiguration. Reason " +"%(reason)s" +msgstr "" +"Impossible de signer l'assertion SAML. Il est probable que xmlsec1 ne soit " +"pas installé sur ce serveur ; sinon, cela est dû à un problème de " +"configuration. Raison : %(reason)s" + +msgid "Unable to sign token." +msgstr "Impossible de signer le jeton" + +#, python-format +msgid "Unexpected assignment type encountered, %s" +msgstr "Type inattendu d'affectation, %s" + +#, python-format +msgid "" +"Unexpected combination of grant attributes - User: %(user_id)s, Group: " +"%(group_id)s, Project: %(project_id)s, Domain: %(domain_id)s" +msgstr "" +"Combinaison inattendue d'attributs d'octroi - Utilisateur : %(user_id)s. " +"Groupe : %(group_id)s. Projet : %(project_id)s. Domaine : %(domain_id)s" + +#, python-format +msgid "Unexpected status requested for JSON Home response, %s" +msgstr "Statut inattendu demandé pour la réponse JSON Home, %s" + +msgid "Unknown Target" +msgstr "Cible inconnu" + +#, python-format +msgid "Unknown domain '%(name)s' specified by --domain-name" +msgstr "Domaine inconnu '%(name)s' spécifié par --domain-name" + +#, python-format +msgid "Unknown token version %s" +msgstr "Version du token inconnu %s" + +#, python-format +msgid "Unregistered dependency: %(name)s for %(targets)s" +msgstr "Dépendance désenregistrée : %(name)s pour %(targets)s" + +msgid "Update of `parent_id` is not allowed." +msgstr "La mise à jour de `parent_id` est interdite." + +msgid "Use a project scoped token when attempting to create a SAML assertion" +msgstr "" +"Utilisez un jeton dont la portée est un projet lorsque vous essayez de créer " +"une assertion SAML" + +#, python-format +msgid "User %(u_id)s is unauthorized for tenant %(t_id)s" +msgstr "L'utilisateur %(u_id)s n'est pas autorisé pour le locataire %(t_id)s" + +#, python-format +msgid "User %(user_id)s already has role %(role_id)s in tenant %(tenant_id)s" +msgstr "" +"L'utilisateur %(user_id)s possède déjà le rôle %(role_id)s dans le locataire " +"%(tenant_id)s" + +#, python-format +msgid "User %(user_id)s has no access to domain %(domain_id)s" +msgstr "L'utilisateur %(user_id)s n'a pas accès au domaine %(domain_id)s" + +#, python-format +msgid "User %(user_id)s has no access to project %(project_id)s" +msgstr "L'utilisateur %(user_id)s n'a pas accès au projet %(project_id)s" + +#, python-format +msgid "User %(user_id)s is already a member of group %(group_id)s" +msgstr "L'utilisateur %(user_id)s est déjà membre du groupe %(group_id)s" + +#, python-format +msgid "User '%(user_id)s' not found in group '%(group_id)s'" +msgstr "Utilisateur '%(user_id)s' non trouvé dans le groupe '%(group_id)s'" + +msgid "User IDs do not match" +msgstr "Les ID utilisateur ne correspondent pas." + +#, python-format +msgid "User is disabled: %s" +msgstr "Utilisateur désactivé : %s" + +msgid "User is not a member of the requested project" +msgstr "L'utilisateur n'est pas membre du projet demandé" + +msgid "User is not a trustee." +msgstr "L'utilisateur n'est pas administrateur." + +msgid "User not found" +msgstr "Utilisateur introuvable" + +#, python-format +msgid "User type %s not supported" +msgstr "Type d'utilisateur %s non pris en charge" + +msgid "You are not authorized to perform the requested action." +msgstr "Vous n'êtes pas autorisé à effectuer l'action demandée" + +#, python-format +msgid "You are not authorized to perform the requested action: %(action)s" +msgstr "Vous n'êtes pas autorisé à effectuer l'action demandée: %(action)s" + +msgid "`key_mangler` functions must be callable." +msgstr "Les fonctions `key_mangler` doivent pouvoir être appelées." + +msgid "`key_mangler` option must be a function reference" +msgstr "L'option `key_mangler` doit être une référence de fonction" + +msgid "any options" +msgstr "toute option" + +msgid "auth_type is not Negotiate" +msgstr "auth_type n'est pas négocié" + +msgid "authorizing user does not have role required" +msgstr "un rôle est facultatif pour l'utilisateur d'autorisation" + +msgid "cache_collection name is required" +msgstr "Nom cache_collection est requis" + +#, python-format +msgid "cannot create a project in a branch containing a disabled project: %s" +msgstr "" +"Impossible de créer un projet dans une branche qui contient un projet " +"désactivé : %s" + +msgid "cannot create a project within a different domain than its parents." +msgstr "" +"Impossible de créer un projet dans un domaine qui diffère de celui-ci de ses " +"parents." + +msgid "cannot delete a domain that is enabled, please disable it first." +msgstr "" +"Impossible de supprimer un domaine activé, s'il vous plait le désactiver en " +"premier." + +#, python-format +msgid "cannot delete the project %s since it is not a leaf in the hierarchy." +msgstr "" +"Impossible de supprimer le projet %s car il ne s'agit pas d'une feuille dans " +"la hiérarchie." + +#, python-format +msgid "cannot disable project %s since its subtree contains enabled projects" +msgstr "" +"Impossible de désactiver le projet %s car son sous-arbre contient des " +"projets activés" + +#, python-format +msgid "cannot enable project %s since it has disabled parents" +msgstr "Impossible d'activer le projet %s car ses parents sont désactivés" + +msgid "database db_name is required" +msgstr "db_name database est requis" + +msgid "db_hosts value is required" +msgstr "Valeur db_hosts est requis" + +msgid "delete the default domain" +msgstr "Suppression du domaine par défaut" + +#, python-format +msgid "group %(group)s" +msgstr "groupe %(group)s" + +msgid "" +"idp_contact_type must be one of: [technical, other, support, administrative " +"or billing." +msgstr "" +"idp_contact_type doit avoir l'une des valeurs suivantes : [technical, other, " +"support, administrative ou billing." + +msgid "integer value expected for mongo_ttl_seconds" +msgstr "valeur entière attendue pour mongo_ttl_seconds" + +msgid "integer value expected for w (write concern attribute)" +msgstr "valeur entière attendue pour w (attribut d'écriture)" + +#, python-format +msgid "invalid date format %s" +msgstr "Format de date invalid %s" + +#, python-format +msgid "max hierarchy depth reached for %s branch." +msgstr "La profondeur maximale de hiérarchie est atteinte pour la branche %s." + +msgid "no ssl support available" +msgstr "pas de support du ssl" + +#, python-format +msgid "option %(option)s in group %(group)s" +msgstr "option %(option)s dans le groupe %(group)s" + +msgid "pad must be single character" +msgstr "pad doit etre un seul caractère" + +msgid "padded base64url text must be multiple of 4 characters" +msgstr "Le texte base64url rempli doit être un multiple de 4 caractères" + +msgid "provided consumer key does not match stored consumer key" +msgstr "la clé du client fournie ne correspond pas à la clé du client stockée" + +msgid "provided request key does not match stored request key" +msgstr "" +"la clé de la demande fournie ne correspond pas à la clé de la demande stockée" + +msgid "provided verifier does not match stored verifier" +msgstr "le vérificateur fourni ne correspond pas au vérificateur stocké" + +msgid "region not type dogpile.cache.CacheRegion" +msgstr "la région n'est pas de type dogpile.cache.CacheRegion" + +msgid "remaining_uses must be a positive integer or null." +msgstr "remaining_uses doit être un entier positif ou nul." + +msgid "remaining_uses must not be set if redelegation is allowed" +msgstr "" +"remaining_uses ne doit pas être défini si la redélégation est autorisée" + +msgid "replicaset_name required when use_replica is True" +msgstr "replicaset_name requis si use_replica a la valeur True" + +#, python-format +msgid "" +"request to update group %(group)s, but config provided contains group " +"%(group_other)s instead" +msgstr "" +"demande de mise à jour du groupe %(group)s, mais la configuration fournie " +"contient le groupe %(group_other)s à la place" + +msgid "rescope a scoped token" +msgstr "Redéfinir la portée d'un jeton" + +#, python-format +msgid "text is multiple of 4, but pad \"%s\" occurs before 2nd to last char" +msgstr "" +"le texte est un multiple de 4, mais le remplissage \"%s\" se produit avant " +"l'avant-dernier caractère" + +#, python-format +msgid "text is multiple of 4, but pad \"%s\" occurs before non-pad last char" +msgstr "" +"le texte est un multiple de 4, mais le remplissage \"%s\" se produit avant " +"le dernier caractère qui n'est pas de remplissage" + +#, python-format +msgid "text is not a multiple of 4, but contains pad \"%s\"" +msgstr "" +"le texte n'est pas un multiple de 4, mais contient le remplissage \"%s\"" + +#, python-format +msgid "tls_cacertdir %s not found or is not a directory" +msgstr "tls_cacertdir %s introuvable ou n'est pas un répertoire" + +#, python-format +msgid "tls_cacertfile %s not found or is not a file" +msgstr "tls_cacertfile %s introuvable ou n'est pas un fichier" + +#, python-format +msgid "token reference must be a KeystoneToken type, got: %s" +msgstr "La référence de jeton doit être un type KeystoneToken, obtenu : %s" diff --git a/keystone-moon/keystone/locale/it/LC_MESSAGES/keystone.po b/keystone-moon/keystone/locale/it/LC_MESSAGES/keystone.po new file mode 100644 index 00000000..085f3b34 --- /dev/null +++ b/keystone-moon/keystone/locale/it/LC_MESSAGES/keystone.po @@ -0,0 +1,1292 @@ +# Italian translations for keystone. +# Copyright (C) 2015 OpenStack Foundation +# This file is distributed under the same license as the keystone project. +# +# Translators: +# OpenStack Infra <zanata@openstack.org>, 2015. #zanata +# Tom Cocozzello <tjcocozz@us.ibm.com>, 2015. #zanata +msgid "" +msgstr "" +"Project-Id-Version: keystone 9.0.0.dev14\n" +"Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" +"POT-Creation-Date: 2015-09-24 06:09+0000\n" +"PO-Revision-Date: 2015-09-03 12:54+0000\n" +"Last-Translator: openstackjenkins <jenkins@openstack.org>\n" +"Language-Team: Italian (http://www.transifex.com/openstack/keystone/language/" +"it/)\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\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" + +#, python-format +msgid "%(detail)s" +msgstr "%(detail)s" + +#, python-format +msgid "" +"%(event)s is not a valid notification event, must be one of: %(actions)s" +msgstr "" +"%(event)s non è un evento di notifica valido, deve essere uno tra: " +"%(actions)s" + +#, python-format +msgid "%(host)s is not a trusted dashboard host" +msgstr "%(host)s non è un host di dashboard attendibile" + +#, python-format +msgid "%(message)s %(amendment)s" +msgstr "%(message)s %(amendment)s" + +#, python-format +msgid "" +"%(mod_name)s doesn't provide database migrations. The migration repository " +"path at %(path)s doesn't exist or isn't a directory." +msgstr "" +"%(mod_name)s non fornisce le migrazioni del database. Il percorso del " +"repository di migrazione in %(path)s non esiste o non è una directory." + +#, python-format +msgid "%(property_name)s cannot be less than %(min_length)s characters." +msgstr "%(property_name)s non può essere inferiore a %(min_length)s caratteri." + +#, python-format +msgid "%(property_name)s is not a %(display_expected_type)s" +msgstr "%(property_name)s non è un %(display_expected_type)s" + +#, python-format +msgid "%(property_name)s should not be greater than %(max_length)s characters." +msgstr "%(property_name)s non può essere superiore a %(max_length)s caratteri." + +#, python-format +msgid "%s cannot be empty." +msgstr "%s non può essere vuoto." + +#, python-format +msgid "%s extension does not exist." +msgstr "L'estensione %s non esiste." + +#, python-format +msgid "%s field is required and cannot be empty" +msgstr "Il campo %s è obbligatorio e non può essere vuoto" + +#, python-format +msgid "%s field(s) cannot be empty" +msgstr "i campi %s non possono essere vuoti" + +msgid "(Disable debug mode to suppress these details.)" +msgstr "(Disabilitare la modalità di debug per eliminare questi dettagli.)" + +msgid "--all option cannot be mixed with other options" +msgstr "--l'opzione all non può essere combinata con altre opzioni" + +msgid "A project-scoped token is required to produce a service catalog." +msgstr "" +"È necessario un token in ambito progetto per produrre un catalogo del " +"servizio." + +msgid "Access token is expired" +msgstr "Il token di accesso è scaduto" + +msgid "Access token not found" +msgstr "Token di accesso non trovato" + +msgid "Additional authentications steps required." +msgstr "Sono richiesti ulteriori passi per le autenticazioni." + +msgid "An unexpected error occurred when retrieving domain configs" +msgstr "" +"Si è verificato un errore non previsto durante il richiamo delle " +"configurazioni del dominio" + +#, python-format +msgid "An unexpected error occurred when trying to store %s" +msgstr "Si è verificato un errore quando si tenta di archiviare %s" + +msgid "An unexpected error prevented the server from fulfilling your request." +msgstr "" +"Si è verificato un errore non previsto che ha impedito al server di " +"soddisfare la richiesta." + +#, python-format +msgid "" +"An unexpected error prevented the server from fulfilling your request: " +"%(exception)s" +msgstr "" +"Si è verificato un errore imprevisto che impedisce al server di soddisfare " +"la richiesta: %(exception)s" + +msgid "An unhandled exception has occurred: Could not find metadata." +msgstr "" +"Si è verificata un'eccezione non gestita: impossibile trovare i metadati." + +msgid "At least one option must be provided" +msgstr "È necessario fornire almeno un'opzione" + +msgid "At least one option must be provided, use either --all or --domain-name" +msgstr "" +"È necessario fornire almeno un'opzione, utilizzare --all o --domain-name" + +msgid "At least one role should be specified." +msgstr "Specificare almeno un ruolo." + +msgid "Attempted to authenticate with an unsupported method." +msgstr "Tentativo di autenticazione con un metodo non supportato." + +msgid "" +"Attempting to use OS-FEDERATION token with V2 Identity Service, use V3 " +"Authentication" +msgstr "" +"Tentativo di utilizzare il token OS-FEDERATION con il servizio identità V2, " +"utilizzare l'autenticazione V3" + +msgid "Authentication plugin error." +msgstr "errore di autenticazione plugin." + +msgid "Cannot authorize a request token with a token issued via delegation." +msgstr "" +"Impossibile autorizzare un token di richiesta con un token emesso mediante " +"delega." + +#, python-format +msgid "Cannot change %(option_name)s %(attr)s" +msgstr "Impossibile modificare %(option_name)s %(attr)s" + +msgid "Cannot change Domain ID" +msgstr "Impossibile modificare l'ID dominio" + +msgid "Cannot change consumer secret" +msgstr "Impossibile modificare il segreto del consumer" + +msgid "Cannot change user ID" +msgstr "Impossibile modificare l'ID utente" + +msgid "Cannot change user name" +msgstr "Impossibile modificare il nome utente" + +#, python-format +msgid "Cannot create project with parent: %(project_id)s" +msgstr "Impossibile creare il progetto con l'elemento parent: %(project_id)s" + +#, python-format +msgid "Cannot duplicate name %s" +msgstr "Impossibile duplicare il nome %s" + +msgid "Cannot list request tokens with a token issued via delegation." +msgstr "" +"Impossibile elencare i token della richiesta con un token emesso mediante " +"delega." + +#, python-format +msgid "Cannot open certificate %(cert_file)s. Reason: %(reason)s" +msgstr "Impossibile aprire il certificato %(cert_file)s. Motivo: %(reason)s" + +#, python-format +msgid "Cannot remove role that has not been granted, %s" +msgstr "Impossibile rimuovere un ruolo che non è stato concesso, %s" + +msgid "" +"Cannot truncate a driver call without hints list as first parameter after " +"self " +msgstr "" +"Impossibile troncare una chiamata al driver senza hints list come primo " +"parametro dopo self " + +msgid "" +"Cannot use parents_as_list and parents_as_ids query params at the same time." +msgstr "" +"Impossibile utilizzare i parametri della query parents_as_list e " +"parents_as_ids contemporaneamente." + +msgid "" +"Cannot use subtree_as_list and subtree_as_ids query params at the same time." +msgstr "" +"Impossibile utilizzare i parametri della query subtree_as_list e " +"subtree_as_ids contemporaneamente." + +msgid "" +"Combining effective and group filter will always result in an empty list." +msgstr "" +"La combinazione del filtro operativo e di gruppo avrà sempre come risultato " +"un elenco vuoto." + +msgid "" +"Combining effective, domain and inherited filters will always result in an " +"empty list." +msgstr "" +"La combinazione di filtri operativi, di dominio ed ereditati avrà sempre " +"come risultato un elenco vuoto." + +#, python-format +msgid "Conflict occurred attempting to store %(type)s - %(details)s" +msgstr "" +"Si è verificato un conflitto nel tentativo di archiviare %(type)s - " +"%(details)s" + +#, python-format +msgid "Conflicting region IDs specified: \"%(url_id)s\" != \"%(ref_id)s\"" +msgstr "" +"Sono stati specificati ID regione in conflitto: \"%(url_id)s\" != " +"\"%(ref_id)s\"" + +msgid "Consumer not found" +msgstr "Consumer non trovato" + +#, python-format +msgid "" +"Could not change immutable attribute(s) '%(attributes)s' in target %(target)s" +msgstr "" +"Impossibile modificare gli attributi non modificabili '%(attributes)s' nella " +"destinazione %(target)s" + +#, python-format +msgid "" +"Could not find %(group_or_option)s in domain configuration for domain " +"%(domain_id)s" +msgstr "" +"Impossibile trovare %(group_or_option)s nella configurazione del dominio per " +"il dominio %(domain_id)s" + +#, python-format +msgid "Could not find Endpoint Group: %(endpoint_group_id)s" +msgstr "Impossibile trovare il gruppo di endpoint: %(endpoint_group_id)s" + +msgid "Could not find Identity Provider identifier in environment" +msgstr "" +"Impossibile trovare l'identificativo del provider identità nell'ambiente" + +#, python-format +msgid "Could not find Identity Provider: %(idp_id)s" +msgstr "Impossibile trovare il provider identità: %(idp_id)s" + +#, python-format +msgid "Could not find Service Provider: %(sp_id)s" +msgstr "Impossibile trovare il provider del servizio: %(sp_id)s" + +#, python-format +msgid "Could not find credential: %(credential_id)s" +msgstr "Impossibile trovare la credenziale: %(credential_id)s" + +#, python-format +msgid "Could not find domain: %(domain_id)s" +msgstr "Impossibile trovare il dominio: %(domain_id)s" + +#, python-format +msgid "Could not find endpoint: %(endpoint_id)s" +msgstr "Impossibile trovare l'endpoint: %(endpoint_id)s" + +#, python-format +msgid "" +"Could not find federated protocol %(protocol_id)s for Identity Provider: " +"%(idp_id)s" +msgstr "" +"Impossibile trovare il protocollo federato %(protocol_id)s per il provider " +"identità: %(idp_id)s" + +#, python-format +msgid "Could not find group: %(group_id)s" +msgstr "Impossibile trovare il gruppo: %(group_id)s" + +#, python-format +msgid "Could not find mapping: %(mapping_id)s" +msgstr "Impossibile trovare l'associazione: %(mapping_id)s" + +msgid "Could not find policy association" +msgstr "Impossibile trovare l'associazione della politica" + +#, python-format +msgid "Could not find policy: %(policy_id)s" +msgstr "Impossibile trovare la politica: %(policy_id)s" + +#, python-format +msgid "Could not find project: %(project_id)s" +msgstr "Impossibile trovare il progetto: %(project_id)s" + +#, python-format +msgid "Could not find region: %(region_id)s" +msgstr "Impossibile trovare la regione: %(region_id)s" + +msgid "Could not find role" +msgstr "Impossibile trovare il ruolo" + +#, python-format +msgid "" +"Could not find role assignment with role: %(role_id)s, user or group: " +"%(actor_id)s, project or domain: %(target_id)s" +msgstr "" +"Impossibile trovare l'assegnazione ruolo con il ruolo: %(role_id)s, utente o " +"gruppo: %(actor_id)s, progetto o dominio: %(target_id)s" + +#, python-format +msgid "Could not find role: %(role_id)s" +msgstr "Impossibile trovare il ruolo: %(role_id)s" + +#, python-format +msgid "Could not find service: %(service_id)s" +msgstr "Impossibile trovare il servizio: %(service_id)s" + +#, python-format +msgid "Could not find token: %(token_id)s" +msgstr "Impossibile trovare il token: %(token_id)s" + +#, python-format +msgid "Could not find trust: %(trust_id)s" +msgstr "Impossibile trovare il trust: %(trust_id)s" + +#, python-format +msgid "Could not find user: %(user_id)s" +msgstr "Impossibile trovare l'utente: %(user_id)s" + +#, python-format +msgid "Could not find version: %(version)s" +msgstr "Impossibile trovare la versione: %(version)s" + +#, python-format +msgid "Could not find: %(target)s" +msgstr "Impossibile trovare: %(target)s" + +msgid "Could not validate the access token" +msgstr "Impossibile convalidare il token di accesso" + +msgid "Credential belongs to another user" +msgstr "La credenziale appartiene ad un altro utente" + +#, python-format +msgid "Database at /domains/%s/config" +msgstr "Database presso /domains/%s/config" + +msgid "" +"Disabling an entity where the 'enable' attribute is ignored by configuration." +msgstr "" +"Disabilitazione di un'entità in cui l'attributo 'enable' è ignorato dalla " +"configurazione." + +#, python-format +msgid "Domain (%s)" +msgstr "Dominio (%s)" + +#, python-format +msgid "Domain cannot be named %s" +msgstr "Il dominio non può essere denominato %s" + +#, python-format +msgid "Domain cannot have ID %s" +msgstr "Il dominio non può avere l'ID %s" + +#, python-format +msgid "Domain is disabled: %s" +msgstr "Il dominio è disabilitato: %s" + +msgid "Domain metadata not supported by LDAP" +msgstr "I metadati del dominio non sono supportati da LDAP" + +msgid "Domain scoped token is not supported" +msgstr "L'ambito del dominio token non è supportato" + +#, python-format +msgid "" +"Domain: %(domain)s already has a configuration defined - ignoring file: " +"%(file)s." +msgstr "" +"Il dominio: %(domain)s dispone già di una configurazione definita - si sta " +"ignorando il file: %(file)s." + +msgid "Domains are read-only against LDAP" +msgstr "I domini sono di sola lettura rispetto a LDAP" + +msgid "Duplicate Entry" +msgstr "Duplica voce" + +#, python-format +msgid "Duplicate ID, %s." +msgstr "ID duplicato, %s." + +#, python-format +msgid "Duplicate name, %s." +msgstr "Nome duplicato, %s." + +msgid "Enabled field must be a boolean" +msgstr "Il campo Abilitato deve essere un valore booleano" + +msgid "Enabled field should be a boolean" +msgstr "Il campo Abilitato deve essere un valore booleano" + +#, python-format +msgid "Endpoint %(endpoint_id)s not found in project %(project_id)s" +msgstr "Endpoint %(endpoint_id)s non trovato nel progetto %(project_id)s" + +msgid "Endpoint Group Project Association not found" +msgstr "Associazione al progetto del gruppo di endpoint non trovata" + +msgid "Ensure configuration option idp_entity_id is set." +msgstr "" +"Accertarsi che l'opzione di configurazione idp_entity_id sia impostata." + +msgid "Ensure configuration option idp_sso_endpoint is set." +msgstr "" +"Accertarsi che l'opzione di configurazione idp_sso_endpoint sia impostata." + +#, python-format +msgid "" +"Error parsing configuration file for domain: %(domain)s, file: %(file)s." +msgstr "" +"Errore durante l'analisi del file di configurazione per il dominio: " +"%(domain)s, file: %(file)s." + +#, python-format +msgid "Error while reading metadata file, %(reason)s" +msgstr "Errore durante le lettura del file di metadati, %(reason)s" + +#, python-format +msgid "Expected dict or list: %s" +msgstr "Previsto dict o list: %s" + +msgid "" +"Expected signing certificates are not available on the server. Please check " +"Keystone configuration." +msgstr "" +"I certificati di firma previsti non sono disponibili sul server. Controllare " +"la configurazione Keystone." + +#, python-format +msgid "" +"Expecting to find %(attribute)s in %(target)s - the server could not comply " +"with the request since it is either malformed or otherwise incorrect. The " +"client is assumed to be in error." +msgstr "" +"previsto di trovare %(attribute)s in %(target)s - il server non è in grado " +"di soddisfare la richiesta perché non è valido o non è corretto. Si ritiene " +"che il client sia in errore." + +#, python-format +msgid "Failed to start the %(name)s server" +msgstr "Impossibile avviare il server %(name)s" + +msgid "Failed to validate token" +msgstr "Impossibile convalidare il token" + +msgid "Federation token is expired" +msgstr "Il token comune è scaduto" + +#, python-format +msgid "" +"Field \"remaining_uses\" is set to %(value)s while it must not be set in " +"order to redelegate a trust" +msgstr "" +"Il campo \"remaining_uses\" è impostato su %(value)s mentre non deve essere " +"impostato per assegnare una nuova delega ad un trust" + +msgid "Found invalid token: scoped to both project and domain." +msgstr "trovato token non valido: in ambito sia di progetto che di dominio." + +#, python-format +msgid "Group %(group)s is not supported for domain specific configurations" +msgstr "" +"Il gruppo %(group)s non è supportato per le configurazioni specifiche del " +"dominio" + +#, python-format +msgid "" +"Group %(group_id)s returned by mapping %(mapping_id)s was not found in the " +"backend." +msgstr "" +"Il gruppo %(group_id)s restituito dall'associazione %(mapping_id)s non è " +"stato trovato nel backend." + +#, python-format +msgid "" +"Group membership across backend boundaries is not allowed, group in question " +"is %(group_id)s, user is %(user_id)s" +msgstr "" +"L'appartenenza al gruppo tra i limiti di backend non è consentita, il gruppo " +"in questione è %(group_id)s, l'utente è %(user_id)s" + +#, python-format +msgid "ID attribute %(id_attr)s not found in LDAP object %(dn)s" +msgstr "Attributo ID %(id_attr)s non trovato nell'oggetto LDAP %(dn)s" + +#, python-format +msgid "Identity Provider %(idp)s is disabled" +msgstr "Il provider identità %(idp)s è disabilitato" + +msgid "" +"Incoming identity provider identifier not included among the accepted " +"identifiers." +msgstr "" +"L'identificativo del provider identità in entrata non è incluso tra gli " +"identificativi accettati." + +#, python-format +msgid "Invalid LDAP TLS certs option: %(option)s. Choose one of: %(options)s" +msgstr "" +"Opzione certificazioni (certs) LDAP TLS non valida: %(option)s. Scegliere " +"una delle seguenti: %(options)s" + +#, python-format +msgid "Invalid LDAP TLS_AVAIL option: %s. TLS not available" +msgstr "Opzione LDAP TLS_AVAIL non valida: %s. TLS non disponibile" + +#, python-format +msgid "Invalid LDAP deref option: %(option)s. Choose one of: %(options)s" +msgstr "" +"Opzione deref LDAP non valida: %(option)s. Scegliere una tra: %(options)s" + +#, python-format +msgid "Invalid LDAP scope: %(scope)s. Choose one of: %(options)s" +msgstr "" +"Ambito LDAP non valido: %(scope)s. Scegliere uno dei seguenti: %(options)s" + +msgid "Invalid TLS / LDAPS combination" +msgstr "Combinazione TLS / LDAPS non valida" + +#, python-format +msgid "Invalid audit info data type: %(data)s (%(type)s)" +msgstr "" +"Tipo di dati delle informazioni di verifica non valido: %(data)s (%(type)s)" + +msgid "Invalid blob in credential" +msgstr "Blob non valido nella credenziale" + +#, python-format +msgid "" +"Invalid domain name: %(domain)s found in config file name: %(file)s - " +"ignoring this file." +msgstr "" +"Nome dominio non valido: %(domain)s trovato nel nome file di configurazione: " +"%(file)s - si sta ignorando questo file." + +#, python-format +msgid "Invalid domain specific configuration: %(reason)s" +msgstr "Configurazione specifica del dominio non valida: %(reason)s" + +#, python-format +msgid "Invalid input for field '%(path)s'. The value is '%(value)s'." +msgstr "Input non valido per il campo '%(path)s'. Il valore è '%(value)s'." + +msgid "Invalid limit value" +msgstr "Valore del limite non valido" + +#, python-format +msgid "" +"Invalid mix of entities for policy association - only Endpoint, Service or " +"Region+Service allowed. Request was - Endpoint: %(endpoint_id)s, Service: " +"%(service_id)s, Region: %(region_id)s" +msgstr "" +"combinazione di entità non valida per l'associazione della politica - È " +"consentito solo endpoint, servizio o regione+servizio. La richiesta era - " +"Endpoint: %(endpoint_id)s, Servizio: %(service_id)s, Regione: %(region_id)s" + +#, python-format +msgid "" +"Invalid rule: %(identity_value)s. Both 'groups' and 'domain' keywords must " +"be specified." +msgstr "" +"Regola non valida: %(identity_value)s. Entrambi le parole chiave 'groups' e " +"'domain' devono essere specificate." + +msgid "Invalid signature" +msgstr "Firma non valida" + +#, python-format +msgid "" +"Invalid ssl_cert_reqs value of %s, must be one of \"NONE\", \"OPTIONAL\", " +"\"REQUIRED\"" +msgstr "" +"Valore ssl_cert_reqs di %s non valido; deve essere uno tra \"NONE\", " +"\"OPTIONAL\", \"REQUIRED\"" + +msgid "Invalid user / password" +msgstr "Utente/password non validi" + +msgid "Invalid username or password" +msgstr "username o password non validi" + +#, python-format +msgid "KVS region %s is already configured. Cannot reconfigure." +msgstr "La regione KVS %s è già configurata. Impossibile riconfigurare." + +#, python-format +msgid "Key Value Store not configured: %s" +msgstr "KVS (Key Value Store) non configurato: %s" + +#, python-format +msgid "LDAP %s create" +msgstr "LDAP %s crea" + +#, python-format +msgid "LDAP %s delete" +msgstr "LDAP %s elimina" + +#, python-format +msgid "LDAP %s update" +msgstr "LDAP %s aggiorna" + +#, python-format +msgid "Lock Timeout occurred for key, %(target)s" +msgstr "Si è verificato un timeout di blocco per la chiave, %(target)s" + +#, python-format +msgid "Lock key must match target key: %(lock)s != %(target)s" +msgstr "" +"La chiave di blocco deve corrispondere alla chiave di destinazione: " +"%(lock)s != %(target)s" + +#, python-format +msgid "Malformed endpoint URL (%(endpoint)s), see ERROR log for details." +msgstr "" +"Url dell'endpoint non corretto (%(endpoint)s), consultare il log ERROR per " +"ulteriori dettagli." + +msgid "Marker could not be found" +msgstr "Impossibile trovare l'indicatore" + +#, python-format +msgid "Maximum lock attempts on %s occurred." +msgstr "È stato raggiunto il numero massimo di tentativi di blocco su %s." + +#, python-format +msgid "Member %(member)s is already a member of group %(group)s" +msgstr "Il membro %(member)s è già un membro del gruppo %(group)s" + +#, python-format +msgid "Method not callable: %s" +msgstr "Metodo non richiamabile: %s" + +msgid "Missing entity ID from environment" +msgstr "ID entità mancante dall'ambiente" + +msgid "" +"Modifying \"redelegation_count\" upon redelegation is forbidden. Omitting " +"this parameter is advised." +msgstr "" +"La modifica di \"redelegation_count\" dopo la riassegnazione della delega " +"non è consentita. Si consiglia di omettere questo parametro." + +msgid "Multiple domains are not supported" +msgstr "Non sono supportati più domini" + +msgid "Must be called within an active lock context." +msgstr "Deve essere richiamato all'interno di un contesto di blocco attivo." + +msgid "Must specify either domain or project" +msgstr "È necessario specificare il dominio o il progetto" + +msgid "Name field is required and cannot be empty" +msgstr "Il campo relativo al nome è obbligatorio e non può essere vuoto" + +msgid "" +"No Authorization headers found, cannot proceed with OAuth related calls, if " +"running under HTTPd or Apache, ensure WSGIPassAuthorization is set to On." +msgstr "" +"Nessuna intestazione di autorizzazione trovata, impossibile procedere con le " +"chiamate correlate a OAuth, se l'esecuzione avviene in ambito HTTPd o " +"Apache, assicurarsi che WSGIPassAuthorization sia impostato su Attivo." + +msgid "No authenticated user" +msgstr "Nessun utente autenticato" + +msgid "" +"No encryption keys found; run keystone-manage fernet_setup to bootstrap one." +msgstr "" +"Nessuna chiave di codifica trovata; eseguire keystone-manage fernet_setup " +"per eseguire un avvio." + +msgid "No options specified" +msgstr "Nessuna opzione specificata" + +#, python-format +msgid "No policy is associated with endpoint %(endpoint_id)s." +msgstr "Nessuna politica associata all'endpoint %(endpoint_id)s." + +#, python-format +msgid "No remaining uses for trust: %(trust_id)s" +msgstr "Nessun utilizzo residuo per trust: %(trust_id)s" + +msgid "Non-default domain is not supported" +msgstr "Il dominio non predefinito non è supportato" + +msgid "One of the trust agents is disabled or deleted" +msgstr "Uno degli agent trust è disabilitato o eliminato" + +#, python-format +msgid "" +"Option %(option)s found with no group specified while checking domain " +"configuration request" +msgstr "" +"L'opzione %(option)s è stato trovato senza alcun gruppo specificato durante " +"il controllo della richiesta di configurazione del dominio" + +#, python-format +msgid "" +"Option %(option)s in group %(group)s is not supported for domain specific " +"configurations" +msgstr "" +"L'opzione %(option)s nel gruppo %(group)s non è supportata per le " +"configurazioni specifiche del dominio" + +#, python-format +msgid "Project (%s)" +msgstr "Progetto (%s)" + +#, python-format +msgid "Project is disabled: %s" +msgstr "Il progetto è disabilitato: %s" + +msgid "Redelegation allowed for delegated by trust only" +msgstr "" +"Assegnazione di una nuova delega consentita solo per i delegati dal trust" + +#, python-format +msgid "" +"Remaining redelegation depth of %(redelegation_depth)d out of allowed range " +"of [0..%(max_count)d]" +msgstr "" +"profondità di riassegnazione della delega rimanente %(redelegation_depth)d " +"non compresa nell'intervallo consentito [0..%(max_count)d]" + +msgid "Request Token does not have an authorizing user id" +msgstr "" +"Il token della richiesta non dispone di un id utente per l'autorizzazione" + +#, python-format +msgid "" +"Request attribute %(attribute)s must be less than or equal to %(size)i. The " +"server could not comply with the request because the attribute size is " +"invalid (too large). The client is assumed to be in error." +msgstr "" +"L'attributo della richiesta %(attribute)s deve essere minore o uguale a " +"%(size)i. Il server non è riuscito a soddisfare la richiesta poiché la " +"dimensione dell'attributo non è valido (troppo grande). Si ritiene che il " +"client sia in errore." + +msgid "Request must have an origin query parameter" +msgstr "La richiesta deve avere un parametro della query di origine" + +msgid "Request token is expired" +msgstr "Il token della richiesta è scaduto" + +msgid "Request token not found" +msgstr "token della richiesta non trovata" + +msgid "Requested expiration time is more than redelegated trust can provide" +msgstr "" +"Il tempo di scadenza richiesto è maggiore di quello che può essere fornito " +"dal trust con delega riassegnata" + +#, python-format +msgid "" +"Requested redelegation depth of %(requested_count)d is greater than allowed " +"%(max_count)d" +msgstr "" +"La profondità di riassegnazione della delega richiesta %(requested_count)d è " +"maggiore del valore consentito %(max_count)d" + +#, python-format +msgid "Role %s not found" +msgstr "Ruolo %s non trovato" + +msgid "" +"Running keystone via eventlet is deprecated as of Kilo in favor of running " +"in a WSGI server (e.g. mod_wsgi). Support for keystone under eventlet will " +"be removed in the \"M\"-Release." +msgstr "" +"L'esecuzione del keystone via eventlet è obsoleta in Kilo, rispetto " +"all'esecuzione in un server WSGI (ad esempio mod_wsgi). Il supporto per il " +"keystone in eventlet verrà rimosso in \"M\"-Release." + +msgid "Scoping to both domain and project is not allowed" +msgstr "Il controllo sia del dominio che del progetto non è consentito" + +msgid "Scoping to both domain and trust is not allowed" +msgstr "Il controllo sia del dominio che di trust non è consentito" + +msgid "Scoping to both project and trust is not allowed" +msgstr "Il controllo sia delprogetto che di trust non è consentito" + +#, python-format +msgid "Service Provider %(sp)s is disabled" +msgstr "Il Provider del servizio %(sp)s è disabilitato" + +msgid "Some of requested roles are not in redelegated trust" +msgstr "" +"Alcuni dei ruoli richiesti non sono presenti nel trust con delega riassegnata" + +msgid "Specify a domain or project, not both" +msgstr "Specificare un dominio o un progetto, non entrambi" + +msgid "Specify a user or group, not both" +msgstr "Specificare un utente o un gruppo, non entrambi" + +msgid "Specify one of domain or project" +msgstr "Specificare uno valore di dominio o progetto" + +msgid "Specify one of user or group" +msgstr "Specificare un valore di utente o gruppo" + +#, python-format +msgid "" +"String length exceeded.The length of string '%(string)s' exceeded the limit " +"of column %(type)s(CHAR(%(length)d))." +msgstr "" +"È stata superata la lunghezza della stringa. La lunghezza della stringa " +"'%(string)s' ha superato il limite della colonna %(type)s(CHAR(%(length)d))." + +msgid "The --all option cannot be used with the --domain-name option" +msgstr "L'opzione --all non può essere utilizzata con l'opzione --domain-name" + +#, python-format +msgid "The Keystone configuration file %(config_file)s could not be found." +msgstr "" +"Impossibile trovare il file di configurazione Keystone %(config_file)s." + +#, python-format +msgid "" +"The Keystone domain-specific configuration has specified more than one SQL " +"driver (only one is permitted): %(source)s." +msgstr "" +"La configurazione specifica del dominio keystone ha specificato più di un " +"driver SQL (solo uno è consentito): %(source)s." + +msgid "The action you have requested has not been implemented." +msgstr "L'azione richiesta non è stata implementata." + +msgid "The authenticated user should match the trustor." +msgstr "L'utente autenticato deve corrispondere al ruolo trustor." + +msgid "" +"The certificates you requested are not available. It is likely that this " +"server does not use PKI tokens otherwise this is the result of " +"misconfiguration." +msgstr "" +"I certificati richiesti non sono disponibili. È probabile che questo server " +"non utilizzi i token PKI, altrimenti questo è il risultato di una " +"configurazione errata." + +#, python-format +msgid "" +"The password length must be less than or equal to %(size)i. The server could " +"not comply with the request because the password is invalid." +msgstr "" +"La lunghezza della password deve essere minore o uguale a %(size)i. Il " +"server non è in grado di soddisfare la richiesta perché la password non è " +"valida." + +msgid "The request you have made requires authentication." +msgstr "La richiesta che è stata fatta richiede l'autenticazione." + +msgid "The resource could not be found." +msgstr "Impossibile trovare la risorsa." + +msgid "" +"The revoke call must not have both domain_id and project_id. This is a bug " +"in the Keystone server. The current request is aborted." +msgstr "" +"La chiamata di revoca non deve avere entrambi domain_id e project_id. Questo " +"è un bug nel server Keystone. La richiesta corrente è stata interrotta." + +msgid "The service you have requested is no longer available on this server." +msgstr "Il servizio richiesto non è più disponibile su questo server." + +#, python-format +msgid "" +"The specified parent region %(parent_region_id)s would create a circular " +"region hierarchy." +msgstr "" +"La regione parent specificata %(parent_region_id)s crea una gerarchia di " +"regione circolare." + +#, python-format +msgid "" +"The value of group %(group)s specified in the config should be a dictionary " +"of options" +msgstr "" +"Il valore del gruppo %(group)s specificato nella configurazione deve essere " +"un dizionario di opzioni" + +msgid "There should not be any non-oauth parameters" +msgstr "Non deve essere presente nessun parametro non-oauth" + +#, python-format +msgid "This is not a recognized Fernet payload version: %s" +msgstr "Questa non è una versione di payload Fernet riconosciuta: %s" + +msgid "" +"This is not a v2.0 Fernet token. Use v3 for trust, domain, or federated " +"tokens." +msgstr "" +"Questo non è un token Fernet v2.0. Utilizzare v3 per token attendibili, di " +"dominio o federati." + +msgid "" +"Timestamp not in expected format. The server could not comply with the " +"request since it is either malformed or otherwise incorrect. The client is " +"assumed to be in error." +msgstr "" +"Data/ora non nel formato previsto. Il server non è riuscito a rispettare la " +"richiesta perché è in formato errato o non corretta. Il client viene " +"considerato in errore." + +#, python-format +msgid "" +"To get a more detailed information on this error, re-run this command for " +"the specific domain, i.e.: keystone-manage domain_config_upload --domain-" +"name %s" +msgstr "" +"Per ottenere informazioni più dettagliate su questo errore, eseguire di " +"nuovo questo comando per il dominio specificato, ad esempio: keystone-manage " +"domain_config_upload --domain-name %s" + +msgid "Token belongs to another user" +msgstr "Il token appartiene ad un altro utente" + +msgid "Token does not belong to specified tenant." +msgstr "Il token non appartiene al tenant specificato." + +msgid "Trustee has no delegated roles." +msgstr "Trustee non ha ruoli delegati." + +msgid "Trustor is disabled." +msgstr "Trustor è disabilitato." + +#, python-format +msgid "" +"Trying to update group %(group)s, so that, and only that, group must be " +"specified in the config" +msgstr "" +"Tentativo di aggiornare il gruppo %(group)s, pertanto, solo quel gruppo deve " +"essere specificato nella configurazione" + +#, python-format +msgid "" +"Trying to update option %(option)s in group %(group)s, but config provided " +"contains option %(option_other)s instead" +msgstr "" +"Tentativo di aggiornare l'opzione %(option)s nel gruppo %(group)s, ma la " +"configurazione fornita contiene l'opzione %(option_other)s" + +#, python-format +msgid "" +"Trying to update option %(option)s in group %(group)s, so that, and only " +"that, option must be specified in the config" +msgstr "" +"Tentativo di aggiornare l'opzione %(option)s nel gruppo %(group)s, pertanto, " +"solo quell'opzione deve essere specificata nella configurazione" + +msgid "" +"Unable to access the keystone database, please check it is configured " +"correctly." +msgstr "" +"Impossibile accedere al database del keystone, controllare se è configurato " +"correttamente." + +#, python-format +msgid "Unable to consume trust %(trust_id)s, unable to acquire lock." +msgstr "" +"Impossibile utilizzare trust %(trust_id)s, impossibile acquisire il blocco." + +#, python-format +msgid "" +"Unable to delete region %(region_id)s because it or its child regions have " +"associated endpoints." +msgstr "" +"Impossibile eliminare la regione %(region_id)s perché la regione o le " +"relative regioni child hanno degli endpoint associati." + +#, python-format +msgid "Unable to find valid groups while using mapping %(mapping_id)s" +msgstr "" +"Impossibile trovare i gruppi validi durante l'utilizzo dell'associazione " +"%(mapping_id)s" + +#, python-format +msgid "" +"Unable to get a connection from pool id %(id)s after %(seconds)s seconds." +msgstr "" +"Impossibile ottenere una connessione dall'ID pool %(id)s dopo %(seconds)s " +"secondi." + +#, python-format +msgid "Unable to locate domain config directory: %s" +msgstr "Impossibile individuare la directory config del dominio: %s" + +#, python-format +msgid "Unable to lookup user %s" +msgstr "Impossibile eseguire la ricerca dell'utente %s" + +#, python-format +msgid "" +"Unable to reconcile identity attribute %(attribute)s as it has conflicting " +"values %(new)s and %(old)s" +msgstr "" +"Impossibile riconciliare l'attributo identity %(attribute)s poiché ha " +"valori in conflitto tra i %(new)s e i %(old)s" + +#, python-format +msgid "" +"Unable to sign SAML assertion. It is likely that this server does not have " +"xmlsec1 installed, or this is the result of misconfiguration. Reason " +"%(reason)s" +msgstr "" +"Impossibile firmare l'asserzione SAML. Probabilmente questo server non " +"dispone di xmlsec1 installato o è il risultato di una configurazione " +"sbagliata. Motivo %(reason)s" + +msgid "Unable to sign token." +msgstr "Impossibile firmare il token." + +#, python-format +msgid "Unexpected assignment type encountered, %s" +msgstr "È stato rilevato un tipo di assegnazione non previsto, %s" + +#, python-format +msgid "" +"Unexpected combination of grant attributes - User: %(user_id)s, Group: " +"%(group_id)s, Project: %(project_id)s, Domain: %(domain_id)s" +msgstr "" +"Combinazione non prevista degli attributi di autorizzazione - Utente: " +"%(user_id)s, Gruppo: %(group_id)s, Progetto: %(project_id)s, Dominio: " +"%(domain_id)s" + +#, python-format +msgid "Unexpected status requested for JSON Home response, %s" +msgstr "Stato non previsto richiesto per la risposta JSON Home, %s" + +msgid "Unknown Target" +msgstr "Destinazione sconosciuta" + +#, python-format +msgid "Unknown domain '%(name)s' specified by --domain-name" +msgstr "Dominio sconosciuto '%(name)s' specificato da --domain-name" + +#, python-format +msgid "Unknown token version %s" +msgstr "Versione di token sconosciuta %s" + +#, python-format +msgid "Unregistered dependency: %(name)s for %(targets)s" +msgstr "Dipendenza non registrata: %(name)s per %(targets)s" + +msgid "Update of `parent_id` is not allowed." +msgstr "Aggiornamento di `parent_id` non consentito." + +msgid "Use a project scoped token when attempting to create a SAML assertion" +msgstr "" +"Utilizzare un token nell'ambito del progetto quando si tenta di creare " +"un'asserzione SAML" + +#, python-format +msgid "User %(u_id)s is unauthorized for tenant %(t_id)s" +msgstr "L'utente %(u_id)s non è autorizzato per il tenant %(t_id)s" + +#, python-format +msgid "User %(user_id)s already has role %(role_id)s in tenant %(tenant_id)s" +msgstr "" +"L'utente %(user_id)s ha già un ruolo %(role_id)s nel conduttore (tenant) " +"%(tenant_id)s" + +#, python-format +msgid "User %(user_id)s has no access to domain %(domain_id)s" +msgstr "L'utente %(user_id)s non ha accesso al dominio %(domain_id)s" + +#, python-format +msgid "User %(user_id)s has no access to project %(project_id)s" +msgstr "L'utente %(user_id)s non ha accesso al progetto %(project_id)s" + +#, python-format +msgid "User %(user_id)s is already a member of group %(group_id)s" +msgstr "L'utente %(user_id)s è già membro del gruppo %(group_id)s" + +#, python-format +msgid "User '%(user_id)s' not found in group '%(group_id)s'" +msgstr "L'utente '%(user_id)s' non è stato trovato nel gruppo '%(group_id)s'" + +msgid "User IDs do not match" +msgstr "Gli ID utente non corrispondono" + +#, python-format +msgid "User is disabled: %s" +msgstr "L'utente è disabilitato: %s" + +msgid "User is not a member of the requested project" +msgstr "L'utente non è un membro del progetto richiesto" + +msgid "User is not a trustee." +msgstr "L'utente non è un amministratore." + +msgid "User not found" +msgstr "Utente non trovato" + +#, python-format +msgid "User type %s not supported" +msgstr "Tipo utente %s non supportato" + +msgid "You are not authorized to perform the requested action." +msgstr "Non si possiede l'autorizzazione per eseguire l'operazione richiesta." + +#, python-format +msgid "You are not authorized to perform the requested action: %(action)s" +msgstr "L'utente non è autorizzato ad eseguire l'azione richiesta: %(action)s" + +msgid "`key_mangler` functions must be callable." +msgstr "Le funzioni `key_mangler` devono essere disponibili per la chiamata." + +msgid "`key_mangler` option must be a function reference" +msgstr "L'opzione `key_mangler` deve essere un riferimento funzione" + +msgid "any options" +msgstr "qualsiasi opzione" + +msgid "auth_type is not Negotiate" +msgstr "auth_type non è Negotiate" + +msgid "authorizing user does not have role required" +msgstr "l'utente per l'autorizzazione non dispone del ruolo richiesto" + +msgid "cache_collection name is required" +msgstr "Il nome cache_collection è obbligatorio" + +#, python-format +msgid "cannot create a project in a branch containing a disabled project: %s" +msgstr "" +"impossibile creare un progetto in un ramo che contiene un progetto " +"disabilitato: %s" + +msgid "cannot create a project within a different domain than its parents." +msgstr "" +"impossibile creare un progetto all'interno di un dominio diverso da quello " +"dei relativi elementi parent." + +msgid "cannot delete a domain that is enabled, please disable it first." +msgstr "" +"impossibile eliminare un dominio abilitato; è necessario prima disabilitarlo." + +#, python-format +msgid "cannot delete the project %s since it is not a leaf in the hierarchy." +msgstr "" +"impossibile eliminare il progetto %s perché non è una foglia nella gerarchia." + +#, python-format +msgid "cannot disable project %s since its subtree contains enabled projects" +msgstr "" +"impossibile disabilitare il progetto %s perché la relativa struttura ad " +"albero secondaria contiene progetti abilitati" + +#, python-format +msgid "cannot enable project %s since it has disabled parents" +msgstr "" +"impossibile abilitare il progetto %s perché dispone di elementi parent " +"disabilitati" + +msgid "database db_name is required" +msgstr "Il database db_name è obbligatorio" + +msgid "db_hosts value is required" +msgstr "Il valore db_hosts è obbligatorio" + +msgid "delete the default domain" +msgstr "eliminare il dominio predefinito" + +#, python-format +msgid "group %(group)s" +msgstr "gruppo %(group)s" + +msgid "" +"idp_contact_type must be one of: [technical, other, support, administrative " +"or billing." +msgstr "" +"idp_contact_type deve essere uno tra: [tecnico, altro, supporto, " +"amministrativo o di fatturazione." + +msgid "integer value expected for mongo_ttl_seconds" +msgstr "valore intero previsto per mongo_ttl_seconds" + +msgid "integer value expected for w (write concern attribute)" +msgstr "valore intero previsto per w (attributo di scrittura)" + +#, python-format +msgid "invalid date format %s" +msgstr "formato data non valido %s" + +#, python-format +msgid "max hierarchy depth reached for %s branch." +msgstr "profondità massima della gerarchia raggiunta per il ramo %s." + +msgid "no ssl support available" +msgstr "nessun supporto ssl disponibile" + +#, python-format +msgid "option %(option)s in group %(group)s" +msgstr "opzione %(option)s nel gruppo %(group)s" + +msgid "pad must be single character" +msgstr "il riempimento deve essere un carattere singolo" + +msgid "padded base64url text must be multiple of 4 characters" +msgstr "il testo base64url con riempimento deve essere multiplo di 4 caratteri" + +msgid "provided consumer key does not match stored consumer key" +msgstr "" +"La chiave consumer fornita non corrisponde alla chiave consumer memorizzata" + +msgid "provided request key does not match stored request key" +msgstr "" +"La chiave della richiesta fornita non corrisponde alla chiave della " +"richiesta memorizzata" + +msgid "provided verifier does not match stored verifier" +msgstr "il verificatore fornito non corrisponde al verificatore memorizzato" + +msgid "region not type dogpile.cache.CacheRegion" +msgstr "regione non tipo dogpile.cache.CacheRegion" + +msgid "remaining_uses must be a positive integer or null." +msgstr "remaining_uses deve essere un numero intero positivo o nullo." + +msgid "remaining_uses must not be set if redelegation is allowed" +msgstr "" +"remaining_uses non deve essere impostato se è consentita la riassegnazione " +"della delega" + +msgid "replicaset_name required when use_replica is True" +msgstr "replicaset_name è obbligatorio quando use_replica è True" + +#, python-format +msgid "" +"request to update group %(group)s, but config provided contains group " +"%(group_other)s instead" +msgstr "" +"Richiesta di aggiornamento del gruppo %(group)s, ma la configurazione " +"fornita contiene il gruppo %(group_other)s" + +msgid "rescope a scoped token" +msgstr "riassegna ambito a token con ambito" + +#, python-format +msgid "text is multiple of 4, but pad \"%s\" occurs before 2nd to last char" +msgstr "" +"il testo è multiplo di 4, ma il riempimento \"%s\" si verifica prima del " +"penultimo carattere" + +#, python-format +msgid "text is multiple of 4, but pad \"%s\" occurs before non-pad last char" +msgstr "" +"il testo è multiplo di 4, ma il riempimento \"%s\" si verifica prima " +"dell'ultimo carattere non di riempimento" + +#, python-format +msgid "text is not a multiple of 4, but contains pad \"%s\"" +msgstr "il testo non è un multiplo di 4, ma contiene il riempimento \"%s\"" + +#, python-format +msgid "tls_cacertdir %s not found or is not a directory" +msgstr "Impossibile trovare tls_cacertdir %s o non è una directory" + +#, python-format +msgid "tls_cacertfile %s not found or is not a file" +msgstr "Impossibile trovare tls_cacertfile %s o non è un file" + +#, python-format +msgid "token reference must be a KeystoneToken type, got: %s" +msgstr "" +"il riferimento al token deve essere un tipo KeystoneToken, ottenuto: %s" diff --git a/keystone-moon/keystone/locale/ja/LC_MESSAGES/keystone.po b/keystone-moon/keystone/locale/ja/LC_MESSAGES/keystone.po new file mode 100644 index 00000000..639a2438 --- /dev/null +++ b/keystone-moon/keystone/locale/ja/LC_MESSAGES/keystone.po @@ -0,0 +1,1254 @@ +# Japanese translations for keystone. +# Copyright (C) 2015 OpenStack Foundation +# This file is distributed under the same license as the keystone project. +# +# Translators: +# Tomoyuki KATO <tomo@dream.daynight.jp>, 2012-2013 +# OpenStack Infra <zanata@openstack.org>, 2015. #zanata +# Tom Cocozzello <tjcocozz@us.ibm.com>, 2015. #zanata +msgid "" +msgstr "" +"Project-Id-Version: keystone 9.0.0.dev14\n" +"Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" +"POT-Creation-Date: 2015-09-24 06:09+0000\n" +"PO-Revision-Date: 2015-09-03 12:54+0000\n" +"Last-Translator: openstackjenkins <jenkins@openstack.org>\n" +"Language-Team: Japanese (http://www.transifex.com/openstack/keystone/" +"language/ja/)\n" +"Plural-Forms: nplurals=1; plural=0\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" + +#, python-format +msgid "%(detail)s" +msgstr "%(detail)s" + +#, python-format +msgid "" +"%(event)s is not a valid notification event, must be one of: %(actions)s" +msgstr "" +"%(event)s は有効な通知イベントではありません。%(actions)s のいずれかでなけれ" +"ばなりません。" + +#, python-format +msgid "%(host)s is not a trusted dashboard host" +msgstr "%(host)s はトラステッド・ダッシュボード・ホストではありません" + +#, python-format +msgid "%(message)s %(amendment)s" +msgstr "%(message)s %(amendment)s" + +#, python-format +msgid "" +"%(mod_name)s doesn't provide database migrations. The migration repository " +"path at %(path)s doesn't exist or isn't a directory." +msgstr "" +"%(mod_name)s はデータベース・マイグレーションを提供していません。%(path)s の" +"マイグレーション・リポジトリー・パスは存在しないか、またはディレクトリーでは" +"ありません。" + +#, python-format +msgid "%(property_name)s cannot be less than %(min_length)s characters." +msgstr "%(property_name)s は %(min_length)s 文字より小さくできません。" + +#, python-format +msgid "%(property_name)s is not a %(display_expected_type)s" +msgstr "%(property_name)s が %(display_expected_type)s ではありません。" + +#, python-format +msgid "%(property_name)s should not be greater than %(max_length)s characters." +msgstr "%(property_name)s は %(max_length)s 文字より大きくできません。" + +#, python-format +msgid "%s cannot be empty." +msgstr "%s は空にできません。" + +#, python-format +msgid "%s extension does not exist." +msgstr "%s 拡張が存在しません。" + +#, python-format +msgid "%s field is required and cannot be empty" +msgstr "%s フィールドは必須フィールドであるため、空にできません" + +#, python-format +msgid "%s field(s) cannot be empty" +msgstr "%s フィールドを空にすることはできません" + +msgid "(Disable debug mode to suppress these details.)" +msgstr "(これらの詳細を抑制するには、デバッグ・モードを無効にします。)" + +msgid "--all option cannot be mixed with other options" +msgstr "--all オプションを他のオプションと組み合わせて使用することはできません" + +msgid "A project-scoped token is required to produce a service catalog." +msgstr "" +"サービス・カタログを生成するには、プロジェクトにスコープ宣言されたトークンが" +"必要です。" + +msgid "Access token is expired" +msgstr "アクセス・トークンの有効期限が切れています" + +msgid "Access token not found" +msgstr "アクセス・トークンが見つかりません" + +msgid "Additional authentications steps required." +msgstr "追加認証手順が必要です。" + +msgid "An unexpected error occurred when retrieving domain configs" +msgstr "ドメイン構成の取得中に予期しないエラーが発生しました" + +#, python-format +msgid "An unexpected error occurred when trying to store %s" +msgstr "%s を保管しようと試みているときに、予期しないエラーが発生しました" + +msgid "An unexpected error prevented the server from fulfilling your request." +msgstr "予期しないエラーが発生したため、サーバーは要求を満たすことが" + +#, python-format +msgid "" +"An unexpected error prevented the server from fulfilling your request: " +"%(exception)s" +msgstr "" +"予期しないエラーが発生したため、サーバーは要求を満たすことができませんでし" +"た: %(exception)s" + +msgid "An unhandled exception has occurred: Could not find metadata." +msgstr "未処理例外が発生しました。メタデータが見つかりませんでした。" + +msgid "At least one option must be provided" +msgstr "オプションを少なくとも 1 つは指定する必要があります" + +msgid "At least one option must be provided, use either --all or --domain-name" +msgstr "" +"少なくとも 1 つのオプションを指定する必要があります。--all または --domain-" +"name を使用してください" + +msgid "At least one role should be specified." +msgstr "少なくとも 1 つの役割を指定する必要があります。" + +msgid "Attempted to authenticate with an unsupported method." +msgstr "サポートされていないメソッドを使用して認証を行おうとしました。" + +msgid "" +"Attempting to use OS-FEDERATION token with V2 Identity Service, use V3 " +"Authentication" +msgstr "" +"V2 Identity Service で OS-FEDERATION トークンを使用しようとしています。V3 認" +"証を使用してください" + +msgid "Authentication plugin error." +msgstr "認証プラグイン・エラー。" + +msgid "Cannot authorize a request token with a token issued via delegation." +msgstr "" +"委任によって発行されたトークンを使用して要求トークンを許可することはできませ" +"ん。" + +#, python-format +msgid "Cannot change %(option_name)s %(attr)s" +msgstr "%(option_name)s %(attr)s を変更できません" + +msgid "Cannot change Domain ID" +msgstr "ドメイン ID を変更できません" + +msgid "Cannot change consumer secret" +msgstr "コンシューマーの秘密を変更できません" + +msgid "Cannot change user ID" +msgstr "ユーザー ID を変更できません" + +msgid "Cannot change user name" +msgstr "ユーザー名を変更できません" + +#, python-format +msgid "Cannot create project with parent: %(project_id)s" +msgstr "親を持つプロジェクトを作成できません: %(project_id)s" + +#, python-format +msgid "Cannot duplicate name %s" +msgstr "名前 %s は重複してはなりません" + +msgid "Cannot list request tokens with a token issued via delegation." +msgstr "" +"委任によって発行されたトークンを使用して要求トークンをリストすることはできま" +"せん。" + +#, python-format +msgid "Cannot open certificate %(cert_file)s. Reason: %(reason)s" +msgstr "証明書 %(cert_file)s を開くことができません。理由: %(reason)s" + +#, python-format +msgid "Cannot remove role that has not been granted, %s" +msgstr "許可されていないロールを削除できません、%s" + +msgid "" +"Cannot truncate a driver call without hints list as first parameter after " +"self " +msgstr "" +"セルフの後に最初のパラメーターとしてヒント・リストなしでドライバー呼び出しを" +"切り捨てることはできません" + +msgid "" +"Cannot use parents_as_list and parents_as_ids query params at the same time." +msgstr "" +"照会パラメーター parents_as_list と parents_as_ids を併用することはできませ" +"ん。" + +msgid "" +"Cannot use subtree_as_list and subtree_as_ids query params at the same time." +msgstr "" +"照会パラメーター subtree_as_list と subtree_as_ids を併用することはできませ" +"ん。" + +msgid "" +"Combining effective and group filter will always result in an empty list." +msgstr "" +"有効フィルターとグループ・フィルターの組み合わせは常に空のリストになります。" + +msgid "" +"Combining effective, domain and inherited filters will always result in an " +"empty list." +msgstr "" +"有効フィルター、ドメイン・フィルター、および継承フィルターの組み合わせは常に" +"空のリストになります。" + +#, python-format +msgid "Conflict occurred attempting to store %(type)s - %(details)s" +msgstr "%(type)s を保管するときに競合が発生しました - %(details)s" + +#, python-format +msgid "Conflicting region IDs specified: \"%(url_id)s\" != \"%(ref_id)s\"" +msgstr "矛盾する領域 ID が指定されました: \"%(url_id)s\" != \"%(ref_id)s\"" + +msgid "Consumer not found" +msgstr "コンシューマーが見つかりません" + +#, python-format +msgid "" +"Could not change immutable attribute(s) '%(attributes)s' in target %(target)s" +msgstr "" +"ターゲット %(target)s で不変の属性 '%(attributes)s' を変更できませんでした" + +#, python-format +msgid "" +"Could not find %(group_or_option)s in domain configuration for domain " +"%(domain_id)s" +msgstr "" +"%(group_or_option)s がドメイン %(domain_id)s のドメイン構成に見つかりませんで" +"した" + +#, python-format +msgid "Could not find Endpoint Group: %(endpoint_group_id)s" +msgstr "エンドポイント・グループが見つかりませんでした: %(endpoint_group_id)s" + +msgid "Could not find Identity Provider identifier in environment" +msgstr "Identity Provider ID が環境内に見つかりませんでした" + +#, python-format +msgid "Could not find Identity Provider: %(idp_id)s" +msgstr "ID プロバイダーが見つかりませんでした: %(idp_id)s" + +#, python-format +msgid "Could not find Service Provider: %(sp_id)s" +msgstr "サービス・プロバイダー %(sp_id)s が見つかりませんでした" + +#, python-format +msgid "Could not find credential: %(credential_id)s" +msgstr "資格情報が見つかりませんでした: %(credential_id)s" + +#, python-format +msgid "Could not find domain: %(domain_id)s" +msgstr "ドメインが見つかりませんでした: %(domain_id)s" + +#, python-format +msgid "Could not find endpoint: %(endpoint_id)s" +msgstr "エンドポイントが見つかりませんでした: %(endpoint_id)s" + +#, python-format +msgid "" +"Could not find federated protocol %(protocol_id)s for Identity Provider: " +"%(idp_id)s" +msgstr "" +"Identity Provider の統合プロトコル %(protocol_id)s が見つかりませんでした: " +"%(idp_id)s" + +#, python-format +msgid "Could not find group: %(group_id)s" +msgstr "グループが見つかりませんでした: %(group_id)s" + +#, python-format +msgid "Could not find mapping: %(mapping_id)s" +msgstr "マッピングが見つかりませんでした: %(mapping_id)s" + +msgid "Could not find policy association" +msgstr "ポリシー関連付けが見つかりませんでした" + +#, python-format +msgid "Could not find policy: %(policy_id)s" +msgstr "ポリシーが見つかりませんでした: %(policy_id)s" + +#, python-format +msgid "Could not find project: %(project_id)s" +msgstr "プロジェクトが見つかりませんでした: %(project_id)s" + +#, python-format +msgid "Could not find region: %(region_id)s" +msgstr "領域が見つかりませんでした: %(region_id)s" + +msgid "Could not find role" +msgstr "役割が見つかりませんでした" + +#, python-format +msgid "" +"Could not find role assignment with role: %(role_id)s, user or group: " +"%(actor_id)s, project or domain: %(target_id)s" +msgstr "" +"役割 %(role_id)s を持つ割り当てが見つかりませんでした。ユーザーまたはグルー" +"プ: %(actor_id)s、プロジェクトまたはドメイン: %(target_id)s" + +#, python-format +msgid "Could not find role: %(role_id)s" +msgstr "役割が見つかりませんでした: %(role_id)s" + +#, python-format +msgid "Could not find service: %(service_id)s" +msgstr "サービスが見つかりませんでした: %(service_id)s" + +#, python-format +msgid "Could not find token: %(token_id)s" +msgstr "トークンが見つかりませんでした: %(token_id)s" + +#, python-format +msgid "Could not find trust: %(trust_id)s" +msgstr "トラストが見つかりませんでした: %(trust_id)s" + +#, python-format +msgid "Could not find user: %(user_id)s" +msgstr "ユーザーが見つかりませんでした: %(user_id)s" + +#, python-format +msgid "Could not find version: %(version)s" +msgstr "バージョンが見つかりませんでした: %(version)s" + +#, python-format +msgid "Could not find: %(target)s" +msgstr "見つかりませんでした: %(target)s" + +msgid "Could not validate the access token" +msgstr "アクセス・トークンを検証できませんでした" + +msgid "Credential belongs to another user" +msgstr "資格情報が別のユーザーに属しています" + +#, python-format +msgid "Database at /domains/%s/config" +msgstr "/domains/%s/config のデータベース" + +msgid "" +"Disabling an entity where the 'enable' attribute is ignored by configuration." +msgstr "「enable」属性が構成によって無視されているエンティティーを無効化中。" + +#, python-format +msgid "Domain (%s)" +msgstr "ドメイン (%s)" + +#, python-format +msgid "Domain cannot be named %s" +msgstr "ドメインに %s という名前を付けることはできません" + +#, python-format +msgid "Domain cannot have ID %s" +msgstr "ドメインに %s という ID を付けることはできません" + +#, python-format +msgid "Domain is disabled: %s" +msgstr "ドメインが無効化されています: %s" + +msgid "Domain metadata not supported by LDAP" +msgstr "ドメイン・メタデータは LDAP ではサポートされません" + +msgid "Domain scoped token is not supported" +msgstr "ドメインをスコープにしたトークンはサポートされません" + +#, python-format +msgid "" +"Domain: %(domain)s already has a configuration defined - ignoring file: " +"%(file)s." +msgstr "" +"ドメイン %(domain)s には既に構成があります - ファイル %(file)s は無視されま" +"す。" + +msgid "Domains are read-only against LDAP" +msgstr "ドメインが LDAP に対して読み取り専用です" + +msgid "Duplicate Entry" +msgstr "重複する項目" + +#, python-format +msgid "Duplicate ID, %s." +msgstr "重複した ID、%s。" + +#, python-format +msgid "Duplicate name, %s." +msgstr "重複した名前、%s。" + +msgid "Enabled field must be a boolean" +msgstr "「有効」フィールドはブール値でなければなりません" + +msgid "Enabled field should be a boolean" +msgstr "「有効」フィールドはブール値でなければなりません" + +#, python-format +msgid "Endpoint %(endpoint_id)s not found in project %(project_id)s" +msgstr "" +"エンドポイント %(endpoint_id)s はプロジェクト %(project_id)s に見つかりません" + +msgid "Endpoint Group Project Association not found" +msgstr "エンドポイント・グループ・プロジェクト関連付けが見つかりません" + +msgid "Ensure configuration option idp_entity_id is set." +msgstr "構成オプション idp_entity_id が設定されていることを確認してください。" + +msgid "Ensure configuration option idp_sso_endpoint is set." +msgstr "" +"構成オプション idp_sso_endpoint が設定されていることを確認してください。" + +#, python-format +msgid "" +"Error parsing configuration file for domain: %(domain)s, file: %(file)s." +msgstr "" +"構成ファイルの構文解析エラー。ドメイン: %(domain)s、ファイル: %(file)s。" + +#, python-format +msgid "Error while reading metadata file, %(reason)s" +msgstr "メタデータ・ファイルの読み取り中にエラーが発生しました。%(reason)s" + +#, python-format +msgid "Expected dict or list: %s" +msgstr "期待される辞書または一覧: %s" + +msgid "" +"Expected signing certificates are not available on the server. Please check " +"Keystone configuration." +msgstr "" +"予期された署名証明書がサーバーにありません。Keystone の構成を確認してくださ" +"い。" + +#, python-format +msgid "" +"Expecting to find %(attribute)s in %(target)s - the server could not comply " +"with the request since it is either malformed or otherwise incorrect. The " +"client is assumed to be in error." +msgstr "" +"%(target)s で %(attribute)s が検出されることが予期されています。要求の形式が" +"誤っているか、または要求が正しくないため、サーバーは要求に従うことができませ" +"んでした。クライアントでエラーが発生していると見なされます。" + +#, python-format +msgid "Failed to start the %(name)s server" +msgstr "%(name)s サーバーの始動に失敗しました" + +msgid "Failed to validate token" +msgstr "トークンの検証に失敗しました" + +msgid "Federation token is expired" +msgstr "連合トークンの有効期限が切れています" + +#, python-format +msgid "" +"Field \"remaining_uses\" is set to %(value)s while it must not be set in " +"order to redelegate a trust" +msgstr "" +"フィールド「remaining_uses」が %(value)s に設定されていますが、トラストを再委" +"任するにはこのフィールドを設定してはなりません" + +msgid "Found invalid token: scoped to both project and domain." +msgstr "" +"無効なトークンが見つかりました: プロジェクトとドメインの両方にスコープ宣言さ" +"れています。" + +#, python-format +msgid "Group %(group)s is not supported for domain specific configurations" +msgstr "ドメイン固有の構成ではグループ %(group)s はサポートされません" + +#, python-format +msgid "" +"Group %(group_id)s returned by mapping %(mapping_id)s was not found in the " +"backend." +msgstr "" +"マッピング %(mapping_id)s によって返されたグループ %(group_id)s がバックエン" +"ドに見つかりませんでした。" + +#, python-format +msgid "" +"Group membership across backend boundaries is not allowed, group in question " +"is %(group_id)s, user is %(user_id)s" +msgstr "" +"バックエンド境界を越えるグループ・メンバーシップは許可されません。問題となっ" +"ているグループは %(group_id)s、ユーザーは %(user_id)s です" + +#, python-format +msgid "ID attribute %(id_attr)s not found in LDAP object %(dn)s" +msgstr "ID 属性 %(id_attr)s が LDAP オブジェクト %(dn)s に見つかりません" + +#, python-format +msgid "Identity Provider %(idp)s is disabled" +msgstr "ID プロバイダー %(idp)s は無効になっています" + +msgid "" +"Incoming identity provider identifier not included among the accepted " +"identifiers." +msgstr "着信 ID プロバイダー ID が受諾 ID に含まれていませんでした。" + +#, python-format +msgid "Invalid LDAP TLS certs option: %(option)s. Choose one of: %(options)s" +msgstr "" +"LDAP TLS 証明書オプション %(option)s が無効です。以下のいずれかを選択してくだ" +"さい: %(options)s" + +#, python-format +msgid "Invalid LDAP TLS_AVAIL option: %s. TLS not available" +msgstr "無効な LDAP TLS_AVAIL オプション: %s。TLS が利用できません。" + +#, python-format +msgid "Invalid LDAP deref option: %(option)s. Choose one of: %(options)s" +msgstr "" +"LDAP deref オプションが無効です: %(option)s。%(options)s のいずれかを選択して" +"ください" + +#, python-format +msgid "Invalid LDAP scope: %(scope)s. Choose one of: %(options)s" +msgstr "無効な LDAP 範囲: %(scope)s。次のどれかを選んでください: %(options)s" + +msgid "Invalid TLS / LDAPS combination" +msgstr "無効な TLS / LDAPS の組み合わせ" + +#, python-format +msgid "Invalid audit info data type: %(data)s (%(type)s)" +msgstr "無効な監査情報データ・タイプ: %(data)s (%(type)s)" + +msgid "Invalid blob in credential" +msgstr "資格情報内の blob が無効です" + +#, python-format +msgid "" +"Invalid domain name: %(domain)s found in config file name: %(file)s - " +"ignoring this file." +msgstr "" +"無効なドメイン・ネーム %(domain)s が構成ファイル名 %(file)s に見つかりました " +"- このファイルは無視されます。" + +#, python-format +msgid "Invalid domain specific configuration: %(reason)s" +msgstr "ドメイン固有の構成が無効です: %(reason)s" + +#, python-format +msgid "Invalid input for field '%(path)s'. The value is '%(value)s'." +msgstr "フィールド '%(path)s' の入力が無効です。値は '%(value)s' です。" + +msgid "Invalid limit value" +msgstr "制限値が無効です" + +#, python-format +msgid "" +"Invalid mix of entities for policy association - only Endpoint, Service or " +"Region+Service allowed. Request was - Endpoint: %(endpoint_id)s, Service: " +"%(service_id)s, Region: %(region_id)s" +msgstr "" +"ポリシー関連付けのエンティティーの組み合わせが無効です。エンドポイント、サー" +"ビス、または領域とサービスのみ許可されています。要求 - エンドポイント: " +"%(endpoint_id)s、サービス: %(service_id)s、領域: %(region_id)s" + +#, python-format +msgid "" +"Invalid rule: %(identity_value)s. Both 'groups' and 'domain' keywords must " +"be specified." +msgstr "" +"無効なルール: %(identity_value)s。「グループ」と「ドメイン」の両方のキーワー" +"ドを指定する必要があります。" + +msgid "Invalid signature" +msgstr "シグニチャーが無効です" + +#, python-format +msgid "" +"Invalid ssl_cert_reqs value of %s, must be one of \"NONE\", \"OPTIONAL\", " +"\"REQUIRED\"" +msgstr "" +"%s の ssl_cert_reqs 値が無効です。「NONE」、「OPTIONAL」、「REQUIRED」のいず" +"れかでなければなりません" + +msgid "Invalid user / password" +msgstr "ユーザー/パスワードが無効です" + +msgid "Invalid username or password" +msgstr "無効なユーザー名またはパスワード" + +#, python-format +msgid "KVS region %s is already configured. Cannot reconfigure." +msgstr "KVS 領域 %s は既に構成されています。再構成はできません。" + +#, python-format +msgid "Key Value Store not configured: %s" +msgstr "キー値ストアが構成されていません: %s" + +#, python-format +msgid "LDAP %s create" +msgstr "LDAP %s の作成" + +#, python-format +msgid "LDAP %s delete" +msgstr "LDAP %s の削除" + +#, python-format +msgid "LDAP %s update" +msgstr "LDAP %s の更新" + +#, python-format +msgid "Lock Timeout occurred for key, %(target)s" +msgstr "キー %(target)s についてロック・タイムアウトが発生しました" + +#, python-format +msgid "Lock key must match target key: %(lock)s != %(target)s" +msgstr "" +"ロック・キーがターゲット・キーと一致しなければなりません: %(lock)s != " +"%(target)s" + +#, python-format +msgid "Malformed endpoint URL (%(endpoint)s), see ERROR log for details." +msgstr "" +"エンドポイント URL (%(endpoint)s) の形式が正しくありません。詳しくはエラー・" +"ログを参照してください。" + +msgid "Marker could not be found" +msgstr "マーカーが見つかりませんでした" + +#, python-format +msgid "Maximum lock attempts on %s occurred." +msgstr "%s に対してロックが最大回数まで試みられました。" + +#, python-format +msgid "Member %(member)s is already a member of group %(group)s" +msgstr "メンバー %(member)s は既にグループ %(group)s のメンバーです" + +#, python-format +msgid "Method not callable: %s" +msgstr "メソッドが呼び出し可能ではありません: %s" + +msgid "Missing entity ID from environment" +msgstr "環境にエンティティー ID がありません" + +msgid "" +"Modifying \"redelegation_count\" upon redelegation is forbidden. Omitting " +"this parameter is advised." +msgstr "" +"再委任時の「redelegation_count」の変更は禁止されています。このパラメーターを" +"省略してください。" + +msgid "Multiple domains are not supported" +msgstr "複数のドメインはサポートされていません" + +msgid "Must be called within an active lock context." +msgstr "アクティブ・ロック・コンテキスト内で呼び出されなければなりません。" + +msgid "Must specify either domain or project" +msgstr "ドメインまたはプロジェクトのいずれかを指定する必要があります" + +msgid "Name field is required and cannot be empty" +msgstr "「名前」フィールドは必須フィールドであるため、空にできません" + +msgid "" +"No Authorization headers found, cannot proceed with OAuth related calls, if " +"running under HTTPd or Apache, ensure WSGIPassAuthorization is set to On." +msgstr "" +"許可ヘッダーが見つかりません。OAuth 関連呼び出しを続行できません。HTTPd また" +"は Apache の下で実行している場合は、WSGIPassAuthorization が On に設定されて" +"いることを確認してください。" + +msgid "No authenticated user" +msgstr "認証されていないユーザー" + +msgid "" +"No encryption keys found; run keystone-manage fernet_setup to bootstrap one." +msgstr "" +"暗号鍵が見つかりません。keystone 管理 fernet_setup を実行してブートストラップ" +"を行ってください。" + +msgid "No options specified" +msgstr "オプション指定なし" + +#, python-format +msgid "No policy is associated with endpoint %(endpoint_id)s." +msgstr "" +"エンドポイント %(endpoint_id)s に関連付けられているポリシーはありません。" + +#, python-format +msgid "No remaining uses for trust: %(trust_id)s" +msgstr "トラストには使用が残っていません: %(trust_id)s" + +msgid "Non-default domain is not supported" +msgstr "標準以外のドメインはサポートされません" + +msgid "One of the trust agents is disabled or deleted" +msgstr "トラスト・エージェントの 1 つが無効化または削除されています" + +#, python-format +msgid "" +"Option %(option)s found with no group specified while checking domain " +"configuration request" +msgstr "" +"ドメイン構成要求の検査中にオプション %(option)s にグループが指定されていない" +"ことが検出されました" + +#, python-format +msgid "" +"Option %(option)s in group %(group)s is not supported for domain specific " +"configurations" +msgstr "" +"ドメイン固有の構成ではグループ %(group)s 内のオプション %(option)s はサポート" +"されていません" + +#, python-format +msgid "Project (%s)" +msgstr "プロジェクト (%s)" + +#, python-format +msgid "Project is disabled: %s" +msgstr "プロジェクトが無効化されています: %s" + +msgid "Redelegation allowed for delegated by trust only" +msgstr "再委任はトラストによる委任にのみ許可されます" + +#, python-format +msgid "" +"Remaining redelegation depth of %(redelegation_depth)d out of allowed range " +"of [0..%(max_count)d]" +msgstr "" +"%(redelegation_depth)d の残りの再委任の深さが、許可された範囲 [0.." +"%(max_count)d] を超えています" + +msgid "Request Token does not have an authorizing user id" +msgstr "要求トークンに許可ユーザー ID が含まれていません" + +#, python-format +msgid "" +"Request attribute %(attribute)s must be less than or equal to %(size)i. The " +"server could not comply with the request because the attribute size is " +"invalid (too large). The client is assumed to be in error." +msgstr "" +"要求属性 %(attribute)s は %(size)i 以下でなければなりません。属性のサイズが無" +"効である (大きすぎる) ため、サーバーは要求に従うことができませんでした。クラ" +"イアントでエラーが発生していると見なされます。" + +msgid "Request must have an origin query parameter" +msgstr "要求には起点照会パラメーターが必要です" + +msgid "Request token is expired" +msgstr "要求トークンの有効期限が切れています" + +msgid "Request token not found" +msgstr "要求トークンが見つかりません" + +msgid "Requested expiration time is more than redelegated trust can provide" +msgstr "要求された有効期限は再委任されたトラストが提供可能な期間を超えています" + +#, python-format +msgid "" +"Requested redelegation depth of %(requested_count)d is greater than allowed " +"%(max_count)d" +msgstr "" +"%(requested_count)d の要求された再委任の深さが、許可された %(max_count)d を超" +"えています" + +#, python-format +msgid "Role %s not found" +msgstr "ロール %s が見つかりませんでした" + +msgid "" +"Running keystone via eventlet is deprecated as of Kilo in favor of running " +"in a WSGI server (e.g. mod_wsgi). Support for keystone under eventlet will " +"be removed in the \"M\"-Release." +msgstr "" +"eventlet を介した keystone の実行は Kilo 以降では推奨されておらず、WSGI サー" +"バー (mod_wsgi など) での実行が推奨されています。eventlet 下での keystone の" +"サポートは「M」-リリースで削除される予定です。" + +msgid "Scoping to both domain and project is not allowed" +msgstr "ドメインとプロジェクトの両方をスコープとすることは許可されません" + +msgid "Scoping to both domain and trust is not allowed" +msgstr "ドメインとトラストの両方をスコープとすることは許可されません" + +msgid "Scoping to both project and trust is not allowed" +msgstr "プロジェクトとトラストの両方をスコープとすることは許可されません" + +#, python-format +msgid "Service Provider %(sp)s is disabled" +msgstr "サービス・プロバイダー %(sp)s は無効になっています" + +msgid "Some of requested roles are not in redelegated trust" +msgstr "要求された役割の一部が再委任されたトラスト内にありません" + +msgid "Specify a domain or project, not both" +msgstr "ドメインかプロジェクトを指定してください。両方は指定しないでください" + +msgid "Specify a user or group, not both" +msgstr "ユーザーかグループを指定してください。両方は指定しないでください" + +msgid "Specify one of domain or project" +msgstr "ドメインまたはプロジェクトのいずれかを指定してください" + +msgid "Specify one of user or group" +msgstr "ユーザーまたはグループのいずれかを指定してください" + +#, python-format +msgid "" +"String length exceeded.The length of string '%(string)s' exceeded the limit " +"of column %(type)s(CHAR(%(length)d))." +msgstr "" +"ストリングの長さを超えました。ストリング %(string)s' の長さが列 " +"%(type)s(CHAR(%(length)d)) の制限を超えました。" + +msgid "The --all option cannot be used with the --domain-name option" +msgstr "--all オプションを --domain-name オプションと併用することはできません" + +#, python-format +msgid "The Keystone configuration file %(config_file)s could not be found." +msgstr "Keystone 構成ファイル %(config_file)s が見つかりませんでした。" + +#, python-format +msgid "" +"The Keystone domain-specific configuration has specified more than one SQL " +"driver (only one is permitted): %(source)s." +msgstr "" +"keystone ドメイン固有構成で複数の SQL ドライバーが指定されています (1 つのみ" +"許可): %(source)s。" + +msgid "The action you have requested has not been implemented." +msgstr "要求したアクションは実装されていません。" + +msgid "The authenticated user should match the trustor." +msgstr "認証ユーザーは委託者と一致している必要があります。" + +msgid "" +"The certificates you requested are not available. It is likely that this " +"server does not use PKI tokens otherwise this is the result of " +"misconfiguration." +msgstr "" +"要求された証明書がありません。このサーバーでは PKI トークンが使用されないか、" +"または結果的に構成が正しくないことが考えられます。 " + +#, python-format +msgid "" +"The password length must be less than or equal to %(size)i. The server could " +"not comply with the request because the password is invalid." +msgstr "" +"パスワードの長さは %(size)i 以下でなければなりません。パスワードが無効である" +"ため、サーバーは要求に従うことができませんでした。" + +msgid "The request you have made requires authentication." +msgstr "実行された要求には認証が必要です。" + +msgid "The resource could not be found." +msgstr "リソースが見つかりませんでした。" + +msgid "" +"The revoke call must not have both domain_id and project_id. This is a bug " +"in the Keystone server. The current request is aborted." +msgstr "" +"取り消し呼び出しに domain_id と project_id の両方を使用することはできません。" +"これは、Keystone サーバーにおけるバグです。現在の要求は打ち切られます。" + +msgid "The service you have requested is no longer available on this server." +msgstr "要求したサービスは現在このサーバーでは使用できません。" + +#, python-format +msgid "" +"The specified parent region %(parent_region_id)s would create a circular " +"region hierarchy." +msgstr "指定された親領域 %(parent_region_id)s は循環領域階層を作成します。" + +#, python-format +msgid "" +"The value of group %(group)s specified in the config should be a dictionary " +"of options" +msgstr "" +"構成で指定されたグループ %(group)s の値はオプションのディクショナリーにする必" +"要があります" + +msgid "There should not be any non-oauth parameters" +msgstr "非 oauth パラメーターが存在していてはなりません" + +#, python-format +msgid "This is not a recognized Fernet payload version: %s" +msgstr "これは認識された Fernet ペイロード・バージョンではありません: %s" + +msgid "" +"This is not a v2.0 Fernet token. Use v3 for trust, domain, or federated " +"tokens." +msgstr "" +"これは v2.0 Fernet トークンではありません。トラスト・トークン、ドメイン・トー" +"クン、または統合トークンには v3 を使用してください。" + +msgid "" +"Timestamp not in expected format. The server could not comply with the " +"request since it is either malformed or otherwise incorrect. The client is " +"assumed to be in error." +msgstr "" +"タイム・スタンプが、予期された形式になっていません。要求の形式が誤っている" +"か、または要求が正しくないため、サーバーは要求に応じることができませんでし" +"た。クライアントはエラーになっているとみなされます。" + +#, python-format +msgid "" +"To get a more detailed information on this error, re-run this command for " +"the specific domain, i.e.: keystone-manage domain_config_upload --domain-" +"name %s" +msgstr "" +"このエラーに関する詳細を得るには、次のように、特定ドメインに対してこのコマン" +"ドを再実行してください: keystone-manage domain_config_upload --domain-name %s" + +msgid "Token belongs to another user" +msgstr "トークンが別のユーザーに属しています" + +msgid "Token does not belong to specified tenant." +msgstr "トークンが指定されたテナントに所属していません。" + +msgid "Trustee has no delegated roles." +msgstr "受託者に委任された役割がありません。" + +msgid "Trustor is disabled." +msgstr "委託者は無効です。" + +#, python-format +msgid "" +"Trying to update group %(group)s, so that, and only that, group must be " +"specified in the config" +msgstr "" +"グループ %(group)s を更新しようとしているため、そのオプションのみを構成で指定" +"する必要があります" + +#, python-format +msgid "" +"Trying to update option %(option)s in group %(group)s, but config provided " +"contains option %(option_other)s instead" +msgstr "" +"グループ %(group)s 内のオプション %(option)s を更新しようとしていますが、指定" +"された構成には代わりにオプション %(option_other)s が含まれています" + +#, python-format +msgid "" +"Trying to update option %(option)s in group %(group)s, so that, and only " +"that, option must be specified in the config" +msgstr "" +"グループ %(group)s 内のオプション %(option)s を更新しようとしているため、その" +"オプションのみを構成で指定する必要があります" + +msgid "" +"Unable to access the keystone database, please check it is configured " +"correctly." +msgstr "" +"keystone データベースにアクセスできません。このデータベースが正しく構成されて" +"いるかどうかを確認してください。" + +#, python-format +msgid "Unable to consume trust %(trust_id)s, unable to acquire lock." +msgstr "トラスト %(trust_id)s を使用できず、ロックを取得できません。" + +#, python-format +msgid "" +"Unable to delete region %(region_id)s because it or its child regions have " +"associated endpoints." +msgstr "" +"領域 %(region_id)s またはその子領域にエンドポイントが関連付けられているため、" +"この領域を削除できません。" + +#, python-format +msgid "Unable to find valid groups while using mapping %(mapping_id)s" +msgstr "" +"マッピング %(mapping_id)s の使用中に有効なグループが見つかりませんでした" + +#, python-format +msgid "" +"Unable to get a connection from pool id %(id)s after %(seconds)s seconds." +msgstr "" +"%(seconds)s 秒が経過した時点で、プール ID %(id)s からの接続がありません。" + +#, python-format +msgid "Unable to locate domain config directory: %s" +msgstr "ドメイン構成ディレクトリーが見つかりません: %s" + +#, python-format +msgid "Unable to lookup user %s" +msgstr "ユーザー %s を検索できません" + +#, python-format +msgid "" +"Unable to reconcile identity attribute %(attribute)s as it has conflicting " +"values %(new)s and %(old)s" +msgstr "" +"ID 属性 %(attribute)s に競合する値 %(new)s と %(old)s が含まれているため、調" +"整できません" + +#, python-format +msgid "" +"Unable to sign SAML assertion. It is likely that this server does not have " +"xmlsec1 installed, or this is the result of misconfiguration. Reason " +"%(reason)s" +msgstr "" +"SAML アサーションに署名できません。このサーバーに xmlsec1 がインストールされ" +"ていないか、誤った構成による結果と考えられます。理由: %(reason)s" + +msgid "Unable to sign token." +msgstr "トークンに署名できません。" + +#, python-format +msgid "Unexpected assignment type encountered, %s" +msgstr "無効な割り当てタイプ %s が検出されました" + +#, python-format +msgid "" +"Unexpected combination of grant attributes - User: %(user_id)s, Group: " +"%(group_id)s, Project: %(project_id)s, Domain: %(domain_id)s" +msgstr "" +"認可属性 (ユーザー: %(user_id)s、グループ: %(group_id)s、プロジェクト: " +"%(project_id)s、ドメイン: %(domain_id)s) の組み合わせが正しくありません。" + +#, python-format +msgid "Unexpected status requested for JSON Home response, %s" +msgstr "JSON ホーム応答に対して予期しない状況が要求されました。%s" + +msgid "Unknown Target" +msgstr "不明なターゲット" + +#, python-format +msgid "Unknown domain '%(name)s' specified by --domain-name" +msgstr "不明なドメイン '%(name)s' が --domain-name によって指定されました" + +#, python-format +msgid "Unknown token version %s" +msgstr "トークン・バージョン %s は不明です" + +#, python-format +msgid "Unregistered dependency: %(name)s for %(targets)s" +msgstr "未登録の依存関係: %(targets)s の %(name)s" + +msgid "Update of `parent_id` is not allowed." +msgstr "「parent_id」の更新は許可されていません。" + +msgid "Use a project scoped token when attempting to create a SAML assertion" +msgstr "" +"SAML アサーションの作成を試みるときは、プロジェクト範囲トークンを使用してくだ" +"さい。" + +#, python-format +msgid "User %(u_id)s is unauthorized for tenant %(t_id)s" +msgstr "ユーザー %(u_id)s はテナント %(t_id)s のアクセス権限がありません。" + +#, python-format +msgid "User %(user_id)s already has role %(role_id)s in tenant %(tenant_id)s" +msgstr "" +"ユーザー %(user_id)s には既に役割 %(role_id)s がテナント %(tenant_id)s にあり" +"ます" + +#, python-format +msgid "User %(user_id)s has no access to domain %(domain_id)s" +msgstr "" +"ユーザー %(user_id)s にはドメイン %(domain_id)s へのアクセス権限がありません" + +#, python-format +msgid "User %(user_id)s has no access to project %(project_id)s" +msgstr "" +"ユーザー %(user_id)s にはプロジェクト %(project_id)s へのアクセス権限がありま" +"せん" + +#, python-format +msgid "User %(user_id)s is already a member of group %(group_id)s" +msgstr "ユーザー %(user_id)s はすでにグループ %(group_id)s のメンバーです" + +#, python-format +msgid "User '%(user_id)s' not found in group '%(group_id)s'" +msgstr "ユーザー '%(user_id)s' がグループ '%(group_id)s' で見つかりません" + +msgid "User IDs do not match" +msgstr "ユーザー ID が一致しません" + +#, python-format +msgid "User is disabled: %s" +msgstr "ユーザーが無効化されています: %s" + +msgid "User is not a member of the requested project" +msgstr "ユーザーは、要求されたプロジェクトのメンバーではありません" + +msgid "User is not a trustee." +msgstr "ユーザーは受託者ではありません。" + +msgid "User not found" +msgstr "ユーザーが見つかりませんでした" + +#, python-format +msgid "User type %s not supported" +msgstr "ユーザー・タイプ %s はサポートされていません" + +msgid "You are not authorized to perform the requested action." +msgstr "要求されたアクションを実行する許可がありません。" + +#, python-format +msgid "You are not authorized to perform the requested action: %(action)s" +msgstr "要求されたアクションを実行する許可がありません: %(action)s" + +msgid "`key_mangler` functions must be callable." +msgstr "`key_mangler` 関数は呼び出し可能でなければなりません。" + +msgid "`key_mangler` option must be a function reference" +msgstr "`key_mangler` オプションは関数参照でなければなりません" + +msgid "any options" +msgstr "任意のオプション" + +msgid "auth_type is not Negotiate" +msgstr "auth_type は折衝されていません" + +msgid "authorizing user does not have role required" +msgstr "許可を与えるユーザーに、必要な役割がありません" + +msgid "cache_collection name is required" +msgstr "cache_collection name は必須です" + +#, python-format +msgid "cannot create a project in a branch containing a disabled project: %s" +msgstr "" +"無効なプロジェクトを含むブランチにプロジェクトを作成することはできません: %s" + +msgid "cannot create a project within a different domain than its parents." +msgstr "親とは別のドメイン内にプロジェクトを作成することはできません。" + +msgid "cannot delete a domain that is enabled, please disable it first." +msgstr "" +"有効なドメインは削除できません。最初に有効なドメインを無効にしてください。" + +#, python-format +msgid "cannot delete the project %s since it is not a leaf in the hierarchy." +msgstr "プロジェクト %s は階層内のリーフではないため、削除できません。" + +#, python-format +msgid "cannot disable project %s since its subtree contains enabled projects" +msgstr "" +"プロジェクト %s のサブツリーには有効になっているプロジェクトが含まれているた" +"め、このプロジェクトは無効にできません" + +#, python-format +msgid "cannot enable project %s since it has disabled parents" +msgstr "プロジェクト %s は無効になっている親を含むため、有効にできません" + +msgid "database db_name is required" +msgstr "database db_name は必須です" + +msgid "db_hosts value is required" +msgstr "db_hosts 値は必須です" + +msgid "delete the default domain" +msgstr "デフォルト・ドメインを削除してください" + +#, python-format +msgid "group %(group)s" +msgstr "グループ %(group)s" + +msgid "" +"idp_contact_type must be one of: [technical, other, support, administrative " +"or billing." +msgstr "" +"idp_contact_type は technical、other、support、administrative、または " +"billing のいずれかでなければなりません。" + +msgid "integer value expected for mongo_ttl_seconds" +msgstr "mongo_ttl_seconds には整数値が必要です" + +msgid "integer value expected for w (write concern attribute)" +msgstr "w (書き込み関連属性) には整数値が必要です" + +#, python-format +msgid "invalid date format %s" +msgstr "日付形式 %s は無効です" + +#, python-format +msgid "max hierarchy depth reached for %s branch." +msgstr "%s ブランチに到達する最大の階層の深さ。" + +msgid "no ssl support available" +msgstr "使用可能な SSL サポートがありません" + +#, python-format +msgid "option %(option)s in group %(group)s" +msgstr "グループ %(group)s 内のオプション %(option)s" + +msgid "pad must be single character" +msgstr "埋め込みは単一文字でなければなりません" + +msgid "padded base64url text must be multiple of 4 characters" +msgstr "埋め込まれた base64url テキストは 4 の倍数の文字数でなければなりません" + +msgid "provided consumer key does not match stored consumer key" +msgstr "指定されたコンシューマー鍵は保管済みコンシューマー鍵と一致しません" + +msgid "provided request key does not match stored request key" +msgstr "指定された要求鍵は保管済み要求鍵と一致しません" + +msgid "provided verifier does not match stored verifier" +msgstr "指定されたベリファイヤーは保管済みベリファイヤーと一致しません" + +msgid "region not type dogpile.cache.CacheRegion" +msgstr "領域のタイプが dogpile.cache.CacheRegion ではありません" + +msgid "remaining_uses must be a positive integer or null." +msgstr "remaining_uses は正整数またはヌルでなければなりません。" + +msgid "remaining_uses must not be set if redelegation is allowed" +msgstr "再委任が許可されている場合は remaining_uses を設定してはなりません" + +msgid "replicaset_name required when use_replica is True" +msgstr "use_replica が TRUE の場合は replicaset_name が必要です" + +#, python-format +msgid "" +"request to update group %(group)s, but config provided contains group " +"%(group_other)s instead" +msgstr "" +"グループ %(group)s の更新を要求しましたが、指定された構成には代わりにグルー" +"プ %(group_other)s が含まれています" + +msgid "rescope a scoped token" +msgstr "スコープ宣言されたトークンを再スコープ宣言します" + +#, python-format +msgid "text is multiple of 4, but pad \"%s\" occurs before 2nd to last char" +msgstr "" +"テキストは 4 の倍数ですが、最後から2 つ目の文字の前に埋め込み \"%s\" あります" + +#, python-format +msgid "text is multiple of 4, but pad \"%s\" occurs before non-pad last char" +msgstr "" +"テキストは 4 の倍数ですが、埋め込みではない最後の文字の前に埋め込み \"%s\" あ" +"ります" + +#, python-format +msgid "text is not a multiple of 4, but contains pad \"%s\"" +msgstr "テキストは 4 の倍数ではありませんが、埋め込み \"%s\" が含まれています" + +#, python-format +msgid "tls_cacertdir %s not found or is not a directory" +msgstr "" +"tls_cacertdir %s が見つかりませんでした。または、ディレクトリではありません。" + +#, python-format +msgid "tls_cacertfile %s not found or is not a file" +msgstr "" +"tls_cacertfile %s が見つかりませんでした。または、ファイルではありません。" + +#, python-format +msgid "token reference must be a KeystoneToken type, got: %s" +msgstr "" +"トークン参照は KeystoneToken タイプである必要があります。%s を受け取りました" diff --git a/keystone-moon/keystone/locale/keystone-log-error.pot b/keystone-moon/keystone/locale/keystone-log-error.pot index 375fb4b8..f53c653b 100644 --- a/keystone-moon/keystone/locale/keystone-log-error.pot +++ b/keystone-moon/keystone/locale/keystone-log-error.pot @@ -6,9 +6,9 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: keystone 8.0.0.0b3.dev14\n" +"Project-Id-Version: keystone 8.0.0.0b4.dev56\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" -"POT-Creation-Date: 2015-08-01 06:07+0000\n" +"POT-Creation-Date: 2015-09-21 06:08+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -17,16 +17,16 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.0\n" -#: keystone/notifications.py:396 +#: keystone/notifications.py:379 msgid "Failed to construct notifier" msgstr "" -#: keystone/notifications.py:491 +#: keystone/notifications.py:474 #, python-format msgid "Failed to send %(res_id)s %(event_type)s notification" msgstr "" -#: keystone/notifications.py:760 +#: keystone/notifications.py:743 #, python-format msgid "Failed to send %(action)s %(event_type)s notification" msgstr "" @@ -88,9 +88,9 @@ msgstr "" msgid "Server error" msgstr "" -#: keystone/contrib/federation/idp.py:428 +#: keystone/contrib/federation/idp.py:429 #, python-format -msgid "Error when signing assertion, reason: %(reason)s" +msgid "Error when signing assertion, reason: %(reason)s%(output)s" msgstr "" #: keystone/contrib/oauth1/core.py:136 @@ -104,14 +104,14 @@ msgid "" "%(region_id)s." msgstr "" -#: keystone/resource/core.py:485 +#: keystone/resource/core.py:488 #, python-format msgid "" "Circular reference or a repeated entry found projects hierarchy - " "%(project_id)s." msgstr "" -#: keystone/resource/core.py:950 +#: keystone/resource/core.py:966 #, python-format msgid "" "Unexpected results in response for domain config - %(count)s responses, " @@ -125,7 +125,7 @@ msgid "" "%(project_id)s." msgstr "" -#: keystone/token/provider.py:284 +#: keystone/token/provider.py:285 #, python-format msgid "Unexpected error or malformed token determining token expiry: %s" msgstr "" @@ -138,7 +138,7 @@ msgid "" "data: %(list)r" msgstr "" -#: keystone/token/providers/common.py:678 +#: keystone/token/providers/common.py:701 msgid "Failed to validate token" msgstr "" diff --git a/keystone-moon/keystone/locale/keystone-log-info.pot b/keystone-moon/keystone/locale/keystone-log-info.pot index f4c52cd4..69c9609c 100644 --- a/keystone-moon/keystone/locale/keystone-log-info.pot +++ b/keystone-moon/keystone/locale/keystone-log-info.pot @@ -6,9 +6,9 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: keystone 8.0.0.0b3.dev45\n" +"Project-Id-Version: keystone 8.0.0.0b4.dev16\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" -"POT-Creation-Date: 2015-08-06 06:28+0000\n" +"POT-Creation-Date: 2015-09-08 06:08+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -17,12 +17,12 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.0\n" -#: keystone/assignment/core.py:250 +#: keystone/assignment/core.py:217 #, python-format msgid "Creating the default role %s because it does not exist." msgstr "" -#: keystone/assignment/core.py:258 +#: keystone/assignment/core.py:225 #, python-format msgid "Creating the default role %s failed because it was already created" msgstr "" @@ -97,12 +97,24 @@ msgid "" "%(resource_type)s, operation %(operation)s payload %(payload)s" msgstr "" +#: keystone/middleware/core.py:266 +#, python-format +msgid "Cannot find client issuer in env by the issuer attribute - %s." +msgstr "" + +#: keystone/middleware/core.py:274 +#, python-format +msgid "" +"The client issuer %(client_issuer)s does not match with the trusted " +"issuer %(trusted_issuer)s" +msgstr "" + #: keystone/token/persistence/backends/sql.py:283 #, python-format msgid "Total expired tokens removed: %d" msgstr "" -#: keystone/token/providers/fernet/token_formatters.py:163 +#: keystone/token/providers/fernet/token_formatters.py:181 #, python-format msgid "" "Fernet token created with length of %d characters, which exceeds 255 " diff --git a/keystone-moon/keystone/locale/keystone-log-warning.pot b/keystone-moon/keystone/locale/keystone-log-warning.pot index 1109bcbe..c591d798 100644 --- a/keystone-moon/keystone/locale/keystone-log-warning.pot +++ b/keystone-moon/keystone/locale/keystone-log-warning.pot @@ -6,9 +6,9 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: keystone 8.0.0.0b3.dev122\n" +"Project-Id-Version: keystone 9.0.0.dev14\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" -"POT-Creation-Date: 2015-08-16 06:06+0000\n" +"POT-Creation-Date: 2015-09-24 06:09+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -21,21 +21,21 @@ msgstr "" msgid "missing exception kwargs (programmer error)" msgstr "" -#: keystone/auth/controllers.py:446 +#: keystone/auth/controllers.py:457 #, python-format msgid "" "User %(user_id)s doesn't have access to default project %(project_id)s. " "The token will be unscoped rather than scoped to the project." msgstr "" -#: keystone/auth/controllers.py:454 +#: keystone/auth/controllers.py:465 #, python-format msgid "" "User %(user_id)s's default project %(project_id)s is disabled. The token " "will be unscoped rather than scoped to the project." msgstr "" -#: keystone/auth/controllers.py:463 +#: keystone/auth/controllers.py:474 #, python-format msgid "" "User %(user_id)s's default project %(project_id)s not found. The token " @@ -55,23 +55,31 @@ msgstr "" msgid "Ignoring file (%s) while scanning domain config directory" msgstr "" -#: keystone/common/authorization.py:55 +#: keystone/common/authorization.py:65 msgid "RBAC: Invalid user data in token" msgstr "" -#: keystone/common/controller.py:83 keystone/middleware/core.py:194 +#: keystone/common/controller.py:84 keystone/middleware/core.py:199 msgid "RBAC: Invalid token" msgstr "" -#: keystone/common/controller.py:108 keystone/common/controller.py:205 -#: keystone/common/controller.py:755 +#: keystone/common/controller.py:109 keystone/common/controller.py:206 +#: keystone/common/controller.py:773 msgid "RBAC: Bypassing authorization" msgstr "" -#: keystone/common/controller.py:710 +#: keystone/common/controller.py:718 msgid "No domain information specified as part of list request" msgstr "" +#: keystone/common/controller.py:745 +msgid "" +"Not specifying a domain during a create user, group or project call, and " +"relying on falling back to the default domain, is deprecated as of " +"Liberty and will be removed in the N release. Specify the domain " +"explicitly or use a domain-scoped token" +msgstr "" + #: keystone/common/openssl.py:73 msgid "Failed to invoke ``openssl version``, assuming is v1.0 or newer" msgstr "" @@ -85,7 +93,7 @@ msgstr "" msgid "Couldn't find the auth context." msgstr "" -#: keystone/common/wsgi.py:243 +#: keystone/common/wsgi.py:251 #, python-format msgid "Authorization failed. %(exception)s from %(remote_addr)s" msgstr "" @@ -128,14 +136,14 @@ msgid "" "therefore cannot be used as an ID. Will get the ID from DN instead" msgstr "" -#: keystone/common/ldap/core.py:1668 +#: keystone/common/ldap/core.py:1669 #, python-format msgid "" "When deleting entries for %(search_base)s, could not delete nonexistent " "entries %(entries)s%(dots)s" msgstr "" -#: keystone/contrib/federation/utils.py:545 +#: keystone/contrib/federation/utils.py:543 msgid "Ignoring user name" msgstr "" @@ -170,28 +178,30 @@ msgstr "" msgid "Unable to add user %(user)s to %(tenant)s." msgstr "" -#: keystone/identity/core.py:122 +#: keystone/identity/core.py:137 #, python-format msgid "Invalid domain name (%s) found in config file name" msgstr "" -#: keystone/identity/core.py:160 +#: keystone/identity/core.py:175 #, python-format msgid "Unable to locate domain config directory: %s" msgstr "" -#: keystone/identity/core.py:602 +#: keystone/identity/core.py:692 #, python-format msgid "" "Found multiple domains being mapped to a driver that does not support " "that (e.g. LDAP) - Domain ID: %(domain)s, Default Driver: %(driver)s" msgstr "" -#: keystone/middleware/core.py:204 -msgid "Auth context already exists in the request environment" +#: keystone/middleware/core.py:293 +msgid "" +"Auth context already exists in the request environment; it will be used " +"for authorization instead of creating a new one." msgstr "" -#: keystone/resource/core.py:1237 +#: keystone/resource/core.py:1254 #, python-format msgid "" "Found what looks like an unmatched config option substitution reference -" @@ -200,7 +210,7 @@ msgid "" "added?" msgstr "" -#: keystone/resource/core.py:1244 +#: keystone/resource/core.py:1261 #, python-format msgid "" "Found what looks like an incorrectly constructed config option " @@ -208,6 +218,10 @@ msgid "" "%(option)s, value: %(value)s." msgstr "" +#: keystone/server/common.py:42 +msgid "debug is enabled so responses may include sensitive information." +msgstr "" + #: keystone/token/persistence/core.py:225 #, python-format msgid "" diff --git a/keystone-moon/keystone/locale/keystone.pot b/keystone-moon/keystone/locale/keystone.pot index 315891aa..64eab4ed 100644 --- a/keystone-moon/keystone/locale/keystone.pot +++ b/keystone-moon/keystone/locale/keystone.pot @@ -6,9 +6,9 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: keystone 8.0.0.0b3.dev122\n" +"Project-Id-Version: keystone 9.0.0.dev14\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" -"POT-Creation-Date: 2015-08-16 06:06+0000\n" +"POT-Creation-Date: 2015-09-24 06:09+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -287,95 +287,95 @@ msgid "" "%(domain_id)s" msgstr "" -#: keystone/exception.py:361 +#: keystone/exception.py:368 #, python-format msgid "Conflict occurred attempting to store %(type)s - %(details)s" msgstr "" -#: keystone/exception.py:369 +#: keystone/exception.py:376 msgid "An unexpected error prevented the server from fulfilling your request." msgstr "" -#: keystone/exception.py:372 +#: keystone/exception.py:379 #, python-format msgid "" "An unexpected error prevented the server from fulfilling your request: " "%(exception)s" msgstr "" -#: keystone/exception.py:395 +#: keystone/exception.py:402 #, python-format msgid "Unable to consume trust %(trust_id)s, unable to acquire lock." msgstr "" -#: keystone/exception.py:400 +#: keystone/exception.py:407 msgid "" "Expected signing certificates are not available on the server. Please " "check Keystone configuration." msgstr "" -#: keystone/exception.py:406 +#: keystone/exception.py:413 #, python-format msgid "Malformed endpoint URL (%(endpoint)s), see ERROR log for details." msgstr "" -#: keystone/exception.py:411 +#: keystone/exception.py:418 #, python-format msgid "" "Group %(group_id)s returned by mapping %(mapping_id)s was not found in " "the backend." msgstr "" -#: keystone/exception.py:416 +#: keystone/exception.py:423 #, python-format msgid "Error while reading metadata file, %(reason)s" msgstr "" -#: keystone/exception.py:420 +#: keystone/exception.py:427 #, python-format msgid "" "Unexpected combination of grant attributes - User: %(user_id)s, Group: " "%(group_id)s, Project: %(project_id)s, Domain: %(domain_id)s" msgstr "" -#: keystone/exception.py:427 +#: keystone/exception.py:434 msgid "The action you have requested has not been implemented." msgstr "" -#: keystone/exception.py:434 +#: keystone/exception.py:441 msgid "The service you have requested is no longer available on this server." msgstr "" -#: keystone/exception.py:441 +#: keystone/exception.py:448 #, python-format msgid "The Keystone configuration file %(config_file)s could not be found." msgstr "" -#: keystone/exception.py:446 +#: keystone/exception.py:453 msgid "" "No encryption keys found; run keystone-manage fernet_setup to bootstrap " "one." msgstr "" -#: keystone/exception.py:451 +#: keystone/exception.py:458 #, python-format msgid "" "The Keystone domain-specific configuration has specified more than one " "SQL driver (only one is permitted): %(source)s." msgstr "" -#: keystone/exception.py:458 +#: keystone/exception.py:465 #, python-format msgid "" "%(mod_name)s doesn't provide database migrations. The migration " "repository path at %(path)s doesn't exist or isn't a directory." msgstr "" -#: keystone/exception.py:465 +#: keystone/exception.py:472 msgid "Token version is unrecognizable or unsupported." msgstr "" -#: keystone/exception.py:470 +#: keystone/exception.py:477 #, python-format msgid "" "Unable to sign SAML assertion. It is likely that this server does not " @@ -383,25 +383,32 @@ msgid "" " %(reason)s" msgstr "" -#: keystone/exception.py:478 +#: keystone/exception.py:485 msgid "" "No Authorization headers found, cannot proceed with OAuth related calls, " "if running under HTTPd or Apache, ensure WSGIPassAuthorization is set to " "On." msgstr "" -#: keystone/notifications.py:273 +#: keystone/exception.py:493 +#, python-format +msgid "" +"Could not determine Identity Provider ID. The configuration option " +"%(issuer_attribute)s was not found in the request environment." +msgstr "" + +#: keystone/notifications.py:274 #, python-format msgid "%(event)s is not a valid notification event, must be one of: %(actions)s" msgstr "" -#: keystone/notifications.py:282 +#: keystone/notifications.py:283 #, python-format msgid "Method not callable: %s" msgstr "" #: keystone/assignment/controllers.py:99 keystone/identity/controllers.py:71 -#: keystone/resource/controllers.py:78 +#: keystone/resource/controllers.py:88 msgid "Name field is required and cannot be empty" msgstr "" @@ -438,21 +445,21 @@ msgid "" "an empty list." msgstr "" -#: keystone/assignment/core.py:233 +#: keystone/assignment/core.py:198 msgid "Must specify either domain or project" msgstr "" -#: keystone/assignment/core.py:903 +#: keystone/assignment/core.py:873 #, python-format msgid "Project (%s)" msgstr "" -#: keystone/assignment/core.py:905 +#: keystone/assignment/core.py:875 #, python-format msgid "Domain (%s)" msgstr "" -#: keystone/assignment/core.py:907 +#: keystone/assignment/core.py:877 msgid "Unknown Target" msgstr "" @@ -482,7 +489,7 @@ msgid "Unexpected assignment type encountered, %s" msgstr "" #: keystone/assignment/role_backends/ldap.py:61 keystone/catalog/core.py:135 -#: keystone/common/ldap/core.py:1400 keystone/resource/backends/ldap.py:148 +#: keystone/common/ldap/core.py:1400 keystone/resource/backends/ldap.py:170 #, python-format msgid "Duplicate ID, %s." msgstr "" @@ -505,23 +512,23 @@ msgid "" "conflicting values %(new)s and %(old)s" msgstr "" -#: keystone/auth/controllers.py:333 +#: keystone/auth/controllers.py:344 keystone/middleware/core.py:227 msgid "Scoping to both domain and project is not allowed" msgstr "" -#: keystone/auth/controllers.py:336 +#: keystone/auth/controllers.py:347 msgid "Scoping to both domain and trust is not allowed" msgstr "" -#: keystone/auth/controllers.py:339 +#: keystone/auth/controllers.py:350 msgid "Scoping to both project and trust is not allowed" msgstr "" -#: keystone/auth/controllers.py:509 +#: keystone/auth/controllers.py:520 msgid "User not found" msgstr "" -#: keystone/auth/controllers.py:613 +#: keystone/auth/controllers.py:624 msgid "A project-scoped token is required to produce a service catalog." msgstr "" @@ -640,7 +647,7 @@ msgstr "" msgid "Error while parsing line: '%(line)s': %(err)s" msgstr "" -#: keystone/common/authorization.py:47 keystone/common/wsgi.py:66 +#: keystone/common/authorization.py:57 keystone/common/wsgi.py:66 #, python-format msgid "token reference must be a KeystoneToken type, got: %s" msgstr "" @@ -688,27 +695,27 @@ msgstr "" msgid "%(property_name)s is not a %(display_expected_type)s" msgstr "" -#: keystone/common/controller.py:229 keystone/common/controller.py:245 -#: keystone/token/providers/common.py:636 +#: keystone/common/controller.py:230 keystone/common/controller.py:246 +#: keystone/token/providers/common.py:638 msgid "Non-default domain is not supported" msgstr "" -#: keystone/common/controller.py:322 keystone/common/controller.py:350 -#: keystone/identity/core.py:506 keystone/resource/core.py:774 -#: keystone/resource/backends/ldap.py:61 +#: keystone/common/controller.py:329 keystone/common/controller.py:358 +#: keystone/identity/core.py:596 keystone/resource/core.py:788 +#: keystone/resource/backends/ldap.py:66 keystone/resource/backends/ldap.py:74 #, python-format msgid "Expected dict or list: %s" msgstr "" -#: keystone/common/controller.py:363 +#: keystone/common/controller.py:371 msgid "Marker could not be found" msgstr "" -#: keystone/common/controller.py:374 +#: keystone/common/controller.py:382 msgid "Invalid limit value" msgstr "" -#: keystone/common/controller.py:682 +#: keystone/common/controller.py:690 msgid "Cannot change Domain ID" msgstr "" @@ -722,21 +729,35 @@ msgstr "" msgid "Unexpected status requested for JSON Home response, %s" msgstr "" +#: keystone/common/tokenless_auth.py:74 +msgid "Neither Project Domain ID nor Project Domain Name was provided." +msgstr "" + +#: keystone/common/tokenless_auth.py:166 +msgid "" +"User auth cannot be built due to missing either user id, or user name " +"with domain id, or user name with domain name." +msgstr "" + #: keystone/common/utils.py:166 keystone/credential/controllers.py:44 msgid "Invalid blob in credential" msgstr "" -#: keystone/common/wsgi.py:335 +#: keystone/common/wsgi.py:206 +msgid "Query string is not UTF-8 encoded" +msgstr "" + +#: keystone/common/wsgi.py:343 #, python-format msgid "%s field is required and cannot be empty" msgstr "" -#: keystone/common/wsgi.py:347 +#: keystone/common/wsgi.py:355 #, python-format msgid "%s field(s) cannot be empty" msgstr "" -#: keystone/common/wsgi.py:558 +#: keystone/common/wsgi.py:566 msgid "The resource could not be found." msgstr "" @@ -882,18 +903,18 @@ msgstr "" msgid "LDAP %s delete" msgstr "" -#: keystone/common/ldap/core.py:1521 +#: keystone/common/ldap/core.py:1522 msgid "" "Disabling an entity where the 'enable' attribute is ignored by " "configuration." msgstr "" -#: keystone/common/ldap/core.py:1532 +#: keystone/common/ldap/core.py:1533 #, python-format msgid "Cannot change %(option_name)s %(attr)s" msgstr "" -#: keystone/common/ldap/core.py:1619 +#: keystone/common/ldap/core.py:1620 #, python-format msgid "Member %(member)s is already a member of group %(group)s" msgstr "" @@ -919,7 +940,7 @@ msgstr "" msgid "%s extension does not exist." msgstr "" -#: keystone/common/validation/__init__.py:41 +#: keystone/common/validation/__init__.py:45 #, python-format msgid "" "validated expected to find %(param_name)r in function signature for " @@ -939,12 +960,12 @@ msgstr "" msgid "Credential belongs to another user" msgstr "" -#: keystone/contrib/endpoint_filter/backends/sql.py:69 +#: keystone/contrib/endpoint_filter/backends/sql.py:70 #, python-format msgid "Endpoint %(endpoint_id)s not found in project %(project_id)s" msgstr "" -#: keystone/contrib/endpoint_filter/backends/sql.py:180 +#: keystone/contrib/endpoint_filter/backends/sql.py:181 msgid "Endpoint Group Project Association not found" msgstr "" @@ -961,61 +982,61 @@ msgstr "" msgid "Missing entity ID from environment" msgstr "" -#: keystone/contrib/federation/controllers.py:344 +#: keystone/contrib/federation/controllers.py:353 msgid "Use a project scoped token when attempting to create a SAML assertion" msgstr "" -#: keystone/contrib/federation/idp.py:476 +#: keystone/contrib/federation/idp.py:477 #, python-format msgid "Cannot open certificate %(cert_file)s. Reason: %(reason)s" msgstr "" -#: keystone/contrib/federation/idp.py:543 +#: keystone/contrib/federation/idp.py:544 msgid "Ensure configuration option idp_entity_id is set." msgstr "" -#: keystone/contrib/federation/idp.py:546 +#: keystone/contrib/federation/idp.py:547 msgid "Ensure configuration option idp_sso_endpoint is set." msgstr "" -#: keystone/contrib/federation/idp.py:566 +#: keystone/contrib/federation/idp.py:567 msgid "" "idp_contact_type must be one of: [technical, other, support, " "administrative or billing." msgstr "" -#: keystone/contrib/federation/utils.py:178 +#: keystone/contrib/federation/utils.py:177 msgid "Federation token is expired" msgstr "" -#: keystone/contrib/federation/utils.py:231 +#: keystone/contrib/federation/utils.py:230 msgid "Could not find Identity Provider identifier in environment" msgstr "" -#: keystone/contrib/federation/utils.py:235 +#: keystone/contrib/federation/utils.py:234 msgid "" "Incoming identity provider identifier not included among the accepted " "identifiers." msgstr "" -#: keystone/contrib/federation/utils.py:523 +#: keystone/contrib/federation/utils.py:522 #, python-format msgid "User type %s not supported" msgstr "" -#: keystone/contrib/federation/utils.py:559 +#: keystone/contrib/federation/utils.py:557 #, python-format msgid "" "Invalid rule: %(identity_value)s. Both 'groups' and 'domain' keywords " "must be specified." msgstr "" -#: keystone/contrib/federation/utils.py:775 +#: keystone/contrib/federation/utils.py:766 #, python-format msgid "Identity Provider %(idp)s is disabled" msgstr "" -#: keystone/contrib/federation/utils.py:783 +#: keystone/contrib/federation/utils.py:774 #, python-format msgid "Service Provider %(sp)s is disabled" msgstr "" @@ -1102,8 +1123,8 @@ msgid "" msgstr "" #: keystone/contrib/revoke/core.py:227 keystone/token/provider.py:197 -#: keystone/token/provider.py:220 keystone/token/provider.py:286 -#: keystone/token/provider.py:293 +#: keystone/token/provider.py:221 keystone/token/provider.py:287 +#: keystone/token/provider.py:294 msgid "Failed to validate token" msgstr "" @@ -1120,31 +1141,37 @@ msgstr "" msgid "Enabled field should be a boolean" msgstr "" -#: keystone/identity/core.py:112 +#: keystone/identity/core.py:127 #, python-format msgid "Database at /domains/%s/config" msgstr "" -#: keystone/identity/core.py:189 +#: keystone/identity/core.py:271 +#, python-format +msgid "Config API entity at /domains/%s/config" +msgstr "" + +#: keystone/identity/core.py:277 #, python-format msgid "" -"Domain specific sql drivers are not supported via the Identity API. One " -"is specified in /domains/%s/config" +"Exceeded attempts to register domain %(domain)s to use the SQL driver, " +"the last domain that appears to have had it is %(last_domain)s, giving " +"up" msgstr "" -#: keystone/identity/core.py:361 keystone/identity/backends/ldap.py:58 +#: keystone/identity/core.py:451 keystone/identity/backends/ldap.py:58 #: keystone/identity/backends/ldap.py:60 keystone/identity/backends/ldap.py:66 #: keystone/identity/backends/ldap.py:68 keystone/identity/backends/sql.py:104 #: keystone/identity/backends/sql.py:106 msgid "Invalid user / password" msgstr "" -#: keystone/identity/core.py:771 +#: keystone/identity/core.py:861 #, python-format msgid "User is disabled: %s" msgstr "" -#: keystone/identity/core.py:813 +#: keystone/identity/core.py:903 msgid "Cannot change user ID" msgstr "" @@ -1167,19 +1194,23 @@ msgstr "" msgid "Found invalid token: scoped to both project and domain." msgstr "" -#: keystone/resource/controllers.py:234 +#: keystone/resource/controllers.py:218 +msgid "The creation of projects acting as domains is not allowed yet." +msgstr "" + +#: keystone/resource/controllers.py:252 msgid "" "Cannot use parents_as_list and parents_as_ids query params at the same " "time." msgstr "" -#: keystone/resource/controllers.py:240 +#: keystone/resource/controllers.py:258 msgid "" "Cannot use subtree_as_list and subtree_as_ids query params at the same " "time." msgstr "" -#: keystone/resource/core.py:82 +#: keystone/resource/core.py:81 #, python-format msgid "max hierarchy depth reached for %s branch." msgstr "" @@ -1198,137 +1229,145 @@ msgstr "" msgid "Domain is disabled: %s" msgstr "" -#: keystone/resource/core.py:145 +#: keystone/resource/core.py:143 #, python-format msgid "Domain cannot be named %s" msgstr "" -#: keystone/resource/core.py:148 +#: keystone/resource/core.py:146 #, python-format msgid "Domain cannot have ID %s" msgstr "" -#: keystone/resource/core.py:160 +#: keystone/resource/core.py:158 #, python-format msgid "Project is disabled: %s" msgstr "" -#: keystone/resource/core.py:180 +#: keystone/resource/core.py:178 #, python-format msgid "cannot enable project %s since it has disabled parents" msgstr "" -#: keystone/resource/core.py:188 +#: keystone/resource/core.py:186 #, python-format msgid "cannot disable project %s since its subtree contains enabled projects" msgstr "" -#: keystone/resource/core.py:199 +#: keystone/resource/core.py:197 msgid "Update of `parent_id` is not allowed." msgstr "" -#: keystone/resource/core.py:226 +#: keystone/resource/core.py:202 +msgid "Update of `is_domain` is not allowed." +msgstr "" + +#: keystone/resource/core.py:229 #, python-format msgid "cannot delete the project %s since it is not a leaf in the hierarchy." msgstr "" -#: keystone/resource/core.py:253 +#: keystone/resource/core.py:256 msgid "Project field is required and cannot be empty." msgstr "" -#: keystone/resource/core.py:389 +#: keystone/resource/core.py:392 msgid "Multiple domains are not supported" msgstr "" -#: keystone/resource/core.py:442 +#: keystone/resource/core.py:445 msgid "delete the default domain" msgstr "" -#: keystone/resource/core.py:453 +#: keystone/resource/core.py:456 msgid "cannot delete a domain that is enabled, please disable it first." msgstr "" -#: keystone/resource/core.py:859 +#: keystone/resource/core.py:876 msgid "No options specified" msgstr "" -#: keystone/resource/core.py:865 +#: keystone/resource/core.py:882 #, python-format msgid "" "The value of group %(group)s specified in the config should be a " "dictionary of options" msgstr "" -#: keystone/resource/core.py:889 +#: keystone/resource/core.py:906 #, python-format msgid "" "Option %(option)s found with no group specified while checking domain " "configuration request" msgstr "" -#: keystone/resource/core.py:896 +#: keystone/resource/core.py:913 #, python-format msgid "Group %(group)s is not supported for domain specific configurations" msgstr "" -#: keystone/resource/core.py:903 +#: keystone/resource/core.py:920 #, python-format msgid "" "Option %(option)s in group %(group)s is not supported for domain specific" " configurations" msgstr "" -#: keystone/resource/core.py:956 +#: keystone/resource/core.py:973 msgid "An unexpected error occurred when retrieving domain configs" msgstr "" -#: keystone/resource/core.py:1035 keystone/resource/core.py:1119 -#: keystone/resource/core.py:1190 keystone/resource/config_backends/sql.py:70 +#: keystone/resource/core.py:1052 keystone/resource/core.py:1136 +#: keystone/resource/core.py:1207 keystone/resource/config_backends/sql.py:76 #, python-format msgid "option %(option)s in group %(group)s" msgstr "" -#: keystone/resource/core.py:1038 keystone/resource/core.py:1124 -#: keystone/resource/core.py:1186 +#: keystone/resource/core.py:1055 keystone/resource/core.py:1141 +#: keystone/resource/core.py:1203 #, python-format msgid "group %(group)s" msgstr "" -#: keystone/resource/core.py:1040 +#: keystone/resource/core.py:1057 msgid "any options" msgstr "" -#: keystone/resource/core.py:1084 +#: keystone/resource/core.py:1101 #, python-format msgid "" "Trying to update option %(option)s in group %(group)s, so that, and only " "that, option must be specified in the config" msgstr "" -#: keystone/resource/core.py:1089 +#: keystone/resource/core.py:1106 #, python-format msgid "" "Trying to update group %(group)s, so that, and only that, group must be " "specified in the config" msgstr "" -#: keystone/resource/core.py:1098 +#: keystone/resource/core.py:1115 #, python-format msgid "" "request to update group %(group)s, but config provided contains group " "%(group_other)s instead" msgstr "" -#: keystone/resource/core.py:1105 +#: keystone/resource/core.py:1122 #, python-format msgid "" "Trying to update option %(option)s in group %(group)s, but config " "provided contains option %(option_other)s instead" msgstr "" -#: keystone/resource/backends/ldap.py:150 -#: keystone/resource/backends/ldap.py:158 -#: keystone/resource/backends/ldap.py:162 +#: keystone/resource/backends/ldap.py:88 +msgid "LDAP does not support projects with is_domain flag enabled" +msgstr "" + +#: keystone/resource/backends/ldap.py:172 +#: keystone/resource/backends/ldap.py:180 +#: keystone/resource/backends/ldap.py:184 msgid "Domains are read-only against LDAP" msgstr "" @@ -1363,62 +1402,62 @@ msgstr "" msgid "Unknown token version %s" msgstr "" -#: keystone/token/providers/common.py:54 +#: keystone/token/providers/common.py:53 msgid "Domains are not supported by the v2 API. Please use the v3 API instead." msgstr "" -#: keystone/token/providers/common.py:64 +#: keystone/token/providers/common.py:63 #, python-format msgid "" "Project not found in the default domain (please use the v3 API instead): " "%s" msgstr "" -#: keystone/token/providers/common.py:83 +#: keystone/token/providers/common.py:82 #, python-format msgid "User not found in the default domain (please use the v3 API instead): %s" msgstr "" -#: keystone/token/providers/common.py:292 -#: keystone/token/providers/common.py:397 +#: keystone/token/providers/common.py:299 +#: keystone/token/providers/common.py:404 #, python-format msgid "User %(user_id)s has no access to project %(project_id)s" msgstr "" -#: keystone/token/providers/common.py:297 -#: keystone/token/providers/common.py:402 +#: keystone/token/providers/common.py:304 +#: keystone/token/providers/common.py:409 #, python-format msgid "User %(user_id)s has no access to domain %(domain_id)s" msgstr "" -#: keystone/token/providers/common.py:324 +#: keystone/token/providers/common.py:331 msgid "Trustor is disabled." msgstr "" -#: keystone/token/providers/common.py:388 +#: keystone/token/providers/common.py:395 msgid "Trustee has no delegated roles." msgstr "" -#: keystone/token/providers/common.py:449 +#: keystone/token/providers/common.py:456 #, python-format msgid "Invalid audit info data type: %(data)s (%(type)s)" msgstr "" -#: keystone/token/providers/common.py:477 +#: keystone/token/providers/common.py:484 msgid "User is not a trustee." msgstr "" -#: keystone/token/providers/common.py:546 +#: keystone/token/providers/common.py:553 msgid "The configured token provider does not support bind authentication." msgstr "" -#: keystone/token/providers/common.py:626 +#: keystone/token/providers/common.py:628 msgid "" "Attempting to use OS-FEDERATION token with V2 Identity Service, use V3 " "Authentication" msgstr "" -#: keystone/token/providers/common.py:644 +#: keystone/token/providers/common.py:646 msgid "Domain scoped token is not supported" msgstr "" @@ -1426,30 +1465,30 @@ msgstr "" msgid "Unable to sign token." msgstr "" -#: keystone/token/providers/fernet/core.py:182 +#: keystone/token/providers/fernet/core.py:215 msgid "" "This is not a v2.0 Fernet token. Use v3 for trust, domain, or federated " "tokens." msgstr "" -#: keystone/token/providers/fernet/token_formatters.py:80 +#: keystone/token/providers/fernet/token_formatters.py:83 msgid "This is not a recognized Fernet token" msgstr "" -#: keystone/token/providers/fernet/token_formatters.py:202 +#: keystone/token/providers/fernet/token_formatters.py:246 #, python-format msgid "This is not a recognized Fernet payload version: %s" msgstr "" -#: keystone/trust/controllers.py:144 +#: keystone/trust/controllers.py:141 msgid "Redelegation allowed for delegated by trust only" msgstr "" -#: keystone/trust/controllers.py:177 +#: keystone/trust/controllers.py:174 msgid "The authenticated user should match the trustor." msgstr "" -#: keystone/trust/controllers.py:182 +#: keystone/trust/controllers.py:179 msgid "At least one role should be specified." msgstr "" diff --git a/keystone-moon/keystone/locale/ko_KR/LC_MESSAGES/keystone-log-critical.po b/keystone-moon/keystone/locale/ko_KR/LC_MESSAGES/keystone-log-critical.po index 6a6f1868..ae6dc845 100644 --- a/keystone-moon/keystone/locale/ko_KR/LC_MESSAGES/keystone-log-critical.po +++ b/keystone-moon/keystone/locale/ko_KR/LC_MESSAGES/keystone-log-critical.po @@ -3,21 +3,23 @@ # This file is distributed under the same license as the keystone project. # # Translators: +# OpenStack Infra <zanata@openstack.org>, 2015. #zanata msgid "" msgstr "" -"Project-Id-Version: Keystone\n" +"Project-Id-Version: keystone 8.0.0.0b4.dev56\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" -"POT-Creation-Date: 2015-08-06 06:28+0000\n" -"PO-Revision-Date: 2014-08-31 15:19+0000\n" +"POT-Creation-Date: 2015-09-21 06:08+0000\n" +"PO-Revision-Date: 2014-08-31 03:19+0000\n" "Last-Translator: openstackjenkins <jenkins@openstack.org>\n" "Language-Team: Korean (Korea) (http://www.transifex.com/openstack/keystone/" "language/ko_KR/)\n" -"Language: ko_KR\n" +"Language: ko-KR\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" "Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Zanata 3.7.1\n" #, python-format msgid "Unable to open template file %s" diff --git a/keystone-moon/keystone/locale/ko_KR/LC_MESSAGES/keystone.po b/keystone-moon/keystone/locale/ko_KR/LC_MESSAGES/keystone.po new file mode 100644 index 00000000..f6e0fab8 --- /dev/null +++ b/keystone-moon/keystone/locale/ko_KR/LC_MESSAGES/keystone.po @@ -0,0 +1,1213 @@ +# Korean (South Korea) translations for keystone. +# Copyright (C) 2015 OpenStack Foundation +# This file is distributed under the same license as the keystone project. +# +# Translators: +# Sungjin Kang <potopro@gmail.com>, 2013 +# Lucas Palm <lapalm@us.ibm.com>, 2015. #zanata +# OpenStack Infra <zanata@openstack.org>, 2015. #zanata +msgid "" +msgstr "" +"Project-Id-Version: keystone 9.0.0.dev14\n" +"Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" +"POT-Creation-Date: 2015-09-24 06:09+0000\n" +"PO-Revision-Date: 2015-09-03 12:54+0000\n" +"Last-Translator: openstackjenkins <jenkins@openstack.org>\n" +"Language-Team: Korean (Korea) (http://www.transifex.com/openstack/keystone/" +"language/ko_KR/)\n" +"Plural-Forms: nplurals=1; plural=0\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" + +#, python-format +msgid "%(detail)s" +msgstr "%(detail)s" + +#, python-format +msgid "" +"%(event)s is not a valid notification event, must be one of: %(actions)s" +msgstr "" +"%(event)s은(는) 올바른 알림 이벤트가 아니며 %(actions)s 중 하나여야 합니다." + +#, python-format +msgid "%(host)s is not a trusted dashboard host" +msgstr "%(host)s이(가) 신뢰 대시보드 호스트가 아님" + +#, python-format +msgid "%(message)s %(amendment)s" +msgstr "%(message)s %(amendment)s" + +#, python-format +msgid "" +"%(mod_name)s doesn't provide database migrations. The migration repository " +"path at %(path)s doesn't exist or isn't a directory." +msgstr "" +"%(mod_name)s은(는) 데이터베이스 마이그레이션을 제공하지 않습니다. 마이그레이" +"션 저장소 경로가 %(path)s에 존재하지 않거나 디렉토리가 아닙니다." + +#, python-format +msgid "%(property_name)s cannot be less than %(min_length)s characters." +msgstr "%(property_name)s은(는) %(min_length)s자 미만일 수 없습니다. " + +#, python-format +msgid "%(property_name)s is not a %(display_expected_type)s" +msgstr "%(property_name)s이(가) %(display_expected_type)s이(가) 아님" + +#, python-format +msgid "%(property_name)s should not be greater than %(max_length)s characters." +msgstr "%(property_name)s은(는) %(max_length)s자 이하여야 합니다. " + +#, python-format +msgid "%s cannot be empty." +msgstr "%s은(는) 공백일 수 없습니다. " + +#, python-format +msgid "%s extension does not exist." +msgstr "%s 확장자가 존재하지 않습니다." + +#, python-format +msgid "%s field is required and cannot be empty" +msgstr "%s 필드가 필요하며 비어 있을 수 없음" + +#, python-format +msgid "%s field(s) cannot be empty" +msgstr "%s 필드는 비어 있을 수 없음" + +msgid "(Disable debug mode to suppress these details.)" +msgstr "" +"(이러한 세부사항을 억제하려면 디버그 모드를 사용 안함으로 설정하십시오.)" + +msgid "--all option cannot be mixed with other options" +msgstr "--all 옵션은 다른 옵션과 함께 사용할 수 없음" + +msgid "A project-scoped token is required to produce a service catalog." +msgstr "서비스 카탈로그를 생성하려면 프로젝트 범위 토큰이 필요합니다." + +msgid "Access token is expired" +msgstr "액세스 토큰이 만료됨" + +msgid "Access token not found" +msgstr "액세스 토큰을 찾을 수 없음" + +msgid "Additional authentications steps required." +msgstr "추가 인증 단계가 필요합니다." + +msgid "An unexpected error occurred when retrieving domain configs" +msgstr "도메인 구성 검색 중 예상치 못한 오류 발생" + +#, python-format +msgid "An unexpected error occurred when trying to store %s" +msgstr "%s을(를) 저장하려 할 때 예기치 않은 오류가 발생했음" + +msgid "An unexpected error prevented the server from fulfilling your request." +msgstr "예상치 않은 오류가 발생하여 서버가 사용자 요청을 이행하지 못함." + +#, python-format +msgid "" +"An unexpected error prevented the server from fulfilling your request: " +"%(exception)s" +msgstr "" +"예상치 않은 오류가 발생하여 서버가 사용자 요청을 이행하지 못함:%(exception)s" + +msgid "An unhandled exception has occurred: Could not find metadata." +msgstr "처리되지 않은 예외가 발생함: 메타데이터를 찾을 수 없음." + +msgid "At least one option must be provided" +msgstr "하나 이상의 옵션을 제공해야 함" + +msgid "At least one option must be provided, use either --all or --domain-name" +msgstr "" +"하나 이상의 옵션을 제공해야 합니다. --all 또는 --domain-name을 사용하십시오. " + +msgid "At least one role should be specified." +msgstr "최소한 하나의 역할을 지정해야 합니다." + +msgid "Attempted to authenticate with an unsupported method." +msgstr "지원되지 않는 방법으로 인증을 시도했습니다." + +msgid "" +"Attempting to use OS-FEDERATION token with V2 Identity Service, use V3 " +"Authentication" +msgstr "" +"V2 ID 서비스에서 OS-FEDERATION 토큰을 사용할 경우 V3 인증을 사용하십시오." + +msgid "Authentication plugin error." +msgstr "인증 플러그인 오류." + +msgid "Cannot authorize a request token with a token issued via delegation." +msgstr "위임을 통해 발행된 토큰으로 요청 토큰에 권한을 부여할 수 없습니다." + +#, python-format +msgid "Cannot change %(option_name)s %(attr)s" +msgstr "%(option_name)s %(attr)s을(를) 변경할 수 없음" + +msgid "Cannot change Domain ID" +msgstr "도메인 ID를 변경할 수 없음" + +msgid "Cannot change consumer secret" +msgstr "이용자 본인확인정보를 변경할 수 없음" + +msgid "Cannot change user ID" +msgstr "사용자 ID를 변경할 수 없음" + +msgid "Cannot change user name" +msgstr "사용자 이름을 변경할 수 없음" + +#, python-format +msgid "Cannot create project with parent: %(project_id)s" +msgstr "상위로 프로젝트를 작성할 수 없음: %(project_id)s" + +#, python-format +msgid "Cannot duplicate name %s" +msgstr "%s 이름을 복제할 수 없습니다." + +msgid "Cannot list request tokens with a token issued via delegation." +msgstr "위임을 통해 발행된 토큰으로 요청 토큰을 나열할 수 없습니다." + +#, python-format +msgid "Cannot open certificate %(cert_file)s. Reason: %(reason)s" +msgstr "%(cert_file)s 인증서를 열수 없습니다. 이유: %(reason)s" + +#, python-format +msgid "Cannot remove role that has not been granted, %s" +msgstr "권한이 부여되지 않은 역할을 제거할 수 없음: %s" + +msgid "" +"Cannot truncate a driver call without hints list as first parameter after " +"self " +msgstr "" +"자신 뒤의 첫 번째 매개변수와 같은 힌트 목록 없이 드라이버 호출을 자를 수 없음" + +msgid "" +"Cannot use parents_as_list and parents_as_ids query params at the same time." +msgstr "" +"parents_as_list 및 parents_as_ids 조회 매개변수를 동시에 사용할 수 없습니다." + +msgid "" +"Cannot use subtree_as_list and subtree_as_ids query params at the same time." +msgstr "" +"subtree_as_list 및 subtree_as_ids 조회 매개변수를 동시에 사용할 수 없습니다." + +msgid "" +"Combining effective and group filter will always result in an empty list." +msgstr "결합에 효율적인 그룹 필터는 항상 빈 목록을 생성합니다." + +msgid "" +"Combining effective, domain and inherited filters will always result in an " +"empty list." +msgstr "결합에 효율적인 도메인과 상속 필터는 항상 빈 목록을 생성합니다." + +#, python-format +msgid "Conflict occurred attempting to store %(type)s - %(details)s" +msgstr "%(type)s을(를) 저장하는 중에 충돌이 발생함 - %(details)s" + +#, python-format +msgid "Conflicting region IDs specified: \"%(url_id)s\" != \"%(ref_id)s\"" +msgstr "지정된 리젼 ID가 충돌함: \"%(url_id)s\" != \"%(ref_id)s\"" + +msgid "Consumer not found" +msgstr "이용자를 찾을 수 없음" + +#, python-format +msgid "" +"Could not change immutable attribute(s) '%(attributes)s' in target %(target)s" +msgstr "%(target)s 대상에서 불변 속성 '%(attributes)s'을(를) 변경할 수 없음" + +#, python-format +msgid "" +"Could not find %(group_or_option)s in domain configuration for domain " +"%(domain_id)s" +msgstr "" +"다음 도메인의 도메인 구성에서 %(group_or_option)s을(를) 찾을 수 없습니다. " +"%(domain_id)s" + +#, python-format +msgid "Could not find Endpoint Group: %(endpoint_group_id)s" +msgstr "엔드포인트 그룹을 찾을 수 없음: %(endpoint_group_id)s" + +msgid "Could not find Identity Provider identifier in environment" +msgstr "환경에서 ID 제공자의 ID를 찾을 수 없음" + +#, python-format +msgid "Could not find Identity Provider: %(idp_id)s" +msgstr "%(idp_id)s ID 제공자를 찾을 수 없음" + +#, python-format +msgid "Could not find Service Provider: %(sp_id)s" +msgstr "서비스 제공자를 찾을 수 없음: %(sp_id)s" + +#, python-format +msgid "Could not find credential: %(credential_id)s" +msgstr "%(credential_id)s 신임 정보를 찾을 수 없음" + +#, python-format +msgid "Could not find domain: %(domain_id)s" +msgstr "%(domain_id)s 도메인을 찾을 수 없음" + +#, python-format +msgid "Could not find endpoint: %(endpoint_id)s" +msgstr "%(endpoint_id)s 엔드포인트를 찾을 수 없음" + +#, python-format +msgid "" +"Could not find federated protocol %(protocol_id)s for Identity Provider: " +"%(idp_id)s" +msgstr "" +"ID 제공자 %(idp_id)s에 대한 연합 프로토콜 %(protocol_id)s을(를) 찾을 수 없음" + +#, python-format +msgid "Could not find group: %(group_id)s" +msgstr "%(group_id)s 그룹을 찾을 수 없음" + +#, python-format +msgid "Could not find mapping: %(mapping_id)s" +msgstr "%(mapping_id)s 맵핑을 찾을 수 없음" + +msgid "Could not find policy association" +msgstr "정책 연관을 찾을 수 없음" + +#, python-format +msgid "Could not find policy: %(policy_id)s" +msgstr "%(policy_id)s 정책을 찾을 수 없음" + +#, python-format +msgid "Could not find project: %(project_id)s" +msgstr "%(project_id)s 프로젝트를 찾을 수 없음" + +#, python-format +msgid "Could not find region: %(region_id)s" +msgstr "%(region_id)s 리젼을 찾을 수 없음" + +msgid "Could not find role" +msgstr "역할을 찾을 수 없음" + +#, python-format +msgid "" +"Could not find role assignment with role: %(role_id)s, user or group: " +"%(actor_id)s, project or domain: %(target_id)s" +msgstr "" +"%(role_id)s 역할에 대한 역할 지정을 찾을 수 없음. 사용자 또는 그룹: " +"%(actor_id)s, 프로젝트 또는 도메인: %(target_id)s" + +#, python-format +msgid "Could not find role: %(role_id)s" +msgstr "%(role_id)s 규칙을 찾을 수 없음" + +#, python-format +msgid "Could not find service: %(service_id)s" +msgstr "%(service_id)s 서비스를 찾을 수 없음" + +#, python-format +msgid "Could not find token: %(token_id)s" +msgstr "%(token_id)s 토큰을 찾을 수 없음" + +#, python-format +msgid "Could not find trust: %(trust_id)s" +msgstr "%(trust_id)s 신뢰를 찾을 수 없음" + +#, python-format +msgid "Could not find user: %(user_id)s" +msgstr "%(user_id)s 사용자를 찾을 수 없음" + +#, python-format +msgid "Could not find version: %(version)s" +msgstr "%(version)s 버전을 찾을 수 없음" + +#, python-format +msgid "Could not find: %(target)s" +msgstr "%(target)s을(를) 찾을 수 없음" + +msgid "Could not validate the access token" +msgstr "액세스 토큰을 유효성 검증할 수 없음" + +msgid "Credential belongs to another user" +msgstr "신임 정보가 다른 사용자에 속함" + +#, python-format +msgid "Database at /domains/%s/config" +msgstr "/domains/%s/config의 데이터베이스" + +msgid "" +"Disabling an entity where the 'enable' attribute is ignored by configuration." +msgstr "구성에서 'enable' 속성이 있는 엔티티의 사용 안함 설정을 무시합니다." + +#, python-format +msgid "Domain (%s)" +msgstr "도메인(%s)" + +#, python-format +msgid "Domain cannot be named %s" +msgstr "도메인 이름은 %s일 수 없음" + +#, python-format +msgid "Domain cannot have ID %s" +msgstr "도메인 ID가 %s일 수 없음" + +#, python-format +msgid "Domain is disabled: %s" +msgstr "도메인을 사용 안함: %s" + +msgid "Domain metadata not supported by LDAP" +msgstr "도메인 메타데이터가 LDAP에 의해 지원되지 않음" + +msgid "Domain scoped token is not supported" +msgstr "도메인 범위 지정 토큰은 지원되지 않음" + +#, python-format +msgid "" +"Domain: %(domain)s already has a configuration defined - ignoring file: " +"%(file)s." +msgstr "" +"%(domain)s 도메인에 이미 정의된 구성이 있음 - 다음 파일을 무시하십시오. " +"%(file)s." + +msgid "Domains are read-only against LDAP" +msgstr "LDAP에 대한 도메인이 읽기 전용입니다." + +msgid "Duplicate Entry" +msgstr "중복 항목" + +#, python-format +msgid "Duplicate ID, %s." +msgstr "중복 ID, %s." + +#, python-format +msgid "Duplicate name, %s." +msgstr "중복 이름, %s." + +msgid "Enabled field must be a boolean" +msgstr "사용으로 설정된 필드는 부울이어야 함" + +msgid "Enabled field should be a boolean" +msgstr "사용으로 설정된 필드는 부울이어야 함" + +#, python-format +msgid "Endpoint %(endpoint_id)s not found in project %(project_id)s" +msgstr "%(endpoint_id)s 엔드포인트가 %(project_id)s 프로젝트에 없음 " + +msgid "Endpoint Group Project Association not found" +msgstr "엔드포인트 그룹 프로젝트 연관을 찾을 수 없음" + +msgid "Ensure configuration option idp_entity_id is set." +msgstr "구성 옵션 idp_entity_id가 설정되어 있는지 확인하십시오." + +msgid "Ensure configuration option idp_sso_endpoint is set." +msgstr "구성 옵션 idp_sso_endpoint가 설정되어 있는지 확인하십시오." + +#, python-format +msgid "" +"Error parsing configuration file for domain: %(domain)s, file: %(file)s." +msgstr "" +"%(domain)s 도메인에 대한 구성 파일을 구문 분석하는 중 오류 발생. 파일: " +"%(file)s." + +#, python-format +msgid "Error while reading metadata file, %(reason)s" +msgstr "메타데이터 파일을 읽는 중에 오류 발생, %(reason)s" + +#, python-format +msgid "Expected dict or list: %s" +msgstr "예상된 사전 또는 목록: %s" + +msgid "" +"Expected signing certificates are not available on the server. Please check " +"Keystone configuration." +msgstr "" +"예상 서명 인증서를 서버에서 사용할 수 없습니다. 키스톤 구성을 확인하십시오." + +#, python-format +msgid "" +"Expecting to find %(attribute)s in %(target)s - the server could not comply " +"with the request since it is either malformed or otherwise incorrect. The " +"client is assumed to be in error." +msgstr "" +"%(target)s에 %(attribute)s이(가) 있어야 합니다- 서버의 형식이나 다른 항목이 " +"올바르지 않기 때문에 서버가 요청을 준수할 수 없습니다. 클라이언트가 오류 상태" +"로 간주됩니다." + +#, python-format +msgid "Failed to start the %(name)s server" +msgstr "%(name)s 서버를 시작하지 못함" + +msgid "Failed to validate token" +msgstr "토큰을 유효성 검증하지 못했음" + +msgid "Federation token is expired" +msgstr "연합 토큰이 만료됨" + +#, python-format +msgid "" +"Field \"remaining_uses\" is set to %(value)s while it must not be set in " +"order to redelegate a trust" +msgstr "" +"필드 \"remaining_uses\"가 %(value)s(으)로 설정되었으나 신뢰를 재위임하려면 설" +"정하지 않아야 함" + +msgid "Found invalid token: scoped to both project and domain." +msgstr "" +"올바르지 않은 토큰이 있습니다. 프로젝트와 도메인 둘 다 범위에 포함됩니다." + +#, python-format +msgid "Group %(group)s is not supported for domain specific configurations" +msgstr "도메인 특정 구성에 대해 %(group)s 그룹이 지원되지 않음" + +#, python-format +msgid "" +"Group %(group_id)s returned by mapping %(mapping_id)s was not found in the " +"backend." +msgstr "" +"맵핑 %(mapping_id)s별로 리턴된 그룹 %(group_id)s을(를) 백엔드에서 찾지 못했습" +"니다." + +#, python-format +msgid "" +"Group membership across backend boundaries is not allowed, group in question " +"is %(group_id)s, user is %(user_id)s" +msgstr "" +"경계를 초월한 그룹 멤버십이 허용되지 않습니다. 관련 그룹은 %(group_id)s이고 " +"사용자는 %(user_id)s입니다." + +#, python-format +msgid "ID attribute %(id_attr)s not found in LDAP object %(dn)s" +msgstr "ID 속성 %(id_attr)s을(를) LDAP 오브젝트 %(dn)s에서 찾을 수 없음" + +#, python-format +msgid "Identity Provider %(idp)s is disabled" +msgstr "ID 제공자 %(idp)s이(가) 사용 안함으로 설정됨" + +msgid "" +"Incoming identity provider identifier not included among the accepted " +"identifiers." +msgstr "승인 ID에 수신 ID 제공자가 포함되지 않습니다." + +#, python-format +msgid "Invalid LDAP TLS certs option: %(option)s. Choose one of: %(options)s" +msgstr "" +"올바르지 않은 LDAP TLS 인증 옵션: %(option)s. 다음 중 하나 선택: %(options)s" + +#, python-format +msgid "Invalid LDAP TLS_AVAIL option: %s. TLS not available" +msgstr "올바르지 않은 LDAP TLS_AVAIL 옵션: %s. TLS를 사용할 수 없음" + +#, python-format +msgid "Invalid LDAP deref option: %(option)s. Choose one of: %(options)s" +msgstr "" +"올바르지 않은 LDAP deref 옵션: %(option)s. 다음 중 하나 선택: %(options)s" + +#, python-format +msgid "Invalid LDAP scope: %(scope)s. Choose one of: %(options)s" +msgstr "올바르지 않은 LDAP 범위: %(scope)s. 다음 중 하나를 선택: %(options)s" + +msgid "Invalid TLS / LDAPS combination" +msgstr "잘못된 TLS / LDAPS 결합." + +#, python-format +msgid "Invalid audit info data type: %(data)s (%(type)s)" +msgstr "올바르지 않은 감사 정보 데이터 유형: %(data)s (%(type)s)" + +msgid "Invalid blob in credential" +msgstr "신임 정보에 올바르지 blob가 있음" + +#, python-format +msgid "" +"Invalid domain name: %(domain)s found in config file name: %(file)s - " +"ignoring this file." +msgstr "" +"구성 파일 이름에 올바르지 않은 도메인 이름 %(domain)s이(가) 있음: %(file)s - " +"이 파일을 무시하십시오." + +#, python-format +msgid "Invalid domain specific configuration: %(reason)s" +msgstr "올바르지 않은 도메인 특정 구성: %(reason)s" + +#, python-format +msgid "Invalid input for field '%(path)s'. The value is '%(value)s'." +msgstr "'%(path)s' 필드에 올바르지 않은 입력입니다. 값은 '%(value)s'입니다." + +msgid "Invalid limit value" +msgstr "올바르지 않은 한계 값" + +#, python-format +msgid "" +"Invalid mix of entities for policy association - only Endpoint, Service or " +"Region+Service allowed. Request was - Endpoint: %(endpoint_id)s, Service: " +"%(service_id)s, Region: %(region_id)s" +msgstr "" +"정책 연관에 대한 엔티티의 올바르지 않은 조합인 엔드포인트, 서비스 또는 리젼" +"+서비스가 허용되었습니다. 요청은 엔드포인트: %(endpoint_id)s, 서비스: " +"%(service_id)s, 리젼: %(region_id)s입니다." + +#, python-format +msgid "" +"Invalid rule: %(identity_value)s. Both 'groups' and 'domain' keywords must " +"be specified." +msgstr "" +"올바르지 않은 규칙: %(identity_value)s. 'groups' 및 'domain' 키워드가 둘 다 " +"지정되어야 합니다." + +msgid "Invalid signature" +msgstr "올바르지 않은 서명" + +#, python-format +msgid "" +"Invalid ssl_cert_reqs value of %s, must be one of \"NONE\", \"OPTIONAL\", " +"\"REQUIRED\"" +msgstr "" +"%s의 ssl_cert_reqs 값이 올바르지 않음, \"NONE\", \"OPTIONAL\", \"REQUIRED\" " +"중 하나여야 함 " + +msgid "Invalid user / password" +msgstr "올바르지 않은 사용자 / 비밀번호" + +msgid "Invalid username or password" +msgstr "올바르지 않은 사용자 이름 또는 비밀번호" + +#, python-format +msgid "KVS region %s is already configured. Cannot reconfigure." +msgstr "KVS 리젼 %s이(가) 이미 구성되어 있습니다. 재구성할 수 없습니다." + +#, python-format +msgid "Key Value Store not configured: %s" +msgstr "키 값 저장소가 구성되지 않음: %s" + +#, python-format +msgid "LDAP %s create" +msgstr "LDAP %s 작성" + +#, python-format +msgid "LDAP %s delete" +msgstr "LDAP %s 삭제" + +#, python-format +msgid "LDAP %s update" +msgstr "LDAP %s 업데이트" + +#, python-format +msgid "Lock Timeout occurred for key, %(target)s" +msgstr "키 %(target)s에 대해 잠금 제한시간 초과가 발생함" + +#, python-format +msgid "Lock key must match target key: %(lock)s != %(target)s" +msgstr "잠금 키가 대상 키와 일치해야 함: %(lock)s != %(target)s" + +#, python-format +msgid "Malformed endpoint URL (%(endpoint)s), see ERROR log for details." +msgstr "" +"잘못된 형식의 엔드포인트 URL(%(endpoint)s). 세부사항은 오류 로그를 참조하십시" +"오." + +msgid "Marker could not be found" +msgstr "마커를 찾을 수 없음" + +#, python-format +msgid "Maximum lock attempts on %s occurred." +msgstr "%s에서 최대 잠금 시도가 발생했습니다." + +#, python-format +msgid "Member %(member)s is already a member of group %(group)s" +msgstr "%(member)s 구성원은 이미 %(group)s 그룹의 구성원임" + +#, python-format +msgid "Method not callable: %s" +msgstr "메소드를 호출할 수 없음: %s" + +msgid "Missing entity ID from environment" +msgstr "환경에서 엔티티 ID가 누락됨" + +msgid "" +"Modifying \"redelegation_count\" upon redelegation is forbidden. Omitting " +"this parameter is advised." +msgstr "" +"재위임 시 \"redelegation_count\"를 수정할 수 없습니다. 이 매개변수는 생략하" +"는 것이 좋습니다." + +msgid "Multiple domains are not supported" +msgstr "여러 도메인이 지원되지 않음" + +msgid "Must be called within an active lock context." +msgstr "활성 잠금 컨텍스트 내에서 호출되어야 합니다." + +msgid "Must specify either domain or project" +msgstr "도메인 프로젝트 중 하나를 지정해야 함" + +msgid "Name field is required and cannot be empty" +msgstr "이름 필드가 필요하며 비어 있을 수 없음" + +msgid "" +"No Authorization headers found, cannot proceed with OAuth related calls, if " +"running under HTTPd or Apache, ensure WSGIPassAuthorization is set to On." +msgstr "" +"권한 부여 헤더를 찾을 수 없습니다. HTTPd 또는 Apache에서 실행 중인 경우 " +"OAuth 관련 호출을 사용하여 계속 진행할 수 없습니다. WSGIPassAuthorization이 " +"On으로 설정되어 있는지 확인하십시오." + +msgid "No authenticated user" +msgstr "인증된 사용자가 없음" + +msgid "" +"No encryption keys found; run keystone-manage fernet_setup to bootstrap one." +msgstr "" +"암호화 키를 찾을 수 없음: keystone-manage fernet_setup을 부트스트랩 1로 실행" +"하십시오." + +msgid "No options specified" +msgstr "지정된 옵션 없음" + +#, python-format +msgid "No policy is associated with endpoint %(endpoint_id)s." +msgstr "엔드포인트 %(endpoint_id)s과(와) 연관된 정책이 없습니다." + +#, python-format +msgid "No remaining uses for trust: %(trust_id)s" +msgstr "신뢰 %(trust_id)s에 대해 남아 있는 사용이 없음" + +msgid "Non-default domain is not supported" +msgstr "기본이 아닌 도메인은 지원되지 않음" + +msgid "One of the trust agents is disabled or deleted" +msgstr "신뢰 에이전트 중 하나가 사용 안함으로 설정되었거나 삭제됨" + +#, python-format +msgid "" +"Option %(option)s found with no group specified while checking domain " +"configuration request" +msgstr "" +"%(option)s 옵션은 도메인 구성 요청 확인 중에 지정된 그룹이 없음을 발견함" + +#, python-format +msgid "" +"Option %(option)s in group %(group)s is not supported for domain specific " +"configurations" +msgstr "" +"도메인 특정 구성에 대해 %(group)s 그룹의 %(option)s 옵션이 지원되지않음" + +#, python-format +msgid "Project (%s)" +msgstr "프로젝트(%s)" + +#, python-format +msgid "Project is disabled: %s" +msgstr "프로젝트를 사용 안함: %s" + +msgid "Redelegation allowed for delegated by trust only" +msgstr "신뢰에서 위임한 경우에만 재위임 허용" + +#, python-format +msgid "" +"Remaining redelegation depth of %(redelegation_depth)d out of allowed range " +"of [0..%(max_count)d]" +msgstr "" +"%(redelegation_depth)d의 나머지 재위임 깊이가 허용 범위 [0..%(max_count)d]을" +"(를) 벗어남" + +msgid "Request Token does not have an authorizing user id" +msgstr "요청 토큰에 인증하는 사용자 ID가 없음" + +#, python-format +msgid "" +"Request attribute %(attribute)s must be less than or equal to %(size)i. The " +"server could not comply with the request because the attribute size is " +"invalid (too large). The client is assumed to be in error." +msgstr "" +"요청 속성 %(attribute)s이(가) %(size)i 이하여야 합니다. 속성 크기가 올바르지 " +"않기 때문에(너무 큼) 서버가 요청을 준수할 수 없습니다. 클라이언트가 오류 상태" +"로 간주됩니다." + +msgid "Request must have an origin query parameter" +msgstr "요청에는 원본 조회 매개변수가 있어야 함" + +msgid "Request token is expired" +msgstr "요청 토큰이 만료됨" + +msgid "Request token not found" +msgstr "요청 토큰을 찾을 수 없음" + +msgid "Requested expiration time is more than redelegated trust can provide" +msgstr "요청된 만기 시간이 재위임된 신뢰에서 제공할 수 있는 시간보다 큼" + +#, python-format +msgid "" +"Requested redelegation depth of %(requested_count)d is greater than allowed " +"%(max_count)d" +msgstr "" +"%(requested_count)d의 요청된 재위임 깊이가 허용되는 %(max_count)d보다 깊음" + +#, python-format +msgid "Role %s not found" +msgstr "%s 역할을 찾을 수 없음" + +msgid "" +"Running keystone via eventlet is deprecated as of Kilo in favor of running " +"in a WSGI server (e.g. mod_wsgi). Support for keystone under eventlet will " +"be removed in the \"M\"-Release." +msgstr "" +"eventlet을 통한 키스톤 실행은 WSGI 서버 실행의 플레이버에 있는 Kilo부터 더 " +"이상 사용되지 않습니다(예: mod_wsgi). eventlet 아래의 키스톤에 대한 지원은 " +"\"M\"-릴리스에서 제거됩니다." + +msgid "Scoping to both domain and project is not allowed" +msgstr "도메인과 프로젝트에 대한 범위 지정이 허용되지 않음" + +msgid "Scoping to both domain and trust is not allowed" +msgstr "도메인과 신뢰에 대한 범위 지정이 허용되지 않음" + +msgid "Scoping to both project and trust is not allowed" +msgstr "프로젝트와 신뢰에 대한 범위 지정이 허용되지 않음" + +#, python-format +msgid "Service Provider %(sp)s is disabled" +msgstr "서비스 제공자 %(sp)s이(가) 사용 안함으로 설정됨" + +msgid "Some of requested roles are not in redelegated trust" +msgstr "요청된 일부 역할이 재위임된 신뢰에 없음" + +msgid "Specify a domain or project, not both" +msgstr "도메인 또는 프로젝트 중 하나 지정" + +msgid "Specify a user or group, not both" +msgstr "사용자 또는 그룹 중 하나 지정" + +msgid "Specify one of domain or project" +msgstr "도메인 또는 프로젝트 중 하나 지정" + +msgid "Specify one of user or group" +msgstr "사용자 또는 그룹 중 하나 지정" + +#, python-format +msgid "" +"String length exceeded.The length of string '%(string)s' exceeded the limit " +"of column %(type)s(CHAR(%(length)d))." +msgstr "" +"문자열 길이 제한을 초과합니다. '%(string)s' 문자열 길이가 열의 한도 " +"%(type)s(CHAR(%(length)d))을(를) 초과합니다." + +msgid "The --all option cannot be used with the --domain-name option" +msgstr "--all 옵션은 --domain-name 옵션과 함께 사용할 수 없습니다." + +#, python-format +msgid "The Keystone configuration file %(config_file)s could not be found." +msgstr "키스톤 구성 파일 %(config_file)s을(를) 찾을 수 없습니다." + +#, python-format +msgid "" +"The Keystone domain-specific configuration has specified more than one SQL " +"driver (only one is permitted): %(source)s." +msgstr "" +"키스톤 도메인 특정 구성에 하나 이상의 SQL 드라이버가 지정됨(하나만 허용됨): " +"%(source)s." + +msgid "The action you have requested has not been implemented." +msgstr "요청한 조치가 구현되지 않았습니다." + +msgid "The authenticated user should match the trustor." +msgstr "인증된 사용자는 trustor와 일치해야 합니다." + +msgid "" +"The certificates you requested are not available. It is likely that this " +"server does not use PKI tokens otherwise this is the result of " +"misconfiguration." +msgstr "" +"요청한 인증서를 사용할 수 없습니다. 서버가 PKI 토큰을 사용하지 않거나 잘못된 " +"구성의 결과로 인해 발생했을 수 있습니다." + +#, python-format +msgid "" +"The password length must be less than or equal to %(size)i. The server could " +"not comply with the request because the password is invalid." +msgstr "" +"비밀번호 길이는 %(size)i 이하여야 합니다. 비밀번호가 올바르지 않아 서버가 요" +"청을 준수할 수 없습니다." + +msgid "The request you have made requires authentication." +msgstr "요청에 인증이 필요합니다." + +msgid "The resource could not be found." +msgstr "자원을 찾을 수 없습니다. " + +msgid "" +"The revoke call must not have both domain_id and project_id. This is a bug " +"in the Keystone server. The current request is aborted." +msgstr "" +"취소 호출은 domain_id와 project_id가 둘 다 있으면 안됩니다.키스톤 서버에서 이" +"는 버그입니다. 현재 요청이 중단됩니다." + +msgid "The service you have requested is no longer available on this server." +msgstr "요청한 서비스를 더 이상 이 서버에서 사용할 수 없습니다." + +#, python-format +msgid "" +"The specified parent region %(parent_region_id)s would create a circular " +"region hierarchy." +msgstr "지정된 상위 리젼 %(parent_region_id)s에서 순환 리젼 계층을 작성합니다." + +#, python-format +msgid "" +"The value of group %(group)s specified in the config should be a dictionary " +"of options" +msgstr "구성에 지정된 %(group)s 그룹의 값은 옵션의 사전이어야 함" + +msgid "There should not be any non-oauth parameters" +msgstr "non-oauth 매개변수가 없어야 함" + +#, python-format +msgid "This is not a recognized Fernet payload version: %s" +msgstr "인식되는 Fernet 페이로드 버전이 아님: %s" + +msgid "" +"This is not a v2.0 Fernet token. Use v3 for trust, domain, or federated " +"tokens." +msgstr "" +"이는 v2.0 Fernet 토큰이 아닙니다. 신뢰, 도메인 또는 연합 토큰의 경우 v3를 사" +"용하십시오." + +msgid "" +"Timestamp not in expected format. The server could not comply with the " +"request since it is either malformed or otherwise incorrect. The client is " +"assumed to be in error." +msgstr "" +"시간소인이 예상된 형식이 아닙니다. 잘못 구성되었거나 올바르지 않으므로 서버" +"가 요청을 준수할 수 없습니다. 클라이언트가 오류 상태로 간주됩니다." + +#, python-format +msgid "" +"To get a more detailed information on this error, re-run this command for " +"the specific domain, i.e.: keystone-manage domain_config_upload --domain-" +"name %s" +msgstr "" +"이 오류에 대한 자세한 정보를 보려면 특정 도메인에 대해 이 명령을 다시 실행하" +"십시오. 예: keystone-manage domain_config_upload --domain-name %s" + +msgid "Token belongs to another user" +msgstr "토큰이 다른 사용자에 속함" + +msgid "Token does not belong to specified tenant." +msgstr "토큰이 지정된 테넌트에 속하지 않습니다." + +msgid "Trustee has no delegated roles." +msgstr "Trustee에 위임된 역할이 없습니다. " + +msgid "Trustor is disabled." +msgstr "Trustor를 사용하지 않습니다. " + +#, python-format +msgid "" +"Trying to update group %(group)s, so that, and only that, group must be " +"specified in the config" +msgstr "구성에서 그룹만 지정되도록 %(group)s 그룹을 업데이트하려고 합니다. " + +#, python-format +msgid "" +"Trying to update option %(option)s in group %(group)s, but config provided " +"contains option %(option_other)s instead" +msgstr "" +"%(group)s 그룹에서 %(option)s 옵션을 업데이트하려고 했지만 제공된 구성에 " +"%(option_other)s 옵션이 대신 포함되어 있습니다." + +#, python-format +msgid "" +"Trying to update option %(option)s in group %(group)s, so that, and only " +"that, option must be specified in the config" +msgstr "" +"구성에서 옵션만 지정되도록 %(group)s 그룹에서 %(option)s 옵션을 업데이트하려" +"고 합니다." + +msgid "" +"Unable to access the keystone database, please check it is configured " +"correctly." +msgstr "" +"키스톤 데이터베이스를 액세스할 수 없습니다. 데이터베이스가 제대로 구성되어 있" +"는지 확인하십시오. " + +#, python-format +msgid "Unable to consume trust %(trust_id)s, unable to acquire lock." +msgstr "%(trust_id)s 신뢰를 이용할 수 없어서 잠금을 획득할 수 없습니다." + +#, python-format +msgid "" +"Unable to delete region %(region_id)s because it or its child regions have " +"associated endpoints." +msgstr "" +"리젼 %(region_id)s 또는 하위 리젼에 연관된 엔드포인트가 있어 삭제할 수 없습니" +"다." + +#, python-format +msgid "Unable to find valid groups while using mapping %(mapping_id)s" +msgstr "%(mapping_id)s 맵핑을 사용하는 중에 올바른 그룹을 찾을 수 없음 " + +#, python-format +msgid "" +"Unable to get a connection from pool id %(id)s after %(seconds)s seconds." +msgstr "풀 id %(id)s에서 %(seconds)s분 후에 연결할 수 없습니다." + +#, python-format +msgid "Unable to locate domain config directory: %s" +msgstr "%s: 도메인 설정 디렉토리를 찾을 수 없습니다." + +#, python-format +msgid "Unable to lookup user %s" +msgstr "%s 사용자를 검색할 수 없음" + +#, python-format +msgid "" +"Unable to reconcile identity attribute %(attribute)s as it has conflicting " +"values %(new)s and %(old)s" +msgstr "" +"ID 속성 %(attribute)s에 서로 충돌하는 %(new)s 및 %(old)s 값이 있으므로 이 ID " +"속성을 조정할 수 없음" + +#, python-format +msgid "" +"Unable to sign SAML assertion. It is likely that this server does not have " +"xmlsec1 installed, or this is the result of misconfiguration. Reason " +"%(reason)s" +msgstr "" +"SAML 어설션에 서명할 수 없습니다. 이 서버에 xmlsec1이 설치되지 않았거나 잘못 " +"구성된 결과입니다. 이유%(reason)s" + +msgid "Unable to sign token." +msgstr "토큰을 부호화할 수 없습니다." + +#, python-format +msgid "Unexpected assignment type encountered, %s" +msgstr "예상치 못한 지정 유형 발생, %s" + +#, python-format +msgid "" +"Unexpected combination of grant attributes - User: %(user_id)s, Group: " +"%(group_id)s, Project: %(project_id)s, Domain: %(domain_id)s" +msgstr "" +"grant 속성의 예상치 못한 조합 - 사용자: %(user_id)s, 그룹: %(group_id)s, 프로" +"젝트: %(project_id)s, 도메인: %(domain_id)s" + +#, python-format +msgid "Unexpected status requested for JSON Home response, %s" +msgstr "JSON 홈 응답에 대해 예상치 못한 상태가 요청됨. %s" + +msgid "Unknown Target" +msgstr "알 수 없는 대상" + +#, python-format +msgid "Unknown domain '%(name)s' specified by --domain-name" +msgstr "--domain-name으로 알 수 없는 도메인 '%(name)s'을(를) 지정했음" + +#, python-format +msgid "Unknown token version %s" +msgstr "알 수 없는 토큰 버전 %s" + +#, python-format +msgid "Unregistered dependency: %(name)s for %(targets)s" +msgstr "등록되지 않은 종속성: %(targets)s의 %(name)s" + +msgid "Update of `parent_id` is not allowed." +msgstr "`parent_id` 업데이트가 허용되지 않습니다." + +msgid "Use a project scoped token when attempting to create a SAML assertion" +msgstr "SAML 어설션을 작성할 때 프로젝트 범위 지정 토큰 사용" + +#, python-format +msgid "User %(u_id)s is unauthorized for tenant %(t_id)s" +msgstr "사용자 %(u_id)s이(는) 테넌트 %(t_id)s에 대한 권한이 없습니다. " + +#, python-format +msgid "User %(user_id)s already has role %(role_id)s in tenant %(tenant_id)s" +msgstr "" +"사용자 %(user_id)s이(가) 테넌트 %(tenant_id)s에서 역할 %(role_id)s을(를) 이" +"미 가집니다. " + +#, python-format +msgid "User %(user_id)s has no access to domain %(domain_id)s" +msgstr "" +"%(user_id)s 사용자는 %(domain_id)s 도메인에 대한 액세스 권한이 없습니다. " + +#, python-format +msgid "User %(user_id)s has no access to project %(project_id)s" +msgstr "" +"%(user_id)s 사용자는 %(project_id)s 프로젝트에 대한 액세스 권한이 없습니다. " + +#, python-format +msgid "User %(user_id)s is already a member of group %(group_id)s" +msgstr "%(user_id)s 사용자는 이미 %(group_id)s 그룹의 구성원임" + +#, python-format +msgid "User '%(user_id)s' not found in group '%(group_id)s'" +msgstr "'%(group_id)s' 그룹에 '%(user_id)s' 사용자가 없음" + +msgid "User IDs do not match" +msgstr "사용자 ID가 일치하지 않음" + +#, python-format +msgid "User is disabled: %s" +msgstr "사용자를 사용 안함: %s" + +msgid "User is not a member of the requested project" +msgstr "사용자가 요청한 프로젝트의 구성원이 아님" + +msgid "User is not a trustee." +msgstr "사용자는 trustee가 아닙니다." + +msgid "User not found" +msgstr "사용자를 찾을 수 없음" + +#, python-format +msgid "User type %s not supported" +msgstr "사용자 유형 %s이(가) 지원되지 않음" + +msgid "You are not authorized to perform the requested action." +msgstr "요청한 조치를 수행할 권한이 없습니다." + +#, python-format +msgid "You are not authorized to perform the requested action: %(action)s" +msgstr "요청한 조치(%(action)s)를 수행할 권한이 없습니다." + +msgid "`key_mangler` functions must be callable." +msgstr "`key_mangler` 기능을 호출할 수 있어야 합니다." + +msgid "`key_mangler` option must be a function reference" +msgstr "`key_mangler` 옵션은 기능 참조여야 함" + +msgid "any options" +msgstr "옵션" + +msgid "auth_type is not Negotiate" +msgstr "auth_type이 Negotiate가 아님" + +msgid "authorizing user does not have role required" +msgstr "인증하는 사용자에게 필요한 역할이 없음" + +msgid "cache_collection name is required" +msgstr "cache_collection 이름이 필요함" + +#, python-format +msgid "cannot create a project in a branch containing a disabled project: %s" +msgstr "" +"사용 안함으로 설정된 프로젝트가 포함된 분기에 프로젝트를 작성할 수 없습니다. " +"%s" + +msgid "cannot create a project within a different domain than its parents." +msgstr "상위와 다른 도메인 내에 프로젝트를 작성할 수 없습니다." + +msgid "cannot delete a domain that is enabled, please disable it first." +msgstr "" +"사용으로 설정된 도메인을 삭제할 수 없습니다. 먼저 해당 도메인을 사용 안함으" +"로 설정하십시오." + +#, python-format +msgid "cannot delete the project %s since it is not a leaf in the hierarchy." +msgstr "계층 내의 리프가 아니므로 프로젝트 %s을(를) 삭제할 수 없습니다." + +#, python-format +msgid "cannot disable project %s since its subtree contains enabled projects" +msgstr "" +"서브트리에 사용 설정된 프로젝트가 있어서 프로젝트 %s을(를) 사용 안함으로 설정" +"할 수 없음" + +#, python-format +msgid "cannot enable project %s since it has disabled parents" +msgstr "프로젝트 %s에 사용 안함으로 설정된 상위가 있어서 이를 사용할 수 없음" + +msgid "database db_name is required" +msgstr "database db_name이 필요함" + +msgid "db_hosts value is required" +msgstr "db_hosts 값이 필요함" + +msgid "delete the default domain" +msgstr "기본 도메인 삭제" + +#, python-format +msgid "group %(group)s" +msgstr "%(group)s 그룹" + +msgid "" +"idp_contact_type must be one of: [technical, other, support, administrative " +"or billing." +msgstr "" +"idp_contact_type은 [기술, 기타, 지원, 관리 또는 비용 청구 중 하나여야 합니다." + +msgid "integer value expected for mongo_ttl_seconds" +msgstr "mongo_ttl_seconds에 대해 정수 값이 예상됨 " + +msgid "integer value expected for w (write concern attribute)" +msgstr "w(write concern 속성)에 대해 정수 값이 예상됨" + +#, python-format +msgid "invalid date format %s" +msgstr "올바르지 않은 날짜 형식 %s" + +#, python-format +msgid "max hierarchy depth reached for %s branch." +msgstr "%s 분기에 대한 최대 계층 깊이에 도달했습니다." + +msgid "no ssl support available" +msgstr "사용 가능한 ssl 지원이 없음" + +#, python-format +msgid "option %(option)s in group %(group)s" +msgstr "%(group)s 그룹의 %(option)s 옵션" + +msgid "pad must be single character" +msgstr "패드는 단일 문자여야 함" + +msgid "padded base64url text must be multiple of 4 characters" +msgstr "채워진 base64url 텍스트는 4의 배수여야 함" + +msgid "provided consumer key does not match stored consumer key" +msgstr "제공된 이용자 키가 저장된 이용자 키와 일치하지 않음" + +msgid "provided request key does not match stored request key" +msgstr "제공된 요청 키가 저장된 요청 키와 일치하지 않음" + +msgid "provided verifier does not match stored verifier" +msgstr "제공된 확인자가 저장된 확인자와 일치하지 않음 " + +msgid "region not type dogpile.cache.CacheRegion" +msgstr "리젼이 dogpile.cache.CacheRegion 유형이 아님 " + +msgid "remaining_uses must be a positive integer or null." +msgstr "remaining_uses는 양의 정수 또는 널이어야 합니다." + +msgid "remaining_uses must not be set if redelegation is allowed" +msgstr "재위임을 허용하는 경우 remaining_uses를 설정하지 않아야 함" + +msgid "replicaset_name required when use_replica is True" +msgstr "use_replica가 True인 경우 replicaset_name이 필요함 " + +#, python-format +msgid "" +"request to update group %(group)s, but config provided contains group " +"%(group_other)s instead" +msgstr "" +"%(group)s 그룹을 업데이트하도록 요청했지만 제공된 구성에 %(group_other)s 그룹" +"이 대신 포함되어 있습니다." + +msgid "rescope a scoped token" +msgstr "범위 지정된 토큰의 범위 재지정" + +#, python-format +msgid "text is multiple of 4, but pad \"%s\" occurs before 2nd to last char" +msgstr "" +"텍스트가 4의 배수이지만 패드 \"%s\"이(가) 두 번째 앞에서부터 마지막 문자까지 " +"발생함" + +#, python-format +msgid "text is multiple of 4, but pad \"%s\" occurs before non-pad last char" +msgstr "" +"텍스트가 4의 배수이지만 패드 \"%s\"이(가) 비패드 마지막 문자 앞에서 발생함" + +#, python-format +msgid "text is not a multiple of 4, but contains pad \"%s\"" +msgstr "텍스트가 4의 배수이지만 패드 \"%s\"을(를) 포함함" + +#, python-format +msgid "tls_cacertdir %s not found or is not a directory" +msgstr "tls_cacertdir %s를 찾을 수 없으며, 이 디렉토리에 존재하지 않습니다." + +#, python-format +msgid "tls_cacertfile %s not found or is not a file" +msgstr "tls_cacertfile %s를 찾을 수 없스며, 그런 파일이 없습니다." + +#, python-format +msgid "token reference must be a KeystoneToken type, got: %s" +msgstr "토큰 참조는 KeystoneToken 유형이어야 합니다. %s을(를) 가져왔습니다." diff --git a/keystone-moon/keystone/locale/pl_PL/LC_MESSAGES/keystone-log-critical.po b/keystone-moon/keystone/locale/pl_PL/LC_MESSAGES/keystone-log-critical.po index 43b0dc54..0d4ec217 100644 --- a/keystone-moon/keystone/locale/pl_PL/LC_MESSAGES/keystone-log-critical.po +++ b/keystone-moon/keystone/locale/pl_PL/LC_MESSAGES/keystone-log-critical.po @@ -3,22 +3,24 @@ # This file is distributed under the same license as the keystone project. # # Translators: +# OpenStack Infra <zanata@openstack.org>, 2015. #zanata msgid "" msgstr "" -"Project-Id-Version: Keystone\n" +"Project-Id-Version: keystone 8.0.0.0b4.dev56\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" -"POT-Creation-Date: 2015-08-06 06:28+0000\n" -"PO-Revision-Date: 2014-08-31 15:19+0000\n" +"POT-Creation-Date: 2015-09-21 06:08+0000\n" +"PO-Revision-Date: 2014-08-31 03:19+0000\n" "Last-Translator: openstackjenkins <jenkins@openstack.org>\n" "Language-Team: Polish (Poland) (http://www.transifex.com/openstack/keystone/" "language/pl_PL/)\n" -"Language: pl_PL\n" +"Language: pl-PL\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" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2);\n" +"X-Generator: Zanata 3.7.1\n" #, python-format msgid "Unable to open template file %s" diff --git a/keystone-moon/keystone/locale/pt_BR/LC_MESSAGES/keystone-log-critical.po b/keystone-moon/keystone/locale/pt_BR/LC_MESSAGES/keystone-log-critical.po index 48e0c8c7..cda7507a 100644 --- a/keystone-moon/keystone/locale/pt_BR/LC_MESSAGES/keystone-log-critical.po +++ b/keystone-moon/keystone/locale/pt_BR/LC_MESSAGES/keystone-log-critical.po @@ -3,21 +3,23 @@ # This file is distributed under the same license as the keystone project. # # Translators: +# OpenStack Infra <zanata@openstack.org>, 2015. #zanata msgid "" msgstr "" -"Project-Id-Version: Keystone\n" +"Project-Id-Version: keystone 8.0.0.0b4.dev56\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" -"POT-Creation-Date: 2015-08-06 06:28+0000\n" -"PO-Revision-Date: 2014-08-31 15:19+0000\n" +"POT-Creation-Date: 2015-09-21 06:08+0000\n" +"PO-Revision-Date: 2014-08-31 03:19+0000\n" "Last-Translator: openstackjenkins <jenkins@openstack.org>\n" "Language-Team: Portuguese (Brazil) (http://www.transifex.com/openstack/" "keystone/language/pt_BR/)\n" -"Language: pt_BR\n" +"Language: pt-BR\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" "Plural-Forms: nplurals=2; plural=(n > 1);\n" +"X-Generator: Zanata 3.7.1\n" #, python-format msgid "Unable to open template file %s" diff --git a/keystone-moon/keystone/locale/pt_BR/LC_MESSAGES/keystone-log-error.po b/keystone-moon/keystone/locale/pt_BR/LC_MESSAGES/keystone-log-error.po index 12e4591f..f56ca03e 100644 --- a/keystone-moon/keystone/locale/pt_BR/LC_MESSAGES/keystone-log-error.po +++ b/keystone-moon/keystone/locale/pt_BR/LC_MESSAGES/keystone-log-error.po @@ -3,21 +3,23 @@ # This file is distributed under the same license as the keystone project. # # Translators: +# OpenStack Infra <zanata@openstack.org>, 2015. #zanata msgid "" msgstr "" -"Project-Id-Version: Keystone\n" +"Project-Id-Version: keystone 8.0.0.0b4.dev56\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" -"POT-Creation-Date: 2015-08-06 06:28+0000\n" -"PO-Revision-Date: 2015-06-26 17:13+0000\n" +"POT-Creation-Date: 2015-09-21 06:08+0000\n" +"PO-Revision-Date: 2015-06-26 05:13+0000\n" "Last-Translator: openstackjenkins <jenkins@openstack.org>\n" "Language-Team: Portuguese (Brazil) (http://www.transifex.com/openstack/" "keystone/language/pt_BR/)\n" -"Language: pt_BR\n" +"Language: pt-BR\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" "Plural-Forms: nplurals=2; plural=(n > 1);\n" +"X-Generator: Zanata 3.7.1\n" msgid "" "Error setting up the debug environment. Verify that the option --debug-url " diff --git a/keystone-moon/keystone/locale/pt_BR/LC_MESSAGES/keystone.po b/keystone-moon/keystone/locale/pt_BR/LC_MESSAGES/keystone.po index 02ff0550..9adc7d82 100644 --- a/keystone-moon/keystone/locale/pt_BR/LC_MESSAGES/keystone.po +++ b/keystone-moon/keystone/locale/pt_BR/LC_MESSAGES/keystone.po @@ -6,12 +6,14 @@ # Gabriel Wainer, 2013 # Lucas Ribeiro <lucasribeiro1990@gmail.com>, 2014 # Volmar Oliveira Junior <volmar.oliveira.jr@gmail.com>, 2013 +# Lucas Palm <lapalm@us.ibm.com>, 2015. #zanata +# OpenStack Infra <zanata@openstack.org>, 2015. #zanata msgid "" msgstr "" -"Project-Id-Version: Keystone\n" +"Project-Id-Version: keystone 9.0.0.dev14\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" -"POT-Creation-Date: 2015-08-06 06:28+0000\n" -"PO-Revision-Date: 2015-08-04 18:01+0000\n" +"POT-Creation-Date: 2015-09-24 06:09+0000\n" +"PO-Revision-Date: 2015-09-03 12:54+0000\n" "Last-Translator: openstackjenkins <jenkins@openstack.org>\n" "Language-Team: Portuguese (Brazil) (http://www.transifex.com/openstack/" "keystone/language/pt_BR/)\n" @@ -22,6 +24,32 @@ msgstr "" "Generated-By: Babel 2.0\n" #, python-format +msgid "%(detail)s" +msgstr "%(detail)s" + +#, python-format +msgid "" +"%(event)s is not a valid notification event, must be one of: %(actions)s" +msgstr "" +"%(event)s não é um evento de notificação válido, deve ser um de: %(actions)s" + +#, python-format +msgid "%(host)s is not a trusted dashboard host" +msgstr "%(host)s não é um host do painel confiável" + +#, python-format +msgid "%(message)s %(amendment)s" +msgstr "%(message)s %(amendment)s" + +#, python-format +msgid "" +"%(mod_name)s doesn't provide database migrations. The migration repository " +"path at %(path)s doesn't exist or isn't a directory." +msgstr "" +"%(mod_name)s não fornece migrações de banco de dados. O caminho do " +"repositório de migração %(path)s não existe ou não é um diretório." + +#, python-format msgid "%(property_name)s cannot be less than %(min_length)s characters." msgstr "%(property_name)s não pode ter menos de %(min_length)s caracteres." @@ -37,6 +65,29 @@ msgstr "%(property_name)s não deve ter mais de %(max_length)s caracteres." msgid "%s cannot be empty." msgstr "%s não pode estar vazio." +#, python-format +msgid "%s extension does not exist." +msgstr "Extensão %s não existe." + +#, python-format +msgid "%s field is required and cannot be empty" +msgstr "campo %s é obrigatório e não pode estar vazio" + +#, python-format +msgid "%s field(s) cannot be empty" +msgstr "%s campo(s) não podem estar vazios" + +msgid "(Disable debug mode to suppress these details.)" +msgstr "(Desative o modo de depuração para suprimir esses detalhes.)" + +msgid "--all option cannot be mixed with other options" +msgstr "A opção --all não pode ser combinada com outras opções" + +msgid "A project-scoped token is required to produce a service catalog." +msgstr "" +"Um token de projeto com escopo é necessário para produzir um catálogo de " +"serviços." + msgid "Access token is expired" msgstr "Token de acesso expirou" @@ -46,47 +97,301 @@ msgstr "Token de acesso não encontrado" msgid "Additional authentications steps required." msgstr "Passos de autenticação adicionais requeridos." +msgid "An unexpected error occurred when retrieving domain configs" +msgstr "Ocorreu um erro inesperado ao recuperar as configurações de domínio" + +#, python-format +msgid "An unexpected error occurred when trying to store %s" +msgstr "Ocorreu um erro inesperado ao tentar armazenar %s" + +msgid "An unexpected error prevented the server from fulfilling your request." +msgstr "Um erro inesperado evitou que o servidor cumprisse sua solicitação." + +#, python-format +msgid "" +"An unexpected error prevented the server from fulfilling your request: " +"%(exception)s" +msgstr "" +"Um erro inesperado evitou que o servidor cumprisse sua solicitação: " +"%(exception)s" + msgid "An unhandled exception has occurred: Could not find metadata." msgstr "Uma exceção não tratada ocorreu: Não foi possível encontrar metadados." +msgid "At least one option must be provided" +msgstr "Pelo menos uma opção deve ser fornecida" + +msgid "At least one option must be provided, use either --all or --domain-name" +msgstr "Pelo menos uma opção deve ser fornecida, use --all ou --domain-name" + +msgid "At least one role should be specified." +msgstr "Pelo menos uma função deve ser especificada." + msgid "Attempted to authenticate with an unsupported method." msgstr "Tentativa de autenticação com um método não suportado." +msgid "" +"Attempting to use OS-FEDERATION token with V2 Identity Service, use V3 " +"Authentication" +msgstr "" +"Ao tentar usar o token OS-FEDERATION com Serviço de identidade V2, use " +"autenticação V3" + msgid "Authentication plugin error." msgstr "Erro do plugin de autenticação." +msgid "Cannot authorize a request token with a token issued via delegation." +msgstr "" +"Não é possível autorizar um token de solicitação com um token emitido por " +"meio de delegação." + #, python-format msgid "Cannot change %(option_name)s %(attr)s" msgstr "Não é possível alterar %(option_name)s %(attr)s" +msgid "Cannot change Domain ID" +msgstr "Não é possível alterar o ID do Domínio" + msgid "Cannot change consumer secret" msgstr "Não é possível alterar segredo do consumidor" +msgid "Cannot change user ID" +msgstr "Não é possível alterar o ID do usuário" + +msgid "Cannot change user name" +msgstr "Não é possível alterar o nome de usuário" + +#, python-format +msgid "Cannot create project with parent: %(project_id)s" +msgstr "Não é possível criar o projeto com o pai: %(project_id)s" + +#, python-format +msgid "Cannot duplicate name %s" +msgstr "Não é possível duplicar o nome %s" + +msgid "Cannot list request tokens with a token issued via delegation." +msgstr "" +"Não é possível listar os tokens de solicitação com um token emitido por meio " +"de delegação." + +#, python-format +msgid "Cannot open certificate %(cert_file)s. Reason: %(reason)s" +msgstr "Não é possível abrir o certificado %(cert_file)s. Motivo: %(reason)s" + #, python-format msgid "Cannot remove role that has not been granted, %s" msgstr "Não é possível remover role que não foi concedido, %s" +msgid "" +"Cannot truncate a driver call without hints list as first parameter after " +"self " +msgstr "" +"Não é possível truncar uma chamada de driver sem lista de sugestões como " +"primeiro parâmetro após self " + +msgid "" +"Cannot use parents_as_list and parents_as_ids query params at the same time." +msgstr "" +"Não é possível usar parâmetros de consulta parents_as_list e parents_as_ids " +"ao mesmo tempo." + +msgid "" +"Cannot use subtree_as_list and subtree_as_ids query params at the same time." +msgstr "" +"Não é possível usar parâmetros de consulta subtree_as_list e subtree_as_ids " +"ao mesmo tempo." + +msgid "" +"Combining effective and group filter will always result in an empty list." +msgstr "" +"Combinar efetivo e filtro de grupo sempre resultará em uma lista vazia." + +msgid "" +"Combining effective, domain and inherited filters will always result in an " +"empty list." +msgstr "" +"Combinar efetivo, domínio e filtros herdados sempre resultará em uma lista " +"vazia." + +#, python-format +msgid "Conflict occurred attempting to store %(type)s - %(details)s" +msgstr "Ocorreu um conflito ao tentar armazenar %(type)s -%(details)s" + +#, python-format +msgid "Conflicting region IDs specified: \"%(url_id)s\" != \"%(ref_id)s\"" +msgstr "" +"IDs de região de conflito especificados: \"%(url_id)s\" != \"%(ref_id)s\"" + msgid "Consumer not found" msgstr "Consumidor não encontrado" +#, python-format +msgid "" +"Could not change immutable attribute(s) '%(attributes)s' in target %(target)s" +msgstr "" +"Não foi possível alterar o atributo imutável '%(attributes)s' no destino " +"%(target)s" + +#, python-format +msgid "" +"Could not find %(group_or_option)s in domain configuration for domain " +"%(domain_id)s" +msgstr "" +"Não foi possível localizar %(group_or_option)s na configuração de domínio " +"para o domínio %(domain_id)s" + +#, python-format +msgid "Could not find Endpoint Group: %(endpoint_group_id)s" +msgstr "Não foi possível localizar o Grupo do Terminal: %(endpoint_group_id)s" + +msgid "Could not find Identity Provider identifier in environment" +msgstr "" +"Não foi possível localizar o identificador do Provedor de Identidade no " +"ambiente" + +#, python-format +msgid "Could not find Identity Provider: %(idp_id)s" +msgstr "Não foi possível localizar o Provedor de Identidade: %(idp_id)s" + +#, python-format +msgid "Could not find Service Provider: %(sp_id)s" +msgstr "Não foi possível localizar o Provedor de Serviços: %(sp_id)s" + +#, python-format +msgid "Could not find credential: %(credential_id)s" +msgstr "Não foi possível localizar a credencial: %(credential_id)s" + +#, python-format +msgid "Could not find domain: %(domain_id)s" +msgstr "Não foi possível localizar o domínio: %(domain_id)s" + +#, python-format +msgid "Could not find endpoint: %(endpoint_id)s" +msgstr "Não foi possível localizar terminal: %(endpoint_id)s" + +#, python-format +msgid "" +"Could not find federated protocol %(protocol_id)s for Identity Provider: " +"%(idp_id)s" +msgstr "" +"Não foi possível localizar o protocolo federado %(protocol_id)s para o " +"Provedor de Identidade: %(idp_id)s" + +#, python-format +msgid "Could not find group: %(group_id)s" +msgstr "Não foi possível localizar o grupo: %(group_id)s" + +#, python-format +msgid "Could not find mapping: %(mapping_id)s" +msgstr "Não foi possível localizar o mapeamento: %(mapping_id)s" + +msgid "Could not find policy association" +msgstr "Não foi possível localizar a associação de política" + +#, python-format +msgid "Could not find policy: %(policy_id)s" +msgstr "Não foi possível localizar a política: %(policy_id)s" + +#, python-format +msgid "Could not find project: %(project_id)s" +msgstr "Não foi possível localizar o projeto: %(project_id)s" + +#, python-format +msgid "Could not find region: %(region_id)s" +msgstr "Não foi possível localizar a região: %(region_id)s" + msgid "Could not find role" msgstr "Não é possível encontrar role" +#, python-format +msgid "" +"Could not find role assignment with role: %(role_id)s, user or group: " +"%(actor_id)s, project or domain: %(target_id)s" +msgstr "" +"Não foi possível localizar a designação de função com a função: %(role_id)s, " +"usuário ou grupo: %(actor_id)s, projeto ou domínio: %(target_id)s" + +#, python-format +msgid "Could not find role: %(role_id)s" +msgstr "Não foi possível localizar a função: %(role_id)s" + +#, python-format +msgid "Could not find service: %(service_id)s" +msgstr "Não foi possível localizar o serviço: %(service_id)s" + +#, python-format +msgid "Could not find token: %(token_id)s" +msgstr "Não foi possível localizar o token: %(token_id)s" + +#, python-format +msgid "Could not find trust: %(trust_id)s" +msgstr "Não foi possível localizar a confiança: %(trust_id)s" + +#, python-format +msgid "Could not find user: %(user_id)s" +msgstr "Não foi possível localizar o usuário: %(user_id)s" + +#, python-format +msgid "Could not find version: %(version)s" +msgstr "Não foi possível localizar a versão: %(version)s" + +#, python-format +msgid "Could not find: %(target)s" +msgstr "Não foi possível localizar: %(target)s" + +msgid "Could not validate the access token" +msgstr "Não foi possível validar o token de acesso" + msgid "Credential belongs to another user" msgstr "A credencial pertence à outro usuário" #, python-format +msgid "Database at /domains/%s/config" +msgstr "Banco de dados em /domains/%s/config" + +msgid "" +"Disabling an entity where the 'enable' attribute is ignored by configuration." +msgstr "" +"A desativação de uma entidade em que o atributo ‘enable' é ignorado pelo " +"configuração." + +#, python-format msgid "Domain (%s)" msgstr "Domínio (%s)" #, python-format +msgid "Domain cannot be named %s" +msgstr "O domínio não pode ser chamado %s" + +#, python-format +msgid "Domain cannot have ID %s" +msgstr "O domínio não pode ter o ID de %s" + +#, python-format msgid "Domain is disabled: %s" msgstr "O domínio está desativado: %s" +msgid "Domain metadata not supported by LDAP" +msgstr "Metadados de domínio não suportados por LDAP" + msgid "Domain scoped token is not supported" msgstr "O token de escopo de domínio não é suportado" #, python-format +msgid "" +"Domain: %(domain)s already has a configuration defined - ignoring file: " +"%(file)s." +msgstr "" +"Domínio: %(domain)s já possui uma configuração definida - ignorando arquivo: " +"%(file)s." + +msgid "Domains are read-only against LDAP" +msgstr "Domínios são somente leitura no LDAP" + +msgid "Duplicate Entry" +msgstr "Entrada Duplicada" + +#, python-format msgid "Duplicate ID, %s." msgstr "ID duplicado, %s." @@ -104,13 +409,104 @@ msgstr "Campo habilitado deve ser um booleano" msgid "Endpoint %(endpoint_id)s not found in project %(project_id)s" msgstr "Endpoint %(endpoint_id)s não encontrado no projeto %(project_id)s" +msgid "Endpoint Group Project Association not found" +msgstr "Associação de Projeto do Grupo do Terminal não localizada" + +msgid "Ensure configuration option idp_entity_id is set." +msgstr "Assegure que a opção de configuração idp_entity_id esteja definida." + +msgid "Ensure configuration option idp_sso_endpoint is set." +msgstr "Assegure que a opção de configuração idp_sso_endpoint esteja definida." + +#, python-format +msgid "" +"Error parsing configuration file for domain: %(domain)s, file: %(file)s." +msgstr "" +"Erro ao analisar o arquivo de configuração para o domínio: %(domain)s, " +"arquivo: %(file)s." + +#, python-format +msgid "Error while reading metadata file, %(reason)s" +msgstr "Erro ao ler arquivo de metadados, %(reason)s" + #, python-format msgid "Expected dict or list: %s" msgstr "Esperado dict ou list: %s" +msgid "" +"Expected signing certificates are not available on the server. Please check " +"Keystone configuration." +msgstr "" +"Certificados de assinatura esperados não estão disponíveis no servidor. " +"Verifique configuração de Keystone." + +#, python-format +msgid "" +"Expecting to find %(attribute)s in %(target)s - the server could not comply " +"with the request since it is either malformed or otherwise incorrect. The " +"client is assumed to be in error." +msgstr "" +"Esperando localizar %(attribute)s em %(target)s - o servidor não pôde " +"obedecer à solicitação porque ela está malformada ou de alguma maneira " +"incorreta. O cliente deve estar em erro." + +#, python-format +msgid "Failed to start the %(name)s server" +msgstr "Falha ao iniciar o servidor do %(name)s" + msgid "Failed to validate token" msgstr "Falha ao validar token" +msgid "Federation token is expired" +msgstr "O token de federação está expirado" + +#, python-format +msgid "" +"Field \"remaining_uses\" is set to %(value)s while it must not be set in " +"order to redelegate a trust" +msgstr "" +"O campo \"remaining_uses\" está configurado como %(value)s enquanto ele não " +"deve ser configurado para delegar novamente uma confiança" + +msgid "Found invalid token: scoped to both project and domain." +msgstr "Token inválido encontrado: escopo para ambos o projeto e o domínio." + +#, python-format +msgid "Group %(group)s is not supported for domain specific configurations" +msgstr "" +"O grupo %(group)s não é suportado para configurações específicas do domínio" + +#, python-format +msgid "" +"Group %(group_id)s returned by mapping %(mapping_id)s was not found in the " +"backend." +msgstr "" +"Grupo %(group_id)s retornou mapeando %(mapping_id)s não foi localizado no " +"backend." + +#, python-format +msgid "" +"Group membership across backend boundaries is not allowed, group in question " +"is %(group_id)s, user is %(user_id)s" +msgstr "" +"Associação ao grupo pelos limites de backend não é permitida, o grupo em " +"questão é %(group_id)s, o usuário é %(user_id)s" + +#, python-format +msgid "ID attribute %(id_attr)s not found in LDAP object %(dn)s" +msgstr "Atributo do ID %(id_attr)s não localizado no objeto LDAP %(dn)s" + +#, python-format +msgid "Identity Provider %(idp)s is disabled" +msgstr "O Provedor de Identidade %(idp)s está desativado" + +msgid "" +"Incoming identity provider identifier not included among the accepted " +"identifiers." +msgstr "" +"O identificador do provedor de identidade recebido não está incluído entre " +"os identificadores aceitos." + #, python-format msgid "Invalid LDAP TLS certs option: %(option)s. Choose one of: %(options)s" msgstr "" @@ -122,22 +518,87 @@ msgid "Invalid LDAP TLS_AVAIL option: %s. TLS not available" msgstr "Opção LDAP TLS_AVAIL inválida: %s. TLS não dsponível" #, python-format +msgid "Invalid LDAP deref option: %(option)s. Choose one of: %(options)s" +msgstr "Opção deref LDAP inválida: %(option)s. Escolha uma destas: %(options)s" + +#, python-format msgid "Invalid LDAP scope: %(scope)s. Choose one of: %(options)s" msgstr "Escopo LDAP inválido: %(scope)s. Escolha um de: %(options)s" msgid "Invalid TLS / LDAPS combination" msgstr "Combinação TLS / LADPS inválida" +#, python-format +msgid "Invalid audit info data type: %(data)s (%(type)s)" +msgstr "" +"Tipo de dados de informações de auditoria inválido: %(data)s (%(type)s)" + msgid "Invalid blob in credential" msgstr "BLOB inválido na credencial" +#, python-format +msgid "" +"Invalid domain name: %(domain)s found in config file name: %(file)s - " +"ignoring this file." +msgstr "" +"Nome de domínio inválido: %(domain)s localizado no nome do arquivo de " +"configuração: %(file)s - ignorando este arquivo." + +#, python-format +msgid "Invalid domain specific configuration: %(reason)s" +msgstr "Configuração específica de domínio inválida: %(reason)s" + +#, python-format +msgid "Invalid input for field '%(path)s'. The value is '%(value)s'." +msgstr "Entrada inválida para o campo '%(path)s'. O valor é '%(value)s'." + msgid "Invalid limit value" msgstr "Valor limite inválido" +#, python-format +msgid "" +"Invalid mix of entities for policy association - only Endpoint, Service or " +"Region+Service allowed. Request was - Endpoint: %(endpoint_id)s, Service: " +"%(service_id)s, Region: %(region_id)s" +msgstr "" +"Combinação de entidades inválida para associação de política - somente " +"Terminal, Serviço ou Região+Serviço permitido. A solicitação foi - Terminal: " +"%(endpoint_id)s, Serviço: %(service_id)s, Região: %(region_id)s" + +#, python-format +msgid "" +"Invalid rule: %(identity_value)s. Both 'groups' and 'domain' keywords must " +"be specified." +msgstr "" +"Regra inválida: %(identity_value)s. As palavras-chave 'groups' e 'domain' " +"devem ser especificadas." + +msgid "Invalid signature" +msgstr "Assinatura inválida" + +#, python-format +msgid "" +"Invalid ssl_cert_reqs value of %s, must be one of \"NONE\", \"OPTIONAL\", " +"\"REQUIRED\"" +msgstr "" +"valor ssl_cert_reqs inválido de %s, deve ser um de \"NONE\", \"OPTIMAL\", " +"\"REQUIRED\"" + +msgid "Invalid user / password" +msgstr "Usuário / senha inválido" + msgid "Invalid username or password" msgstr "Nome de usuário ou senha inválidos" #, python-format +msgid "KVS region %s is already configured. Cannot reconfigure." +msgstr "Região KVS %s já está configurado. Não é possível reconfigurar." + +#, python-format +msgid "Key Value Store not configured: %s" +msgstr "Armazenamento do Valor da Chave não configurado: %s" + +#, python-format msgid "LDAP %s create" msgstr "Criação de LDAP %s" @@ -150,6 +611,15 @@ msgid "LDAP %s update" msgstr "Atualização de LDAP %s" #, python-format +msgid "Lock Timeout occurred for key, %(target)s" +msgstr "Ocorreu um tempo limite de bloqueio para a chave, %(target)s" + +#, python-format +msgid "Lock key must match target key: %(lock)s != %(target)s" +msgstr "" +"Chave de bloqueio deve corresponder à chave de destino: %(lock)s !=%(target)s" + +#, python-format msgid "Malformed endpoint URL (%(endpoint)s), see ERROR log for details." msgstr "" "URL de endpoint mal-formada (%(endpoint)s), veja o log de ERROS para " @@ -158,18 +628,90 @@ msgstr "" msgid "Marker could not be found" msgstr "Marcador não pôde ser encontrado" +#, python-format +msgid "Maximum lock attempts on %s occurred." +msgstr "Máximo de tentativas de bloqueio em %s ocorreu." + +#, python-format +msgid "Member %(member)s is already a member of group %(group)s" +msgstr "O membro %(member)s já é membro do grupo %(group)s" + +#, python-format +msgid "Method not callable: %s" +msgstr "Método não pode ser chamado: %s" + +msgid "Missing entity ID from environment" +msgstr "ID da entidade ausente a partir do ambiente" + +msgid "" +"Modifying \"redelegation_count\" upon redelegation is forbidden. Omitting " +"this parameter is advised." +msgstr "" +"A modificação de \"redelegation_count\" é proibida. É recomendado omitir " +"este parâmetro." + +msgid "Multiple domains are not supported" +msgstr "Múltiplos domínios não são suportados" + +msgid "Must be called within an active lock context." +msgstr "Deve ser chamado dentro de um contexto de bloqueio ativo." + +msgid "Must specify either domain or project" +msgstr "Deve especificar o domínio ou projeto" + msgid "Name field is required and cannot be empty" msgstr "Campo nome é requerido e não pode ser vazio" +msgid "" +"No Authorization headers found, cannot proceed with OAuth related calls, if " +"running under HTTPd or Apache, ensure WSGIPassAuthorization is set to On." +msgstr "" +"Nenhum cabeçalho de autorização foi localizado, não é possível continuar com " +"chamadas relacionadas OAuth, se estiver executando sob HTTPd ou Apache, se " +"WSGIPassAuthorization for configurado para Ligado." + msgid "No authenticated user" msgstr "Nenhum usuário autenticado" +msgid "" +"No encryption keys found; run keystone-manage fernet_setup to bootstrap one." +msgstr "" +"Nenhuma chave de criptografia foi localizada; execute keystone-manage " +"fernet_setup para autoinicialização um." + msgid "No options specified" msgstr "Nenhuma opção especificada" +#, python-format +msgid "No policy is associated with endpoint %(endpoint_id)s." +msgstr "Nenhuma política associada ao terminal %(endpoint_id)s." + +#, python-format +msgid "No remaining uses for trust: %(trust_id)s" +msgstr "Nenhum uso restante para confiança: %(trust_id)s" + msgid "Non-default domain is not supported" msgstr "O domínio não padrão não é suportado" +msgid "One of the trust agents is disabled or deleted" +msgstr "Um dos agentes de confiança está desativado ou excluído" + +#, python-format +msgid "" +"Option %(option)s found with no group specified while checking domain " +"configuration request" +msgstr "" +"A opção %(option)s localizada sem grupo especificado durante a verificação " +"de domínio solicitação de configuração" + +#, python-format +msgid "" +"Option %(option)s in group %(group)s is not supported for domain specific " +"configurations" +msgstr "" +"A opção %(option)s no grupo %(group)s não é suportada para configurações " +"específicas de domínio" + #, python-format msgid "Project (%s)" msgstr "Projeto (%s)" @@ -178,6 +720,17 @@ msgstr "Projeto (%s)" msgid "Project is disabled: %s" msgstr "O projeto está desativado: %s" +msgid "Redelegation allowed for delegated by trust only" +msgstr "Nova delegação permitida para delegado pela confiança somente" + +#, python-format +msgid "" +"Remaining redelegation depth of %(redelegation_depth)d out of allowed range " +"of [0..%(max_count)d]" +msgstr "" +"Profundidade da redelegação restante do %(redelegation_depth)d fora do " +"intervalo permitido de [0..%(max_count)d]" + msgid "Request Token does not have an authorizing user id" msgstr "Token de Requisição não possui um ID de usuário autorizado" @@ -191,16 +744,41 @@ msgstr "" "servidor não pôde atender a requisição porque o tamanho do atributo é " "inválido (muito grande). Assume-se que o cliente está em erro." +msgid "Request must have an origin query parameter" +msgstr "A solicitação deve ter um parâmetro de consulta de origem" + msgid "Request token is expired" msgstr "Token de requisição expirou" msgid "Request token not found" msgstr "Token de requisição não encontrado" +msgid "Requested expiration time is more than redelegated trust can provide" +msgstr "" +"Prazo de expiração solicitado é maior do que a confiança delegada novamente " +"pode fornecer" + +#, python-format +msgid "" +"Requested redelegation depth of %(requested_count)d is greater than allowed " +"%(max_count)d" +msgstr "" +"Profundidade da nova delegação solicitada de %(requested_count)d é maior que " +"a %(max_count)d permitida" + #, python-format msgid "Role %s not found" msgstr "Role %s não localizada" +msgid "" +"Running keystone via eventlet is deprecated as of Kilo in favor of running " +"in a WSGI server (e.g. mod_wsgi). Support for keystone under eventlet will " +"be removed in the \"M\"-Release." +msgstr "" +"Executar o keystone via eventlet foi descontinuado como Kilo em favor de " +"executar em um servidor WSGI (por exemplo, mod_wsgi). Suporte para o " +"keystone sob eventlet será removida no \"M\"-Release." + msgid "Scoping to both domain and project is not allowed" msgstr "A definição de escopo para o domínio e o projeto não é permitida" @@ -210,12 +788,25 @@ msgstr "A definição de escopo para o domínio e a trust não é permitida" msgid "Scoping to both project and trust is not allowed" msgstr "A definição de escopo para o projeto e a trust não é permitida" +#, python-format +msgid "Service Provider %(sp)s is disabled" +msgstr "O Provedor de Serviços %(sp)s está desativado" + +msgid "Some of requested roles are not in redelegated trust" +msgstr "Algumas funções de confiança não estão na confiança da nova delegação" + msgid "Specify a domain or project, not both" msgstr "Especifique um domínio ou projeto, não ambos" msgid "Specify a user or group, not both" msgstr "Epecifique um usuário ou grupo, não ambos" +msgid "Specify one of domain or project" +msgstr "Especifique um domínio ou projeto" + +msgid "Specify one of user or group" +msgstr "Especifique um usuário ou grupo" + #, python-format msgid "" "String length exceeded.The length of string '%(string)s' exceeded the limit " @@ -224,18 +815,92 @@ msgstr "" "Comprimento de string excedido. O comprimento de string '%(string)s' excedeu " "o limite da coluna %(type)s(CHAR(%(length)d))." +msgid "The --all option cannot be used with the --domain-name option" +msgstr "A opção --all não pode ser usada com a opção --domain-name" + +#, python-format +msgid "The Keystone configuration file %(config_file)s could not be found." +msgstr "" +"O arquivo de configuração do Keystone %(config_file)s não pôde ser " +"localizado." + +#, python-format +msgid "" +"The Keystone domain-specific configuration has specified more than one SQL " +"driver (only one is permitted): %(source)s." +msgstr "" +"A configuração específica de domínio Keystone especificou mais de um driver " +"SQL (somente um é permitido): %(source)s." + msgid "The action you have requested has not been implemented." msgstr "A ação que você solicitou não foi implementada." +msgid "The authenticated user should match the trustor." +msgstr "O usuário autenticado deve corresponder à confiança." + +msgid "" +"The certificates you requested are not available. It is likely that this " +"server does not use PKI tokens otherwise this is the result of " +"misconfiguration." +msgstr "" +"Os certificados que você solicitou não estão disponíveis. É provável que " +"esse servidor não utiliza tokens PKI, caso contrário, este é o resultado de " +"configuração incorreta." + +#, python-format +msgid "" +"The password length must be less than or equal to %(size)i. The server could " +"not comply with the request because the password is invalid." +msgstr "" +"O comprimento da senha deve ser menor ou igual a %(size)i. O servidor não " +"pôde obedecer à solicitação porque a senha é inválida." + msgid "The request you have made requires authentication." msgstr "A requisição que você fez requer autenticação." msgid "The resource could not be found." msgstr "O recurso não pôde ser localizado." +msgid "" +"The revoke call must not have both domain_id and project_id. This is a bug " +"in the Keystone server. The current request is aborted." +msgstr "" +"A chamada de revogação não deve ter ambos domain_id e project_id. Esse é um " +"erro no servidor do Keystone. A solicitação atual foi interrompida." + +msgid "The service you have requested is no longer available on this server." +msgstr "O serviço que você solicitou não está mais disponível neste servidor." + +#, python-format +msgid "" +"The specified parent region %(parent_region_id)s would create a circular " +"region hierarchy." +msgstr "" +"A região pai especificada %(parent_region_id)s criaria uma hierarquia de " +"região circular." + +#, python-format +msgid "" +"The value of group %(group)s specified in the config should be a dictionary " +"of options" +msgstr "" +"O valor do grupo %(group)s especificado na configuração deverá ser um " +"dicionário de opções" + msgid "There should not be any non-oauth parameters" msgstr "Não deve haver nenhum parâmetro não oauth" +#, python-format +msgid "This is not a recognized Fernet payload version: %s" +msgstr "Esta não é uma versão de carga útil do Fernet reconhecida: %s" + +msgid "" +"This is not a v2.0 Fernet token. Use v3 for trust, domain, or federated " +"tokens." +msgstr "" +"Este não é um token Fernet v2.0. Use v3 para tokens de confiança, domínio, " +"ou federados." + msgid "" "Timestamp not in expected format. The server could not comply with the " "request since it is either malformed or otherwise incorrect. The client is " @@ -245,6 +910,16 @@ msgstr "" "requisição pois ela está mal formada ou incorreta. Assume-se que o cliente " "está com erro." +#, python-format +msgid "" +"To get a more detailed information on this error, re-run this command for " +"the specific domain, i.e.: keystone-manage domain_config_upload --domain-" +"name %s" +msgstr "" +"Para obter uma obter informações mais detalhadas sobre este erro, execute " +"novamente este comando para o domínio específico, ou seja: keystone-manage " +"domain_config_upload --domain-name %s" + msgid "Token belongs to another user" msgstr "O token pertence à outro usuário" @@ -258,6 +933,64 @@ msgid "Trustor is disabled." msgstr "O fiador está desativado." #, python-format +msgid "" +"Trying to update group %(group)s, so that, and only that, group must be " +"specified in the config" +msgstr "" +"Tentando atualizar o grupo %(group)s de modo que, e apenas que, o grupo deve " +"ser especificado na configuração" + +#, python-format +msgid "" +"Trying to update option %(option)s in group %(group)s, but config provided " +"contains option %(option_other)s instead" +msgstr "" +"Tentando atualizar a opção %(option)s no grupo %(group)s, mas a configuração " +"fornecida contém %(option_other)s ao invés" + +#, python-format +msgid "" +"Trying to update option %(option)s in group %(group)s, so that, and only " +"that, option must be specified in the config" +msgstr "" +"Tentando atualizar a opção %(option)s no grupo %(group)s, de modo que, e " +"apenas que, a opção deve ser especificada na configuração" + +msgid "" +"Unable to access the keystone database, please check it is configured " +"correctly." +msgstr "" +"Não é possível acessar o banco de dados keystone, verifique se ele está " +"configurado corretamente." + +#, python-format +msgid "Unable to consume trust %(trust_id)s, unable to acquire lock." +msgstr "" +"Não é possível consumir a confiança %(trust_id)s, não é possível adquirir o " +"bloqueio." + +#, python-format +msgid "" +"Unable to delete region %(region_id)s because it or its child regions have " +"associated endpoints." +msgstr "" +"Não foi possível excluir a região %(region_id)s, uma vez que ela ou suas " +"regiões filhas possuem terminais associados." + +#, python-format +msgid "Unable to find valid groups while using mapping %(mapping_id)s" +msgstr "" +"Não é possível localizar os grupos válidos ao utilizar o mapeamento " +"%(mapping_id)s" + +#, python-format +msgid "" +"Unable to get a connection from pool id %(id)s after %(seconds)s seconds." +msgstr "" +"Não é possível obter uma conexão do ID do conjunto %(id)s após %(seconds)s " +"segundos." + +#, python-format msgid "Unable to locate domain config directory: %s" msgstr "Não é possível localizar diretório de configuração de domínio: %s" @@ -265,17 +998,75 @@ msgstr "Não é possível localizar diretório de configuração de domínio: %s msgid "Unable to lookup user %s" msgstr "Não é possível consultar o usuário %s" +#, python-format +msgid "" +"Unable to reconcile identity attribute %(attribute)s as it has conflicting " +"values %(new)s and %(old)s" +msgstr "" +"Não é possível reconciliar o atributo de identidade %(attribute)s, pois ele " +"possui valores conflitantes %(new)s e %(old)s" + +#, python-format +msgid "" +"Unable to sign SAML assertion. It is likely that this server does not have " +"xmlsec1 installed, or this is the result of misconfiguration. Reason " +"%(reason)s" +msgstr "" +"Não é possível assinar asserção SAML. Provavelmente esse servidor não possui " +"o xmlsec1 instalado, ou isso é o resultado de uma configuração incorreta. " +"Motivo %(reason)s" + msgid "Unable to sign token." msgstr "Não é possível assinar o token." +#, python-format +msgid "Unexpected assignment type encountered, %s" +msgstr "Tipo de designação inesperada encontrada, %s" + +#, python-format +msgid "" +"Unexpected combination of grant attributes - User: %(user_id)s, Group: " +"%(group_id)s, Project: %(project_id)s, Domain: %(domain_id)s" +msgstr "" +"Combinação inesperada de atributos de concessão – Usuário: %(user_id)s, " +"Grupo: %(group_id)s, Projeto: %(project_id)s, Domínio: %(domain_id)s" + +#, python-format +msgid "Unexpected status requested for JSON Home response, %s" +msgstr "Status inesperado solicitado para resposta JSON Home, %s" + msgid "Unknown Target" msgstr "Alvo Desconhecido" #, python-format +msgid "Unknown domain '%(name)s' specified by --domain-name" +msgstr "Domínio desconhecido '%(name)s' especificado pelo --domain-name" + +#, python-format +msgid "Unknown token version %s" +msgstr "Versão de token desconhecida %s" + +#, python-format +msgid "Unregistered dependency: %(name)s for %(targets)s" +msgstr "Dependência não registrada: %(name)s para %(targets)s" + +msgid "Update of `parent_id` is not allowed." +msgstr "Atualização de ‘parent_id’ não é permitida." + +msgid "Use a project scoped token when attempting to create a SAML assertion" +msgstr "" +"Use um token com escopo definido do projeto ao tentar criar uma asserção SAML" + +#, python-format msgid "User %(u_id)s is unauthorized for tenant %(t_id)s" msgstr "Usuário %(u_id)s não está autorizado para o tenant %(t_id)s" #, python-format +msgid "User %(user_id)s already has role %(role_id)s in tenant %(tenant_id)s" +msgstr "" +"Usuário %(user_id)s já possui a função %(role_id)s no locatário %(tenant_id)s" + +#, python-format msgid "User %(user_id)s has no access to domain %(domain_id)s" msgstr "O usuário %(user_id)s não tem acesso ao domínio %(domain_id)s" @@ -287,6 +1078,10 @@ msgstr "O usuário %(user_id)s não tem acesso ao projeto %(project_id)s" msgid "User %(user_id)s is already a member of group %(group_id)s" msgstr "Usuário %(user_id)s já é membro do grupo %(group_id)s" +#, python-format +msgid "User '%(user_id)s' not found in group '%(group_id)s'" +msgstr "Usuário '%(user_id)s' não localizado no grupo '%(group_id)s'" + msgid "User IDs do not match" msgstr "ID de usuário não confere" @@ -303,12 +1098,111 @@ msgstr "Usuário não é confiável." msgid "User not found" msgstr "Usuário não localizado" +#, python-format +msgid "User type %s not supported" +msgstr "Tipo de usuário %s não suportado" + msgid "You are not authorized to perform the requested action." msgstr "Você não está autorizado à realizar a ação solicitada." +#, python-format +msgid "You are not authorized to perform the requested action: %(action)s" +msgstr "Você não está autorizado a executar a ação solicitada: %(action)s" + +msgid "`key_mangler` functions must be callable." +msgstr "Funções `key_mangler` devem ser chamáveis." + +msgid "`key_mangler` option must be a function reference" +msgstr "opção `key_mangler` deve ser uma referência de função" + +msgid "any options" +msgstr "quaisquer opções" + +msgid "auth_type is not Negotiate" +msgstr "auth_type não é Negotiate" + msgid "authorizing user does not have role required" msgstr "Usuário autorizado não possui o role necessário" +msgid "cache_collection name is required" +msgstr "nome cache_collection é necessário" + +#, python-format +msgid "cannot create a project in a branch containing a disabled project: %s" +msgstr "" +"não é possível criar um projeto em uma ramificação que contém um projeto " +"desativado: %s" + +msgid "cannot create a project within a different domain than its parents." +msgstr "não é possível criar um projeto em um domínio diferente de seus pais." + +msgid "cannot delete a domain that is enabled, please disable it first." +msgstr "" +"não é possível excluir um domínio que esteja ativado, desative-o primeiro." + +#, python-format +msgid "cannot delete the project %s since it is not a leaf in the hierarchy." +msgstr "" +"não é possível excluir o projeto %s, pois ele não é uma folha na hierarquia." + +#, python-format +msgid "cannot disable project %s since its subtree contains enabled projects" +msgstr "" +"não é possível desativar o projeto %s desde que sua subárvore contenha " +"projetos ativados" + +#, python-format +msgid "cannot enable project %s since it has disabled parents" +msgstr "" +"não é possível ativar o projeto %s desde que ele tenha pais desativados" + +msgid "database db_name is required" +msgstr "banco de dados db_name é necessário" + +msgid "db_hosts value is required" +msgstr "valor db_hosts é necessário" + +msgid "delete the default domain" +msgstr "excluir o domínio padrão" + +#, python-format +msgid "group %(group)s" +msgstr "grupo %(group)s" + +msgid "" +"idp_contact_type must be one of: [technical, other, support, administrative " +"or billing." +msgstr "" +"idp_contact_type deve ser uma dessas opções: [técnico, outro, suporte, " +"administrativo ou faturamento." + +msgid "integer value expected for mongo_ttl_seconds" +msgstr "valor de número inteiro esperado para mongo_ttl_seconds" + +msgid "integer value expected for w (write concern attribute)" +msgstr "valor inteiro esperado para w (atributo relativo a gravação)" + +#, python-format +msgid "invalid date format %s" +msgstr "formato de data inválido %s" + +#, python-format +msgid "max hierarchy depth reached for %s branch." +msgstr "profundidade máx. de hierarquia atingida para a ramificação %s." + +msgid "no ssl support available" +msgstr "suporte ssl não disponível" + +#, python-format +msgid "option %(option)s in group %(group)s" +msgstr "opção %(option)s no grupo %(group)s" + +msgid "pad must be single character" +msgstr "preenchimento deve ser caractere único" + +msgid "padded base64url text must be multiple of 4 characters" +msgstr "texto base64url preenchido deve ser múltiplo de 4 caracteres" + msgid "provided consumer key does not match stored consumer key" msgstr "" "Chave de consumidor fornecida não confere com a chave de consumidor " @@ -325,6 +1219,43 @@ msgstr "Verificador fornecido não confere com o verificador armazenado" msgid "region not type dogpile.cache.CacheRegion" msgstr "região não é do tipo dogpile.cache.CacheRegion" +msgid "remaining_uses must be a positive integer or null." +msgstr "remaining_uses deve ser um número inteiro positivo ou nulo." + +msgid "remaining_uses must not be set if redelegation is allowed" +msgstr "" +"remaining_uses não deverá ser definido se a nova delegação for permitida" + +msgid "replicaset_name required when use_replica is True" +msgstr "replicaset_name necessário quando use_replica for True" + +#, python-format +msgid "" +"request to update group %(group)s, but config provided contains group " +"%(group_other)s instead" +msgstr "" +"solicite atualizar o grupo %(group)s, mas a configuração fornecida contém o " +"grupo %(group_other)s ao invés" + +msgid "rescope a scoped token" +msgstr "Defina novamente um escopo de um token com escopo" + +#, python-format +msgid "text is multiple of 4, but pad \"%s\" occurs before 2nd to last char" +msgstr "" +"texto é múltiplo de 4, mas preenchimento \"%s\" ocorre antes do penúltimo " +"caractere" + +#, python-format +msgid "text is multiple of 4, but pad \"%s\" occurs before non-pad last char" +msgstr "" +"texto é múltiplo de 4, mas preenchimento \"%s\" ocorre antes do último " +"caractere de não preenchimento" + +#, python-format +msgid "text is not a multiple of 4, but contains pad \"%s\"" +msgstr "texto não é um múltiplo de 4, mas contém preenchimento \"%s\"" + #, python-format msgid "tls_cacertdir %s not found or is not a directory" msgstr "tls_cacertdir %s não encontrado ou não é um diretório" @@ -332,3 +1263,7 @@ msgstr "tls_cacertdir %s não encontrado ou não é um diretório" #, python-format msgid "tls_cacertfile %s not found or is not a file" msgstr "tls_cacertfile %s não encontrada ou não é um arquivo" + +#, python-format +msgid "token reference must be a KeystoneToken type, got: %s" +msgstr "referência de token deve ser um tipo KeystoneToken, obteve: %s" diff --git a/keystone-moon/keystone/locale/ru/LC_MESSAGES/keystone.po b/keystone-moon/keystone/locale/ru/LC_MESSAGES/keystone.po new file mode 100644 index 00000000..31b8e98d --- /dev/null +++ b/keystone-moon/keystone/locale/ru/LC_MESSAGES/keystone.po @@ -0,0 +1,1271 @@ +# Russian translations for keystone. +# Copyright (C) 2015 OpenStack Foundation +# This file is distributed under the same license as the keystone project. +# +# Translators: +# kogamatranslator49 <r.podarov@yandex.ru>, 2015 +# sher <sher@online.ua>, 2013 +# Lucas Palm <lapalm@us.ibm.com>, 2015. #zanata +# OpenStack Infra <zanata@openstack.org>, 2015. #zanata +msgid "" +msgstr "" +"Project-Id-Version: keystone 9.0.0.dev14\n" +"Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" +"POT-Creation-Date: 2015-09-24 06:09+0000\n" +"PO-Revision-Date: 2015-09-03 12:54+0000\n" +"Last-Translator: openstackjenkins <jenkins@openstack.org>\n" +"Language-Team: Russian (http://www.transifex.com/openstack/keystone/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" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.0\n" + +#, python-format +msgid "%(detail)s" +msgstr "%(detail)s" + +#, python-format +msgid "" +"%(event)s is not a valid notification event, must be one of: %(actions)s" +msgstr "" +"%(event)s не является допустимым событием уведомления, требуется одно из " +"значений: %(actions)s" + +#, python-format +msgid "%(host)s is not a trusted dashboard host" +msgstr "%(host)s не является надежным хостом сводных панелей" + +#, python-format +msgid "%(message)s %(amendment)s" +msgstr "%(message)s %(amendment)s" + +#, python-format +msgid "" +"%(mod_name)s doesn't provide database migrations. The migration repository " +"path at %(path)s doesn't exist or isn't a directory." +msgstr "" +"%(mod_name)s не обеспечивает перенос баз данных. Путь к хранилищу миграции " +"%(path)s не существует или не является каталогом." + +#, python-format +msgid "%(property_name)s cannot be less than %(min_length)s characters." +msgstr "%(property_name)s не может быть короче %(min_length)s символов." + +#, python-format +msgid "%(property_name)s is not a %(display_expected_type)s" +msgstr "%(property_name)s не принадлежит к типу %(display_expected_type)s" + +#, python-format +msgid "%(property_name)s should not be greater than %(max_length)s characters." +msgstr "%(property_name)s не должен быть длинее %(max_length)s символов." + +#, python-format +msgid "%s cannot be empty." +msgstr "%s не может быть пуст." + +#, python-format +msgid "%s extension does not exist." +msgstr "Расширение %s не существует" + +#, python-format +msgid "%s field is required and cannot be empty" +msgstr "Поле %s является обязательным и не может быть пустым" + +#, python-format +msgid "%s field(s) cannot be empty" +msgstr "Поле %s не может быть пустым" + +msgid "(Disable debug mode to suppress these details.)" +msgstr "" +" \t\n" +"(Отключить режим отладки, чтобы подавить эти детали.)" + +msgid "--all option cannot be mixed with other options" +msgstr "опцию --all нельзя указывать вместе с другими опциями" + +msgid "A project-scoped token is required to produce a service catalog." +msgstr "Для создания каталога службы необходим маркер уровня проекта." + +msgid "Access token is expired" +msgstr "Срок действия ключа доступа истек" + +msgid "Access token not found" +msgstr "Ключ доступа не найден" + +msgid "Additional authentications steps required." +msgstr "Требуются дополнительные действия для идентификации." + +msgid "An unexpected error occurred when retrieving domain configs" +msgstr "Возникла непредвиденная ошибка при получении конфигураций доменов" + +#, python-format +msgid "An unexpected error occurred when trying to store %s" +msgstr "При попытке сохранить %s произошла непредвиденная ошибка" + +msgid "An unexpected error prevented the server from fulfilling your request." +msgstr "Из-за непредвиденной ошибки ваш запрос не был выполнен сервером." + +#, python-format +msgid "" +"An unexpected error prevented the server from fulfilling your request: " +"%(exception)s" +msgstr "" +"Из-за непредвиденной ошибки ваш запрос не был выполнен сервером: " +"%(exception)s" + +msgid "An unhandled exception has occurred: Could not find metadata." +msgstr "" +"Возникла необработанная исключительная ситуация: не удалось найти метаданные." + +msgid "At least one option must be provided" +msgstr "Необходимо указать хотя бы одну опцию" + +msgid "At least one option must be provided, use either --all or --domain-name" +msgstr "" +"Должен быть указан хотя бы один параметр. Укажите --all или --domain-name" + +msgid "At least one role should be specified." +msgstr "Необходимо указать по крайней мере одну роль." + +msgid "Attempted to authenticate with an unsupported method." +msgstr "Попытка идентификации с использованием неподдерживаемого метода." + +msgid "" +"Attempting to use OS-FEDERATION token with V2 Identity Service, use V3 " +"Authentication" +msgstr "" +"Попытка использовать маркер OS-FEDERATION со службой идентификации версии 2. " +"Следует использовать идентификацию версии 3" + +msgid "Authentication plugin error." +msgstr "Ошибка модуля идентификации." + +msgid "Cannot authorize a request token with a token issued via delegation." +msgstr "" +"Предоставить права доступа маркеру запроса с маркером, выданным посредством " +"делегирования, невозможно." + +#, python-format +msgid "Cannot change %(option_name)s %(attr)s" +msgstr "Невозможно изменить %(option_name)s %(attr)s" + +msgid "Cannot change Domain ID" +msgstr "Невозможно изменить ИД домена" + +msgid "Cannot change consumer secret" +msgstr "Невозможно изменить секретный ключ приемника" + +msgid "Cannot change user ID" +msgstr "Невозможно изменить ИД пользователя" + +msgid "Cannot change user name" +msgstr "Невозможно изменить имя пользователя" + +#, python-format +msgid "Cannot create project with parent: %(project_id)s" +msgstr "Не удается создать проект с родительским объектом: %(project_id)s" + +#, python-format +msgid "Cannot duplicate name %s" +msgstr "Невозможно копировать имя %s" + +msgid "Cannot list request tokens with a token issued via delegation." +msgstr "" +"Показать список маркеров запросов с маркером, выданным посредством " +"делегирования, невозможно." + +#, python-format +msgid "Cannot open certificate %(cert_file)s. Reason: %(reason)s" +msgstr "Не удалось открыть сертификат %(cert_file)s. Причина: %(reason)s" + +#, python-format +msgid "Cannot remove role that has not been granted, %s" +msgstr "Удалить роль, которая не была предоставлена, нельзя: %s" + +msgid "" +"Cannot truncate a driver call without hints list as first parameter after " +"self " +msgstr "" +"Невозможно отсечь вызов драйвера без списка подсказок в качестве первого " +"параметра после самого себя " + +msgid "" +"Cannot use parents_as_list and parents_as_ids query params at the same time." +msgstr "" +"Нельзя использовать параметры запроса parents_as_list и parents_as_ids " +"одновременно." + +msgid "" +"Cannot use subtree_as_list and subtree_as_ids query params at the same time." +msgstr "" +"Нельзя использовать параметры запроса subtree_as_list и subtree_as_ids " +"одновременно." + +msgid "" +"Combining effective and group filter will always result in an empty list." +msgstr "" +"Сочетание действующего фильтра и фильтра группы всегда дает пустой список." + +msgid "" +"Combining effective, domain and inherited filters will always result in an " +"empty list." +msgstr "" +"Сочетание действующего фильтра, фильтра домена и унаследованного фильтра " +"всегда дает пустой список." + +#, python-format +msgid "Conflict occurred attempting to store %(type)s - %(details)s" +msgstr "При попытке сохранить %(type)s возник конфликт - %(details)s" + +#, python-format +msgid "Conflicting region IDs specified: \"%(url_id)s\" != \"%(ref_id)s\"" +msgstr "Указаны конфликтующие ИД регионов: \"%(url_id)s\" != \"%(ref_id)s\"" + +msgid "Consumer not found" +msgstr "Приемник не найден" + +#, python-format +msgid "" +"Could not change immutable attribute(s) '%(attributes)s' in target %(target)s" +msgstr "" +"Изменить постоянный атрибут '%(attributes)s' в цели %(target)s невозможно" + +#, python-format +msgid "" +"Could not find %(group_or_option)s in domain configuration for domain " +"%(domain_id)s" +msgstr "" +"Не найден пользователь/группа %(group_or_option)s в конфигурации домена " +"%(domain_id)s" + +#, python-format +msgid "Could not find Endpoint Group: %(endpoint_group_id)s" +msgstr "Не найдена группа конечных точек: %(endpoint_group_id)s" + +msgid "Could not find Identity Provider identifier in environment" +msgstr "Не удалось найти идентификатор поставщика идентификаторов в среде" + +#, python-format +msgid "Could not find Identity Provider: %(idp_id)s" +msgstr "Поставщик идентификаторов %(idp_id)s не найден" + +#, python-format +msgid "Could not find Service Provider: %(sp_id)s" +msgstr "Не удалось найти поставщик служб %(sp_id)s" + +#, python-format +msgid "Could not find credential: %(credential_id)s" +msgstr "Идентификационные данные %(credential_id)s не найдены" + +#, python-format +msgid "Could not find domain: %(domain_id)s" +msgstr "Домен %(domain_id)s не найден" + +#, python-format +msgid "Could not find endpoint: %(endpoint_id)s" +msgstr "Конечная точка %(endpoint_id)s не найдена" + +#, python-format +msgid "" +"Could not find federated protocol %(protocol_id)s for Identity Provider: " +"%(idp_id)s" +msgstr "" +"Объединенный протокол %(protocol_id)s для поставщика идентификаторов " +"%(idp_id)s не найден" + +#, python-format +msgid "Could not find group: %(group_id)s" +msgstr "Группа %(group_id)s не найдена" + +#, python-format +msgid "Could not find mapping: %(mapping_id)s" +msgstr "Отображение %(mapping_id)s не найдено" + +msgid "Could not find policy association" +msgstr "Не найдена связь стратегии" + +#, python-format +msgid "Could not find policy: %(policy_id)s" +msgstr "Стратегия %(policy_id)s не найдена" + +#, python-format +msgid "Could not find project: %(project_id)s" +msgstr "Проект %(project_id)s не найден" + +#, python-format +msgid "Could not find region: %(region_id)s" +msgstr "Регион %(region_id)s не найден" + +msgid "Could not find role" +msgstr "Не удалось найти роль" + +#, python-format +msgid "" +"Could not find role assignment with role: %(role_id)s, user or group: " +"%(actor_id)s, project or domain: %(target_id)s" +msgstr "" +"Не найдено присвоение роли %(role_id)s, пользователь/группа: %(actor_id)s, " +"проект/домен: %(target_id)s" + +#, python-format +msgid "Could not find role: %(role_id)s" +msgstr "Роль %(role_id)s не найдена" + +#, python-format +msgid "Could not find service: %(service_id)s" +msgstr "Служба %(service_id)s не найдена" + +#, python-format +msgid "Could not find token: %(token_id)s" +msgstr "Ключ %(token_id)s не найден" + +#, python-format +msgid "Could not find trust: %(trust_id)s" +msgstr "Группа доверия %(trust_id)s не найдена" + +#, python-format +msgid "Could not find user: %(user_id)s" +msgstr "Пользователь %(user_id)s не найден" + +#, python-format +msgid "Could not find version: %(version)s" +msgstr "Версия %(version)s не найдена" + +#, python-format +msgid "Could not find: %(target)s" +msgstr "%(target)s не найдена" + +msgid "Could not validate the access token" +msgstr "Не удалось проверить ключ доступа" + +msgid "Credential belongs to another user" +msgstr "Разрешение принадлежит другому пользователю" + +#, python-format +msgid "Database at /domains/%s/config" +msgstr "База данных в каталоге /domains/%s/config" + +msgid "" +"Disabling an entity where the 'enable' attribute is ignored by configuration." +msgstr "" +"Отключение сущности, при котором атрибут 'enable' в конфигурации " +"игнорируется." + +#, python-format +msgid "Domain (%s)" +msgstr "Домен (%s)" + +#, python-format +msgid "Domain cannot be named %s" +msgstr "Домену нельзя присвоить имя %s" + +#, python-format +msgid "Domain cannot have ID %s" +msgstr "Домен не может иметь идентификатор %s" + +#, python-format +msgid "Domain is disabled: %s" +msgstr "Домен отключен: %s" + +msgid "Domain metadata not supported by LDAP" +msgstr "Метаданные домена не поддерживаются LDAP" + +msgid "Domain scoped token is not supported" +msgstr "Маркер, область которого - домен, не поддерживается" + +#, python-format +msgid "" +"Domain: %(domain)s already has a configuration defined - ignoring file: " +"%(file)s." +msgstr "" +"У домена %(domain)s уже определена конфигурация - файл пропущен: %(file)s." + +msgid "Domains are read-only against LDAP" +msgstr "Домены доступны только для чтения в LDAP" + +msgid "Duplicate Entry" +msgstr "Дубликат записи" + +#, python-format +msgid "Duplicate ID, %s." +msgstr "Повторяющийся идентификатор, %s." + +#, python-format +msgid "Duplicate name, %s." +msgstr "Повторяющееся имя, %s." + +msgid "Enabled field must be a boolean" +msgstr "Активное поле должно быть булевским значением" + +msgid "Enabled field should be a boolean" +msgstr "Активное поле должно быть булевским значением" + +#, python-format +msgid "Endpoint %(endpoint_id)s not found in project %(project_id)s" +msgstr "Конечная точка %(endpoint_id)s не найдена в проекте %(project_id)s" + +msgid "Endpoint Group Project Association not found" +msgstr "Не найдена связь проекта группы конечных точек" + +msgid "Ensure configuration option idp_entity_id is set." +msgstr "Убедитесь, что указан параметр конфигурации idp_entity_id." + +msgid "Ensure configuration option idp_sso_endpoint is set." +msgstr "Убедитесь, что указан параметр конфигурации idp_sso_endpoint." + +#, python-format +msgid "" +"Error parsing configuration file for domain: %(domain)s, file: %(file)s." +msgstr "" +"Ошибка анализа файла конфигурации для домена %(domain)s, файл: %(file)s." + +#, python-format +msgid "Error while reading metadata file, %(reason)s" +msgstr "Ошибка чтения файла метаданных: %(reason)s" + +#, python-format +msgid "Expected dict or list: %s" +msgstr "Ожидается dict или list: %s" + +msgid "" +"Expected signing certificates are not available on the server. Please check " +"Keystone configuration." +msgstr "" +"Ожидаемые сертификаты подписания недоступны на сервере. Рекомендуется " +"проверить конфигурацию Keystone." + +#, python-format +msgid "" +"Expecting to find %(attribute)s in %(target)s - the server could not comply " +"with the request since it is either malformed or otherwise incorrect. The " +"client is assumed to be in error." +msgstr "" +"Ожидается %(attribute)s в %(target)s - серверу не удалось удовлетворить " +"запрос, поскольку его формат является неверным, либо запрос некорректен по " +"другой причине. Предположительно, клиент находится в состоянии ошибки." + +#, python-format +msgid "Failed to start the %(name)s server" +msgstr "Не удалось запустить сервер %(name)s" + +msgid "Failed to validate token" +msgstr "Проверить маркер не удалось" + +msgid "Federation token is expired" +msgstr "Срок действия ключа объединения истек" + +#, python-format +msgid "" +"Field \"remaining_uses\" is set to %(value)s while it must not be set in " +"order to redelegate a trust" +msgstr "" +"Полю \"remaining_uses\" присвоено значение %(value)s, хотя поле не может " +"быть задано для изменения делегирования группы доверия" + +msgid "Found invalid token: scoped to both project and domain." +msgstr "" +"Обнаружен недопустимый маркер: он относится и к уровню проекта, и к уровню " +"домена." + +#, python-format +msgid "Group %(group)s is not supported for domain specific configurations" +msgstr "" +"Группа %(group)s не поддерживается для определенных конфигураций домена" + +#, python-format +msgid "" +"Group %(group_id)s returned by mapping %(mapping_id)s was not found in the " +"backend." +msgstr "" +"Группа %(group_id)s, возвращенная преобразованием %(mapping_id)s, не найдена " +"в на базовом сервере." + +#, python-format +msgid "" +"Group membership across backend boundaries is not allowed, group in question " +"is %(group_id)s, user is %(user_id)s" +msgstr "" +"Членство в группе не может распространяться через границы базовых систем, " +"группа под вопросом - %(group_id)s, пользователь - %(user_id)s" + +#, python-format +msgid "ID attribute %(id_attr)s not found in LDAP object %(dn)s" +msgstr "Атрибут ИД %(id_attr)s не найден в объекте LDAP %(dn)s" + +#, python-format +msgid "Identity Provider %(idp)s is disabled" +msgstr "Поставщик идентификаторов %(idp)s отключен" + +msgid "" +"Incoming identity provider identifier not included among the accepted " +"identifiers." +msgstr "" +"Входящий идентификатор поставщика идентификаторов не включен в принятые " +"идентификаторы." + +#, python-format +msgid "Invalid LDAP TLS certs option: %(option)s. Choose one of: %(options)s" +msgstr "" +"Недопустимая опция certs TLS LDAP: %(option)s. Выберите одно из следующих " +"значений: %(options)s" + +#, python-format +msgid "Invalid LDAP TLS_AVAIL option: %s. TLS not available" +msgstr "Недопустимая опция TLS_AVAIL LDAP: %s. TLS недоступен" + +#, python-format +msgid "Invalid LDAP deref option: %(option)s. Choose one of: %(options)s" +msgstr "" +"Недопустимая опция deref LDAP: %(option)s. Выберите одно из следующих " +"значений: %(options)s" + +#, python-format +msgid "Invalid LDAP scope: %(scope)s. Choose one of: %(options)s" +msgstr "" +"Недопустимая область LDAP: %(scope)s. Выберите одно из следующих значений: " +"%(options)s" + +msgid "Invalid TLS / LDAPS combination" +msgstr "Недопустимое сочетание TLS/LDAPS" + +#, python-format +msgid "Invalid audit info data type: %(data)s (%(type)s)" +msgstr "Недопустимый тип данных в информации контроля: %(data)s (%(type)s)" + +msgid "Invalid blob in credential" +msgstr "Недопустимый большой двоичный объект в разрешении" + +#, python-format +msgid "" +"Invalid domain name: %(domain)s found in config file name: %(file)s - " +"ignoring this file." +msgstr "" +"Обнаружено недопустимое имя домена %(domain)s в файле конфигурации %(file)s " +"- файл пропущен." + +#, python-format +msgid "Invalid domain specific configuration: %(reason)s" +msgstr "Недопустимая конфигурация для домена: %(reason)s" + +#, python-format +msgid "Invalid input for field '%(path)s'. The value is '%(value)s'." +msgstr "Недопустимый ввод для поля '%(path)s'. Значение - '%(value)s'." + +msgid "Invalid limit value" +msgstr "Недопустимое значение ограничения" + +#, python-format +msgid "" +"Invalid mix of entities for policy association - only Endpoint, Service or " +"Region+Service allowed. Request was - Endpoint: %(endpoint_id)s, Service: " +"%(service_id)s, Region: %(region_id)s" +msgstr "" +"Недопустимое смешение сущностей для связывания стратегии. Только Конечная " +"точка, Служба и Регион+Служба разрешены. В запросе было: Конечная точка " +"%(endpoint_id)s, Служба %(service_id)s, Регион %(region_id)s" + +#, python-format +msgid "" +"Invalid rule: %(identity_value)s. Both 'groups' and 'domain' keywords must " +"be specified." +msgstr "" +"Недопустимое правило: %(identity_value)s. Ключевые слова 'groups' и 'domain' " +"должны быть указаны." + +msgid "Invalid signature" +msgstr "Недопустимая подпись" + +#, python-format +msgid "" +"Invalid ssl_cert_reqs value of %s, must be one of \"NONE\", \"OPTIONAL\", " +"\"REQUIRED\"" +msgstr "" +"Недопустимое значение ssl_cert_reqs, %s, необходимо указать одно из " +"значений: \"NONE\", \"OPTIONAL\", \"REQUIRED\"" + +msgid "Invalid user / password" +msgstr "Недопустимый пользователь / пароль" + +msgid "Invalid username or password" +msgstr "Недопустимое имя пользователя или пароль" + +#, python-format +msgid "KVS region %s is already configured. Cannot reconfigure." +msgstr "Регион KVS %s уже настроен. Изменение конфигурации невозможно." + +#, python-format +msgid "Key Value Store not configured: %s" +msgstr "Хранилище значений ключей не настроено: %s" + +#, python-format +msgid "LDAP %s create" +msgstr "LDAP %s создание" + +#, python-format +msgid "LDAP %s delete" +msgstr "LDAP %s удаление" + +#, python-format +msgid "LDAP %s update" +msgstr "LDAP %s обновление" + +#, python-format +msgid "Lock Timeout occurred for key, %(target)s" +msgstr "Наступил тайм-аут блокировки для ключа, %(target)s" + +#, python-format +msgid "Lock key must match target key: %(lock)s != %(target)s" +msgstr "" +"Блокировка должна соответствовать целевому ключу: %(lock)s != %(target)s" + +#, python-format +msgid "Malformed endpoint URL (%(endpoint)s), see ERROR log for details." +msgstr "" +"Неверный формат URL конечной точки (%(endpoint)s), подробную информацию см. " +"в протоколе ОШИБОК." + +msgid "Marker could not be found" +msgstr "Не удалось найти маркер" + +#, python-format +msgid "Maximum lock attempts on %s occurred." +msgstr "Выполнено максимальное число попыток блокировки в %s." + +#, python-format +msgid "Member %(member)s is already a member of group %(group)s" +msgstr "Элемент %(member)s уже является участником группы %(group)s" + +#, python-format +msgid "Method not callable: %s" +msgstr "Вызов метода невозможен: %s" + +msgid "Missing entity ID from environment" +msgstr "В среде отсутствует ИД сущности" + +msgid "" +"Modifying \"redelegation_count\" upon redelegation is forbidden. Omitting " +"this parameter is advised." +msgstr "" +"Изменение параметра \"redelegation_count\" во время изменения делегирования " +"запрещено. Возможен пропуск этого параметра." + +msgid "Multiple domains are not supported" +msgstr "Множественные домены не поддерживаются" + +msgid "Must be called within an active lock context." +msgstr "Требуется вызов в контексте активной блокировки." + +msgid "Must specify either domain or project" +msgstr "Необходимо указать домен или проект" + +msgid "Name field is required and cannot be empty" +msgstr "Поле имени является обязательным и не может быть пустым" + +msgid "" +"No Authorization headers found, cannot proceed with OAuth related calls, if " +"running under HTTPd or Apache, ensure WSGIPassAuthorization is set to On." +msgstr "" +"Не найдены заголовки предоставления доступа - вызовы, связанные с OAuth, " +"невозможны при выполнении под управлением HTTPd или Apache. Убедитесь, что " +"параметру WSGIPassAuthorization присвоено значение On." + +msgid "No authenticated user" +msgstr "Нет идентифицированного пользователя" + +msgid "" +"No encryption keys found; run keystone-manage fernet_setup to bootstrap one." +msgstr "" +"Не найдены ключи шифрования. Выполните команду keystone-manage fernet_setup, " +"чтобы создать ключ." + +msgid "No options specified" +msgstr "Параметры не указаны" + +#, python-format +msgid "No policy is associated with endpoint %(endpoint_id)s." +msgstr "С конечной точкой %(endpoint_id)s не связано ни одной стратегии." + +#, python-format +msgid "No remaining uses for trust: %(trust_id)s" +msgstr "Вариантов использования группы доверия %(trust_id)s не осталось" + +msgid "Non-default domain is not supported" +msgstr "Домен, отличный от применяемого по умолчанию, не поддерживается" + +msgid "One of the trust agents is disabled or deleted" +msgstr "Один из доверенных агентов отключен или удален" + +#, python-format +msgid "" +"Option %(option)s found with no group specified while checking domain " +"configuration request" +msgstr "" +"Обнаружен параметр %(option)s без указанной группы во время проверки запроса " +"на настройку домена" + +#, python-format +msgid "" +"Option %(option)s in group %(group)s is not supported for domain specific " +"configurations" +msgstr "" +"Параметр %(option)s в группе %(group)s не поддерживается для определенных " +"конфигураций домена" + +#, python-format +msgid "Project (%s)" +msgstr "Проект (%s)" + +#, python-format +msgid "Project is disabled: %s" +msgstr "Проект отключен: %s" + +msgid "Redelegation allowed for delegated by trust only" +msgstr "Изменение делегирования разрешено только для доверенного пользователя" + +#, python-format +msgid "" +"Remaining redelegation depth of %(redelegation_depth)d out of allowed range " +"of [0..%(max_count)d]" +msgstr "" +"Оставшаяся глубина изменения делегирования %(redelegation_depth)d выходит за " +"пределы разрешенного диапазона [0..%(max_count)d]" + +msgid "Request Token does not have an authorizing user id" +msgstr "" +"Маркер запроса не содержит ИД пользователя для предоставления прав доступа" + +#, python-format +msgid "" +"Request attribute %(attribute)s must be less than or equal to %(size)i. The " +"server could not comply with the request because the attribute size is " +"invalid (too large). The client is assumed to be in error." +msgstr "" +"Атрибут запроса %(attribute)s не может быть больше %(size)i. Серверу не " +"удалось удовлетворить запрос, поскольку размер атрибута является " +"недопустимым (слишком большой). Предположительно, клиент находится в " +"состоянии ошибки." + +msgid "Request must have an origin query parameter" +msgstr "Запрос должен содержать параметр origin" + +msgid "Request token is expired" +msgstr "Срок действия маркера запроса истек" + +msgid "Request token not found" +msgstr "Маркер запроса не найден" + +msgid "Requested expiration time is more than redelegated trust can provide" +msgstr "" +"Запрошенное время истечения срока действия превышает значение, которое может " +"указать доверенный пользователь" + +#, python-format +msgid "" +"Requested redelegation depth of %(requested_count)d is greater than allowed " +"%(max_count)d" +msgstr "" +"Запрошенная глубина изменения делегирования %(requested_count)d превышает " +"разрешенную %(max_count)d" + +#, python-format +msgid "Role %s not found" +msgstr "Роль %s не найдена" + +msgid "" +"Running keystone via eventlet is deprecated as of Kilo in favor of running " +"in a WSGI server (e.g. mod_wsgi). Support for keystone under eventlet will " +"be removed in the \"M\"-Release." +msgstr "" +"Выполнение Keystone через библиотеку eventlet устарело начиная с выпуска " +"Kilo. Следует выполнять на сервере WSGI (например, mod_wsgi). Поддержка " +"keystone в библиотеке eventlet будет убрана в выпуске \"M\"." + +msgid "Scoping to both domain and project is not allowed" +msgstr "Назначать и домен, и проект в качестве области нельзя" + +msgid "Scoping to both domain and trust is not allowed" +msgstr "Назначать и домен, и группу доверия в качестве области нельзя" + +msgid "Scoping to both project and trust is not allowed" +msgstr "Назначать и проект, и группу доверия в качестве области нельзя" + +#, python-format +msgid "Service Provider %(sp)s is disabled" +msgstr "Поставщик службы %(sp)s отключен" + +msgid "Some of requested roles are not in redelegated trust" +msgstr "" +"Некоторые из запрошенных ролей не относятся к доверенному пользователю с " +"измененными полномочиями" + +msgid "Specify a domain or project, not both" +msgstr "Укажите домен или проект, но не то и другое" + +msgid "Specify a user or group, not both" +msgstr "Укажите пользователя или группу, но не то и другое" + +msgid "Specify one of domain or project" +msgstr "Укажите один домен или проект" + +msgid "Specify one of user or group" +msgstr "Укажите одного пользователя или группу" + +#, python-format +msgid "" +"String length exceeded.The length of string '%(string)s' exceeded the limit " +"of column %(type)s(CHAR(%(length)d))." +msgstr "" +"Превышена длина строки. Длина строки '%(string)s' превышает ограничение " +"столбца %(type)s(CHAR(%(length)d))." + +msgid "The --all option cannot be used with the --domain-name option" +msgstr "Параметр --all нельзя указывать вместе с параметром --domain-name" + +#, python-format +msgid "The Keystone configuration file %(config_file)s could not be found." +msgstr "Не удалось найти файл конфигурации Keystone %(config_file)s." + +#, python-format +msgid "" +"The Keystone domain-specific configuration has specified more than one SQL " +"driver (only one is permitted): %(source)s." +msgstr "" +"В конфигурации для домена Keystone указано несколько драйверов SQL (допустим " +"только один): %(source)s." + +msgid "The action you have requested has not been implemented." +msgstr "Запрошенное действие не реализовано." + +msgid "The authenticated user should match the trustor." +msgstr "Идентифицированный пользователь должен соответствовать доверителю." + +msgid "" +"The certificates you requested are not available. It is likely that this " +"server does not use PKI tokens otherwise this is the result of " +"misconfiguration." +msgstr "" +"Запрошенные сертификаты недоступны. Вероятно, данный сервер не использует " +"маркеры PKI, в противном случае, это является следствием ошибки в " +"конфигурации." + +#, python-format +msgid "" +"The password length must be less than or equal to %(size)i. The server could " +"not comply with the request because the password is invalid." +msgstr "" +"Длина пароля не должна превышать %(size)i. Сервер не может выполнить запрос, " +"поскольку пароль недопустим." + +msgid "The request you have made requires authentication." +msgstr "Выданный запрос требует идентификации." + +msgid "The resource could not be found." +msgstr "Ресурс не найден." + +msgid "" +"The revoke call must not have both domain_id and project_id. This is a bug " +"in the Keystone server. The current request is aborted." +msgstr "" +"В вызове revoke должны быть указаны domain_id и project_id. Это ошибка в " +"коде сервера Keystone. Текущий запрос прерван." + +msgid "The service you have requested is no longer available on this server." +msgstr "Запрошенная служба более не доступна на данном сервере." + +#, python-format +msgid "" +"The specified parent region %(parent_region_id)s would create a circular " +"region hierarchy." +msgstr "" +"Заданная родительская область %(parent_region_id)s создаст круговую " +"структуру области." + +#, python-format +msgid "" +"The value of group %(group)s specified in the config should be a dictionary " +"of options" +msgstr "" +"Значение группы %(group)s, указанное в конфигурации, должно быть словарем " +"параметров" + +msgid "There should not be any non-oauth parameters" +msgstr "Не допускаются параметры, отличные от oauth" + +#, python-format +msgid "This is not a recognized Fernet payload version: %s" +msgstr "Это не распознанная версия полезной нагрузки Fernet: %s" + +msgid "" +"This is not a v2.0 Fernet token. Use v3 for trust, domain, or federated " +"tokens." +msgstr "" +"Это не маркер Fernet версии 2.0. Следует использовать версию 3 для надежных, " +"доменных или объединенных маркеров." + +msgid "" +"Timestamp not in expected format. The server could not comply with the " +"request since it is either malformed or otherwise incorrect. The client is " +"assumed to be in error." +msgstr "" +"Метка в неожиданном формате. Сервер не может выполнить запрос, поскольку он " +"либо искажен или неправилен. Клиент, как предполагается, является ошибочным." + +#, python-format +msgid "" +"To get a more detailed information on this error, re-run this command for " +"the specific domain, i.e.: keystone-manage domain_config_upload --domain-" +"name %s" +msgstr "" +"Для получения дополнительной информации об этой ошибке еще раз выполните эту " +"команду для конкретного домена. Пример: keystone-manage domain_config_upload " +"--domain-name %s" + +msgid "Token belongs to another user" +msgstr "Маркер принадлежит другому пользователю" + +msgid "Token does not belong to specified tenant." +msgstr "Маркер не принадлежит указанному арендатору." + +msgid "Trustee has no delegated roles." +msgstr "У доверенного лица нет делегированных ролей." + +msgid "Trustor is disabled." +msgstr "Доверитель отключен." + +#, python-format +msgid "" +"Trying to update group %(group)s, so that, and only that, group must be " +"specified in the config" +msgstr "" +"Изменение группы %(group)s, чтобы группа должна была указываться только в " +"конфигурации" + +#, python-format +msgid "" +"Trying to update option %(option)s in group %(group)s, but config provided " +"contains option %(option_other)s instead" +msgstr "" +"Изменение параметра %(option)s в группе %(group)s, однако переданная " +"конфигурация содержит параметр %(option_other)s вместо него" + +#, python-format +msgid "" +"Trying to update option %(option)s in group %(group)s, so that, and only " +"that, option must be specified in the config" +msgstr "" +"Изменение параметра %(option)s в группе %(group)s, чтобы параметр должен был " +"указываться только в конфигурации" + +msgid "" +"Unable to access the keystone database, please check it is configured " +"correctly." +msgstr "" +"Нет доступа к базе данных Keystone. Убедитесь, что она настроена правильно." + +#, python-format +msgid "Unable to consume trust %(trust_id)s, unable to acquire lock." +msgstr "Принять группу доверия %(trust_id)s и захватить блокировку невозможно." + +#, python-format +msgid "" +"Unable to delete region %(region_id)s because it or its child regions have " +"associated endpoints." +msgstr "" +"Не удалось удалить регион %(region_id)s: регион или его дочерние регионы " +"имеют связанные конечные точки." + +#, python-format +msgid "Unable to find valid groups while using mapping %(mapping_id)s" +msgstr "" +"Невозможно найти допустимые группы при использовании преобразования " +"%(mapping_id)s" + +#, python-format +msgid "" +"Unable to get a connection from pool id %(id)s after %(seconds)s seconds." +msgstr "" +"Не удалось получить соединение из пула с ИД %(id)s за %(seconds)s секунд." + +#, python-format +msgid "Unable to locate domain config directory: %s" +msgstr "Не удалось найти каталог конфигурации домена: %s" + +#, python-format +msgid "Unable to lookup user %s" +msgstr "Найти пользователя %s невозможно" + +#, python-format +msgid "" +"Unable to reconcile identity attribute %(attribute)s as it has conflicting " +"values %(new)s and %(old)s" +msgstr "" +"Согласовать атрибут идентификатора, %(attribute)s, невозможно, поскольку он " +"содержит конфликтующие значения %(new)s и %(old)s" + +#, python-format +msgid "" +"Unable to sign SAML assertion. It is likely that this server does not have " +"xmlsec1 installed, or this is the result of misconfiguration. Reason " +"%(reason)s" +msgstr "" +"Не удалось подписать утверждение SAML. Вероятно, на этом сервере не " +"установлена программа xmlsec1 или это результат неправильной настройки. " +"Причина: %(reason)s" + +msgid "Unable to sign token." +msgstr "Подписать маркер невозможно." + +#, python-format +msgid "Unexpected assignment type encountered, %s" +msgstr "Обнаружен непредвиденный тип назначения, %s" + +#, python-format +msgid "" +"Unexpected combination of grant attributes - User: %(user_id)s, Group: " +"%(group_id)s, Project: %(project_id)s, Domain: %(domain_id)s" +msgstr "" +"Непредвиденная комбинация атрибутов предоставления доступа - пользователь: " +"%(user_id)s, группа: %(group_id)s, проект: %(project_id)s, домен: " +"%(domain_id)s" + +#, python-format +msgid "Unexpected status requested for JSON Home response, %s" +msgstr "Запрошено неожиданное состояние для ответа JSON Home, %s" + +msgid "Unknown Target" +msgstr "Неизвестный целевой объект" + +#, python-format +msgid "Unknown domain '%(name)s' specified by --domain-name" +msgstr "В опции --domain-name указано неизвестное имя домена '%(name)s'" + +#, python-format +msgid "Unknown token version %s" +msgstr "Неизвестная версия маркера %s" + +#, python-format +msgid "Unregistered dependency: %(name)s for %(targets)s" +msgstr "Незарегистрированная зависимость %(name)s для %(targets)s" + +msgid "Update of `parent_id` is not allowed." +msgstr "Обновление `parent_id` не разрешено." + +msgid "Use a project scoped token when attempting to create a SAML assertion" +msgstr "Использовать локальный ключ проекта при создании утверждения SAML" + +#, python-format +msgid "User %(u_id)s is unauthorized for tenant %(t_id)s" +msgstr "У пользователя %(u_id)s нет доступа к арендатору %(t_id)s" + +#, python-format +msgid "User %(user_id)s already has role %(role_id)s in tenant %(tenant_id)s" +msgstr "" +"Пользователю %(user_id)s уже присвоена роль %(role_id)s в арендаторе " +"%(tenant_id)s" + +#, python-format +msgid "User %(user_id)s has no access to domain %(domain_id)s" +msgstr "У пользователя %(user_id)s нет доступа к домену %(domain_id)s" + +#, python-format +msgid "User %(user_id)s has no access to project %(project_id)s" +msgstr "У пользователя %(user_id)s нет доступа к проекту %(project_id)s" + +#, python-format +msgid "User %(user_id)s is already a member of group %(group_id)s" +msgstr "Пользователь %(user_id)s уже является участником группы %(group_id)s" + +#, python-format +msgid "User '%(user_id)s' not found in group '%(group_id)s'" +msgstr "Пользователь '%(user_id)s' не найден в группе '%(group_id)s'" + +msgid "User IDs do not match" +msgstr "ИД пользователей не совпадают" + +#, python-format +msgid "User is disabled: %s" +msgstr "Пользователь отключен: %s" + +msgid "User is not a member of the requested project" +msgstr "Пользователь не является участником запрошенного проекта" + +msgid "User is not a trustee." +msgstr "Пользователь не является доверенным лицом." + +msgid "User not found" +msgstr "Пользователь не найден" + +#, python-format +msgid "User type %s not supported" +msgstr "Тип пользователя %s не поддерживается" + +msgid "You are not authorized to perform the requested action." +msgstr "У вас нет прав на выполнение запрашиваемого действия." + +#, python-format +msgid "You are not authorized to perform the requested action: %(action)s" +msgstr "У вас нет прав на выполнение запрошенного действия: %(action)s" + +msgid "`key_mangler` functions must be callable." +msgstr "Функции `key_mangler` должны быть доступны для вызова." + +msgid "`key_mangler` option must be a function reference" +msgstr "Опция `key_mangler` должна быть ссылкой на функцию" + +msgid "any options" +msgstr "любые параметры" + +msgid "auth_type is not Negotiate" +msgstr "auth_type отличен от Negotiate" + +msgid "authorizing user does not have role required" +msgstr "" +"пользователю, предоставляющему права доступа, не присвоена требуемая роль" + +msgid "cache_collection name is required" +msgstr "имя cache_collection является обязательным" + +#, python-format +msgid "cannot create a project in a branch containing a disabled project: %s" +msgstr "Нельзя создать проект в ветви, содержащей отключенный проект: %s" + +msgid "cannot create a project within a different domain than its parents." +msgstr "" +"Нельзя создать проект в домене, отличном от домена, в котором находятся его " +"родительские объекты." + +msgid "cannot delete a domain that is enabled, please disable it first." +msgstr "невозможно удалить работающий домен, вначале отключите его." + +#, python-format +msgid "cannot delete the project %s since it is not a leaf in the hierarchy." +msgstr "" +"Нельзя удалить проект %s, так как он не является конечным объектом в " +"структуре." + +#, python-format +msgid "cannot disable project %s since its subtree contains enabled projects" +msgstr "" +"Нельзя отключить проект %s, так как его поддерево содержит включенные проекты" + +#, python-format +msgid "cannot enable project %s since it has disabled parents" +msgstr "" +"Нельзя включить проект %s, так как у него отключены родительские объекты" + +msgid "database db_name is required" +msgstr "db_name базы данных является обязательным" + +msgid "db_hosts value is required" +msgstr "Значение db_hosts является обязательным" + +msgid "delete the default domain" +msgstr "удалить домен по умолчанию" + +#, python-format +msgid "group %(group)s" +msgstr "группа %(group)s" + +msgid "" +"idp_contact_type must be one of: [technical, other, support, administrative " +"or billing." +msgstr "" +"Значение idp_contact_type должно быть одним из следующих: technical, other, " +"support, administrative или billing." + +msgid "integer value expected for mongo_ttl_seconds" +msgstr "для атрибута mongo_ttl_seconds ожидается целочисленное значение" + +msgid "integer value expected for w (write concern attribute)" +msgstr "для w (атрибут участия в записи) ожидается целочисленное значение" + +#, python-format +msgid "invalid date format %s" +msgstr "Недопустимый формат даты %s" + +#, python-format +msgid "max hierarchy depth reached for %s branch." +msgstr "Для ветви %s достигнута максимальная глубина иерархии." + +msgid "no ssl support available" +msgstr "отсутствует поддержка ssl" + +#, python-format +msgid "option %(option)s in group %(group)s" +msgstr "параметр %(option)s в группе %(group)s" + +msgid "pad must be single character" +msgstr "заполнитель должен соответствовать одному символу" + +msgid "padded base64url text must be multiple of 4 characters" +msgstr "размер текста base64url с заполнителями должен быть кратен 4 символам" + +msgid "provided consumer key does not match stored consumer key" +msgstr "переданный ключ приемника не совпадает с сохраненным" + +msgid "provided request key does not match stored request key" +msgstr "переданный ключ запроса не совпадает с сохраненным" + +msgid "provided verifier does not match stored verifier" +msgstr "переданная функция проверки не совпадает с сохраненной" + +msgid "region not type dogpile.cache.CacheRegion" +msgstr "регион не относится к типу dogpile.cache.CacheRegion" + +msgid "remaining_uses must be a positive integer or null." +msgstr "" +"Значение remaining_uses должно быть положительным целым числом или равным " +"нулю." + +msgid "remaining_uses must not be set if redelegation is allowed" +msgstr "" +"Если включено изменение делегирования, параметр remaining_uses не должен " +"быть задан" + +msgid "replicaset_name required when use_replica is True" +msgstr "" +"replicaset_name является обязательным, если для use_replica задано значение " +"True" + +#, python-format +msgid "" +"request to update group %(group)s, but config provided contains group " +"%(group_other)s instead" +msgstr "" +"запрос на изменение группы %(group)s, однако переданная конфигурация " +"содержит группу %(group_other)s вместо нее" + +msgid "rescope a scoped token" +msgstr "Изменить область помещенного в область ключа" + +#, python-format +msgid "text is multiple of 4, but pad \"%s\" occurs before 2nd to last char" +msgstr "" +"размер текста кратен 4, но заполнитель \"%s\" встречается перед 2-м и до " +"последнего символа" + +#, python-format +msgid "text is multiple of 4, but pad \"%s\" occurs before non-pad last char" +msgstr "" +"размер текста кратен 4, но заполнитель \"%s\" встречается перед последним " +"символом без заполнителя" + +#, python-format +msgid "text is not a multiple of 4, but contains pad \"%s\"" +msgstr "размер текста не кратен 4, но содержит заполнитель \"%s\"" + +#, python-format +msgid "tls_cacertdir %s not found or is not a directory" +msgstr "tls_cacertdir %s не найден или не является каталогом" + +#, python-format +msgid "tls_cacertfile %s not found or is not a file" +msgstr "tls_cacertfile %s не найден или не является файлом" + +#, python-format +msgid "token reference must be a KeystoneToken type, got: %s" +msgstr "Ссылка на маркер должна относиться к типу KeystoneToken, а получено %s" diff --git a/keystone-moon/keystone/locale/tr_TR/LC_MESSAGES/keystone-log-critical.po b/keystone-moon/keystone/locale/tr_TR/LC_MESSAGES/keystone-log-critical.po index 7d486e84..78c74c70 100644 --- a/keystone-moon/keystone/locale/tr_TR/LC_MESSAGES/keystone-log-critical.po +++ b/keystone-moon/keystone/locale/tr_TR/LC_MESSAGES/keystone-log-critical.po @@ -3,21 +3,23 @@ # This file is distributed under the same license as the keystone project. # # Translators: +# OpenStack Infra <zanata@openstack.org>, 2015. #zanata msgid "" msgstr "" -"Project-Id-Version: Keystone\n" +"Project-Id-Version: keystone 8.0.0.0b4.dev56\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" -"POT-Creation-Date: 2015-08-06 06:28+0000\n" -"PO-Revision-Date: 2015-08-04 13:49+0000\n" +"POT-Creation-Date: 2015-09-21 06:08+0000\n" +"PO-Revision-Date: 2015-08-04 01:49+0000\n" "Last-Translator: İşbaran Akçayır <isbaran@gmail.com>\n" "Language-Team: Turkish (Turkey) (http://www.transifex.com/openstack/keystone/" "language/tr_TR/)\n" -"Language: tr_TR\n" +"Language: tr-TR\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" "Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Zanata 3.7.1\n" #, python-format msgid "Unable to open template file %s" diff --git a/keystone-moon/keystone/locale/tr_TR/LC_MESSAGES/keystone-log-error.po b/keystone-moon/keystone/locale/tr_TR/LC_MESSAGES/keystone-log-error.po index cded46bb..dd35769a 100644 --- a/keystone-moon/keystone/locale/tr_TR/LC_MESSAGES/keystone-log-error.po +++ b/keystone-moon/keystone/locale/tr_TR/LC_MESSAGES/keystone-log-error.po @@ -3,21 +3,23 @@ # This file is distributed under the same license as the keystone project. # # Translators: +# OpenStack Infra <zanata@openstack.org>, 2015. #zanata msgid "" msgstr "" -"Project-Id-Version: Keystone\n" +"Project-Id-Version: keystone 8.0.0.0b4.dev56\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" -"POT-Creation-Date: 2015-08-06 06:28+0000\n" -"PO-Revision-Date: 2015-08-04 13:50+0000\n" +"POT-Creation-Date: 2015-09-21 06:08+0000\n" +"PO-Revision-Date: 2015-08-04 01:50+0000\n" "Last-Translator: İşbaran Akçayır <isbaran@gmail.com>\n" "Language-Team: Turkish (Turkey) (http://www.transifex.com/openstack/keystone/" "language/tr_TR/)\n" -"Language: tr_TR\n" +"Language: tr-TR\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" "Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Zanata 3.7.1\n" msgid "Cannot retrieve Authorization headers" msgstr "Yetkilendirme başlıkları alınamıyor" @@ -70,10 +72,6 @@ msgstr "" "<istemci>:<bağlantı noktası> biçimine sahip olduğunu ve bu bağlantı " "noktasında hata ayıklama sürecinin dinlediğini doğrulayın." -#, python-format -msgid "Error when signing assertion, reason: %(reason)s" -msgstr "Teyit imzalanırken hata, sebep: %(reason)s" - msgid "Failed to construct notifier" msgstr "Bildirici inşa etme başarısız" diff --git a/keystone-moon/keystone/locale/tr_TR/LC_MESSAGES/keystone-log-info.po b/keystone-moon/keystone/locale/tr_TR/LC_MESSAGES/keystone-log-info.po index 5b6da88f..65f66f3b 100644 --- a/keystone-moon/keystone/locale/tr_TR/LC_MESSAGES/keystone-log-info.po +++ b/keystone-moon/keystone/locale/tr_TR/LC_MESSAGES/keystone-log-info.po @@ -3,21 +3,23 @@ # This file is distributed under the same license as the keystone project. # # Translators: +# OpenStack Infra <zanata@openstack.org>, 2015. #zanata msgid "" msgstr "" -"Project-Id-Version: Keystone\n" +"Project-Id-Version: keystone 8.0.0.0b4.dev56\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" -"POT-Creation-Date: 2015-08-06 06:28+0000\n" -"PO-Revision-Date: 2015-08-04 13:49+0000\n" +"POT-Creation-Date: 2015-09-21 06:08+0000\n" +"PO-Revision-Date: 2015-08-04 01:49+0000\n" "Last-Translator: İşbaran Akçayır <isbaran@gmail.com>\n" "Language-Team: Turkish (Turkey) (http://www.transifex.com/openstack/keystone/" "language/tr_TR/)\n" -"Language: tr_TR\n" +"Language: tr-TR\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" "Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Zanata 3.7.1\n" #, python-format msgid "" diff --git a/keystone-moon/keystone/locale/tr_TR/LC_MESSAGES/keystone-log-warning.po b/keystone-moon/keystone/locale/tr_TR/LC_MESSAGES/keystone-log-warning.po index 1fda963e..d15319e2 100644 --- a/keystone-moon/keystone/locale/tr_TR/LC_MESSAGES/keystone-log-warning.po +++ b/keystone-moon/keystone/locale/tr_TR/LC_MESSAGES/keystone-log-warning.po @@ -3,29 +3,28 @@ # This file is distributed under the same license as the keystone project. # # Translators: +# OpenStack Infra <zanata@openstack.org>, 2015. #zanata msgid "" msgstr "" -"Project-Id-Version: Keystone\n" +"Project-Id-Version: keystone 8.0.0.0b4.dev56\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" -"POT-Creation-Date: 2015-08-16 06:06+0000\n" -"PO-Revision-Date: 2015-08-11 08:29+0000\n" +"POT-Creation-Date: 2015-09-21 06:08+0000\n" +"PO-Revision-Date: 2015-09-03 12:54+0000\n" "Last-Translator: openstackjenkins <jenkins@openstack.org>\n" "Language-Team: Turkish (Turkey) (http://www.transifex.com/openstack/keystone/" "language/tr_TR/)\n" -"Language: tr_TR\n" +"Language: tr-TR\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" "Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Zanata 3.7.1\n" #, python-format msgid "%s is not a dogpile.proxy.ProxyBackend" msgstr "%s dogpile.proxy.ProxyBackend değil" -msgid "Auth context already exists in the request environment" -msgstr "Yetki içeriği istenen ortamda zaten var" - #, python-format msgid "Authorization failed. %(exception)s from %(remote_addr)s" msgstr "Yetkilendirme başarısız. %(remote_addr)s den %(exception)s" diff --git a/keystone-moon/keystone/locale/tr_TR/LC_MESSAGES/keystone.po b/keystone-moon/keystone/locale/tr_TR/LC_MESSAGES/keystone.po index 6b962cfd..5a378314 100644 --- a/keystone-moon/keystone/locale/tr_TR/LC_MESSAGES/keystone.po +++ b/keystone-moon/keystone/locale/tr_TR/LC_MESSAGES/keystone.po @@ -6,12 +6,13 @@ # Alper Çiftçi <alprciftci@gmail.com>, 2015 # Andreas Jaeger <jaegerandi@gmail.com>, 2015 # catborise <muhammetalisag@gmail.com>, 2013 +# OpenStack Infra <zanata@openstack.org>, 2015. #zanata msgid "" msgstr "" -"Project-Id-Version: Keystone\n" +"Project-Id-Version: keystone 9.0.0.dev14\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" -"POT-Creation-Date: 2015-08-16 06:06+0000\n" -"PO-Revision-Date: 2015-08-15 18:05+0000\n" +"POT-Creation-Date: 2015-09-24 06:09+0000\n" +"PO-Revision-Date: 2015-09-03 12:54+0000\n" "Last-Translator: openstackjenkins <jenkins@openstack.org>\n" "Language-Team: Turkish (Turkey) (http://www.transifex.com/openstack/keystone/" "language/tr_TR/)\n" @@ -379,14 +380,6 @@ msgstr "Alan kapsamlı jeton desteklenmiyor" #, python-format msgid "" -"Domain specific sql drivers are not supported via the Identity API. One is " -"specified in /domains/%s/config" -msgstr "" -"Alana özel sql sürücüleri Kimlik API'si tarafından desteklenmiyor. Birisi /" -"domains/%s/config içinde tanımlanmış" - -#, python-format -msgid "" "Domain: %(domain)s already has a configuration defined - ignoring file: " "%(file)s." msgstr "" diff --git a/keystone-moon/keystone/locale/zh_CN/LC_MESSAGES/keystone-log-critical.po b/keystone-moon/keystone/locale/zh_CN/LC_MESSAGES/keystone-log-critical.po index cbdab8a4..9f87b4ea 100644 --- a/keystone-moon/keystone/locale/zh_CN/LC_MESSAGES/keystone-log-critical.po +++ b/keystone-moon/keystone/locale/zh_CN/LC_MESSAGES/keystone-log-critical.po @@ -3,21 +3,23 @@ # This file is distributed under the same license as the keystone project. # # Translators: +# OpenStack Infra <zanata@openstack.org>, 2015. #zanata msgid "" msgstr "" -"Project-Id-Version: Keystone\n" +"Project-Id-Version: keystone 8.0.0.0b4.dev56\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" -"POT-Creation-Date: 2015-08-06 06:28+0000\n" -"PO-Revision-Date: 2014-08-31 15:19+0000\n" +"POT-Creation-Date: 2015-09-21 06:08+0000\n" +"PO-Revision-Date: 2014-08-31 03:19+0000\n" "Last-Translator: openstackjenkins <jenkins@openstack.org>\n" "Language-Team: Chinese (China) (http://www.transifex.com/openstack/keystone/" "language/zh_CN/)\n" -"Language: zh_CN\n" +"Language: zh-CN\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" "Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Zanata 3.7.1\n" #, python-format msgid "Unable to open template file %s" diff --git a/keystone-moon/keystone/locale/zh_CN/LC_MESSAGES/keystone-log-error.po b/keystone-moon/keystone/locale/zh_CN/LC_MESSAGES/keystone-log-error.po index da273412..6017737b 100644 --- a/keystone-moon/keystone/locale/zh_CN/LC_MESSAGES/keystone-log-error.po +++ b/keystone-moon/keystone/locale/zh_CN/LC_MESSAGES/keystone-log-error.po @@ -5,21 +5,23 @@ # Translators: # Xiao Xi LIU <liuxx@cn.ibm.com>, 2014 # 刘俊朋 <liujunpeng@inspur.com>, 2015 +# OpenStack Infra <zanata@openstack.org>, 2015. #zanata msgid "" msgstr "" -"Project-Id-Version: Keystone\n" +"Project-Id-Version: keystone 8.0.0.0b4.dev56\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" -"POT-Creation-Date: 2015-08-06 06:28+0000\n" -"PO-Revision-Date: 2015-06-26 17:13+0000\n" +"POT-Creation-Date: 2015-09-21 06:08+0000\n" +"PO-Revision-Date: 2015-06-26 05:13+0000\n" "Last-Translator: openstackjenkins <jenkins@openstack.org>\n" "Language-Team: Chinese (China) (http://www.transifex.com/openstack/keystone/" "language/zh_CN/)\n" -"Language: zh_CN\n" +"Language: zh-CN\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" "Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Zanata 3.7.1\n" msgid "Cannot retrieve Authorization headers" msgstr "无法获取认证头信息" @@ -63,10 +65,6 @@ msgstr "" "设置调试环境出错。请确保选项--debug-url 的格式是这样的<host>:<port> ,和确保" "有一个调试进程正在监听那个端口" -#, python-format -msgid "Error when signing assertion, reason: %(reason)s" -msgstr "对断言进行签名时出错,原因:%(reason)s" - msgid "Failed to construct notifier" msgstr "构造通知器失败" diff --git a/keystone-moon/keystone/locale/zh_CN/LC_MESSAGES/keystone-log-info.po b/keystone-moon/keystone/locale/zh_CN/LC_MESSAGES/keystone-log-info.po index 92f06dcb..46a6fa21 100644 --- a/keystone-moon/keystone/locale/zh_CN/LC_MESSAGES/keystone-log-info.po +++ b/keystone-moon/keystone/locale/zh_CN/LC_MESSAGES/keystone-log-info.po @@ -4,21 +4,23 @@ # # Translators: # Xiao Xi LIU <liuxx@cn.ibm.com>, 2014 +# OpenStack Infra <zanata@openstack.org>, 2015. #zanata msgid "" msgstr "" -"Project-Id-Version: Keystone\n" +"Project-Id-Version: keystone 8.0.0.0b4.dev56\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" -"POT-Creation-Date: 2015-08-06 06:28+0000\n" +"POT-Creation-Date: 2015-09-21 06:08+0000\n" "PO-Revision-Date: 2015-08-01 06:26+0000\n" "Last-Translator: openstackjenkins <jenkins@openstack.org>\n" "Language-Team: Chinese (China) (http://www.transifex.com/openstack/keystone/" "language/zh_CN/)\n" -"Language: zh_CN\n" +"Language: zh-CN\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" "Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Zanata 3.7.1\n" #, python-format msgid "" diff --git a/keystone-moon/keystone/locale/zh_CN/LC_MESSAGES/keystone.po b/keystone-moon/keystone/locale/zh_CN/LC_MESSAGES/keystone.po new file mode 100644 index 00000000..2a970c85 --- /dev/null +++ b/keystone-moon/keystone/locale/zh_CN/LC_MESSAGES/keystone.po @@ -0,0 +1,1151 @@ +# Chinese (Simplified, China) translations for keystone. +# Copyright (C) 2015 OpenStack Foundation +# This file is distributed under the same license as the keystone project. +# +# Translators: +# Zhong Chaoliang <charliezon@gmail.com>, 2013 +# Dongliang Yu <yudl.nju@gmail.com>, 2013 +# Lee Yao <yaoli111144@gmail.com>, 2013 +# 颜海峰 <yanheven@gmail.com>, 2014 +# Lucas Palm <lapalm@us.ibm.com>, 2015. #zanata +# OpenStack Infra <zanata@openstack.org>, 2015. #zanata +msgid "" +msgstr "" +"Project-Id-Version: keystone 9.0.0.dev14\n" +"Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" +"POT-Creation-Date: 2015-09-24 06:09+0000\n" +"PO-Revision-Date: 2015-09-03 12:54+0000\n" +"Last-Translator: openstackjenkins <jenkins@openstack.org>\n" +"Language-Team: Chinese (China) (http://www.transifex.com/openstack/keystone/" +"language/zh_CN/)\n" +"Plural-Forms: nplurals=1; plural=0\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" + +#, python-format +msgid "%(detail)s" +msgstr "%(detail)s" + +#, python-format +msgid "" +"%(event)s is not a valid notification event, must be one of: %(actions)s" +msgstr "%(event)s 不是有效通知事件,必须是下列其中一项:%(actions)s" + +#, python-format +msgid "%(host)s is not a trusted dashboard host" +msgstr "%(host)s 不是可信的仪表板主机" + +#, python-format +msgid "%(message)s %(amendment)s" +msgstr "%(message)s %(amendment)s" + +#, python-format +msgid "" +"%(mod_name)s doesn't provide database migrations. The migration repository " +"path at %(path)s doesn't exist or isn't a directory." +msgstr "" +"%(mod_name)s 未提供数据库迁移。%(path)s 处的迁移存储库路径不存在或者不是目" +"录。" + +#, python-format +msgid "%(property_name)s cannot be less than %(min_length)s characters." +msgstr "%(property_name)s 不能少于 %(min_length)s 个字符。" + +#, python-format +msgid "%(property_name)s is not a %(display_expected_type)s" +msgstr "%(property_name)s 不在 %(display_expected_type)s 之中" + +#, python-format +msgid "%(property_name)s should not be greater than %(max_length)s characters." +msgstr "%(property_name)s 不应该超过 %(max_length)s 个字符。" + +#, python-format +msgid "%s cannot be empty." +msgstr "%s 不能为空。" + +#, python-format +msgid "%s extension does not exist." +msgstr "%s 扩展不存在。" + +#, python-format +msgid "%s field is required and cannot be empty" +msgstr "%s 字段是必填字段,不能为空" + +#, python-format +msgid "%s field(s) cannot be empty" +msgstr "%s 字段不能为空" + +msgid "(Disable debug mode to suppress these details.)" +msgstr "(禁用调试方式以避免显示这些详细信息。)" + +msgid "--all option cannot be mixed with other options" +msgstr "--all 选项不能与其他选项一起使用" + +msgid "A project-scoped token is required to produce a service catalog." +msgstr "产生服务目录时需要项目范围的令牌。" + +msgid "Access token is expired" +msgstr "访问令牌已过期" + +msgid "Access token not found" +msgstr "找不到访问令牌" + +msgid "Additional authentications steps required." +msgstr "需要额外身份验证" + +msgid "An unexpected error occurred when retrieving domain configs" +msgstr "检索域配置时发生意外错误" + +#, python-format +msgid "An unexpected error occurred when trying to store %s" +msgstr "尝试存储 %s 时发生意外错误" + +msgid "An unexpected error prevented the server from fulfilling your request." +msgstr "意外错误阻止了服务器完成您的请求。" + +#, python-format +msgid "" +"An unexpected error prevented the server from fulfilling your request: " +"%(exception)s" +msgstr "意外错误导致服务器无法完成您的请求:%(exception)s" + +msgid "An unhandled exception has occurred: Could not find metadata." +msgstr "存在无法处理的异常:找不到元数据。" + +msgid "At least one option must be provided" +msgstr "必须至少提供一个选项" + +msgid "At least one option must be provided, use either --all or --domain-name" +msgstr "必须至少提供一个选项,请使用 --all 或 --domain-name" + +msgid "At least one role should be specified." +msgstr "应该至少指定一个角色。" + +msgid "Attempted to authenticate with an unsupported method." +msgstr "尝试使用未支持的方法进行验证" + +msgid "" +"Attempting to use OS-FEDERATION token with V2 Identity Service, use V3 " +"Authentication" +msgstr "正在尝试将 OS-FEDERATION 令牌与 V2 身份服务配合使用,请使用 V3 认证" + +msgid "Authentication plugin error." +msgstr "认证插件错误" + +msgid "Cannot authorize a request token with a token issued via delegation." +msgstr "无法对带有通过代理发出的令牌的请求令牌授权。" + +#, python-format +msgid "Cannot change %(option_name)s %(attr)s" +msgstr "无法更改 %(option_name)s %(attr)s" + +msgid "Cannot change Domain ID" +msgstr "无法更改域标识" + +msgid "Cannot change consumer secret" +msgstr "不能改变用户密码" + +msgid "Cannot change user ID" +msgstr "无法更改用户标识" + +msgid "Cannot change user name" +msgstr "无法更改用户名" + +#, python-format +msgid "Cannot create project with parent: %(project_id)s" +msgstr "无法创建具有父代的项目:%(project_id)s" + +#, python-format +msgid "Cannot duplicate name %s" +msgstr "不能重复名称 %s" + +msgid "Cannot list request tokens with a token issued via delegation." +msgstr "无法列示带有通过代理发出的令牌的请求令牌。" + +#, python-format +msgid "Cannot open certificate %(cert_file)s. Reason: %(reason)s" +msgstr "无法打开证书 %(cert_file)s。原因:%(reason)s" + +#, python-format +msgid "Cannot remove role that has not been granted, %s" +msgstr "无法除去尚未授予的角色 %s" + +msgid "" +"Cannot truncate a driver call without hints list as first parameter after " +"self " +msgstr "" +"在没有将 hints list 用作 self 后面的第一个参数的情况下,无法截断驱动程序调用" + +msgid "" +"Cannot use parents_as_list and parents_as_ids query params at the same time." +msgstr "无法同时使用 parents_as_list 和 parents_as_ids 查询参数。" + +msgid "" +"Cannot use subtree_as_list and subtree_as_ids query params at the same time." +msgstr "无法同时使用 subtree_as_list 和 subtree_as_ids 查询参数。" + +msgid "" +"Combining effective and group filter will always result in an empty list." +msgstr "将有效过滤器与组过滤器进行组合将始终产生空列表。" + +msgid "" +"Combining effective, domain and inherited filters will always result in an " +"empty list." +msgstr "将有效过滤器、域过滤器和继承的过滤器进行组合将始终产生空列表。" + +#, python-format +msgid "Conflict occurred attempting to store %(type)s - %(details)s" +msgstr "尝试存储 %(type)s 时发生冲突 - %(details)s" + +#, python-format +msgid "Conflicting region IDs specified: \"%(url_id)s\" != \"%(ref_id)s\"" +msgstr "指定的区域标识有冲突:“%(url_id)s”不等于“%(ref_id)s”" + +msgid "Consumer not found" +msgstr "找不到使用者" + +#, python-format +msgid "" +"Could not change immutable attribute(s) '%(attributes)s' in target %(target)s" +msgstr "未能更改目标 %(target)s 中的不可变属性 %(attributes)s " + +#, python-format +msgid "" +"Could not find %(group_or_option)s in domain configuration for domain " +"%(domain_id)s" +msgstr "在以下域的域配置中找不到 %(group_or_option)s:%(domain_id)s" + +#, python-format +msgid "Could not find Endpoint Group: %(endpoint_group_id)s" +msgstr "找不到端点组:%(endpoint_group_id)s" + +msgid "Could not find Identity Provider identifier in environment" +msgstr "在环境中,找不到“身份提供者”标识" + +#, python-format +msgid "Could not find Identity Provider: %(idp_id)s" +msgstr "找不到身份提供者:%(idp_id)s" + +#, python-format +msgid "Could not find Service Provider: %(sp_id)s" +msgstr "找不到服务提供程序:%(sp_id)s" + +#, python-format +msgid "Could not find credential: %(credential_id)s" +msgstr "找不到凭证:%(credential_id)s" + +#, python-format +msgid "Could not find domain: %(domain_id)s" +msgstr "找不到域:%(domain_id)s" + +#, python-format +msgid "Could not find endpoint: %(endpoint_id)s" +msgstr "找不到端点:%(endpoint_id)s" + +#, python-format +msgid "" +"Could not find federated protocol %(protocol_id)s for Identity Provider: " +"%(idp_id)s" +msgstr "找不到身份提供者 %(idp_id)s 的联合协议 %(protocol_id)s " + +#, python-format +msgid "Could not find group: %(group_id)s" +msgstr "找不到组:%(group_id)s" + +#, python-format +msgid "Could not find mapping: %(mapping_id)s" +msgstr "找不到映射:%(mapping_id)s" + +msgid "Could not find policy association" +msgstr "找不到策略关联" + +#, python-format +msgid "Could not find policy: %(policy_id)s" +msgstr "找不到策略:%(policy_id)s" + +#, python-format +msgid "Could not find project: %(project_id)s" +msgstr "找不到项目:%(project_id)s" + +#, python-format +msgid "Could not find region: %(region_id)s" +msgstr "找不到区域:%(region_id)s" + +msgid "Could not find role" +msgstr "找不到角色" + +#, python-format +msgid "" +"Could not find role assignment with role: %(role_id)s, user or group: " +"%(actor_id)s, project or domain: %(target_id)s" +msgstr "" +"找不到角色分配,角色为 %(role_id)s,用户或组为 %(actor_id)s,项目或域为 " +"%(target_id)s" + +#, python-format +msgid "Could not find role: %(role_id)s" +msgstr "找不到角色:%(role_id)s" + +#, python-format +msgid "Could not find service: %(service_id)s" +msgstr "找不到服务:%(service_id)s" + +#, python-format +msgid "Could not find token: %(token_id)s" +msgstr "找不到令牌:%(token_id)s" + +#, python-format +msgid "Could not find trust: %(trust_id)s" +msgstr "找不到信任:%(trust_id)s" + +#, python-format +msgid "Could not find user: %(user_id)s" +msgstr "找不到用户:%(user_id)s" + +#, python-format +msgid "Could not find version: %(version)s" +msgstr "找不到版本:%(version)s" + +#, python-format +msgid "Could not find: %(target)s" +msgstr "找不到 %(target)s" + +msgid "Could not validate the access token" +msgstr "未能验证访问令牌" + +msgid "Credential belongs to another user" +msgstr "凭证属于另一用户" + +#, python-format +msgid "Database at /domains/%s/config" +msgstr "位于 /domains/%s/config 处的数据库" + +msgid "" +"Disabling an entity where the 'enable' attribute is ignored by configuration." +msgstr "正在禁用实体,在此情况下,“enable”属性已由配置忽略。" + +#, python-format +msgid "Domain (%s)" +msgstr "域 (%s)" + +#, python-format +msgid "Domain cannot be named %s" +msgstr "无法将域命名为 %s" + +#, python-format +msgid "Domain cannot have ID %s" +msgstr "域不能具有标识 %s" + +#, python-format +msgid "Domain is disabled: %s" +msgstr "域已禁用:%s" + +msgid "Domain metadata not supported by LDAP" +msgstr "LDAP 不支持域元数据" + +msgid "Domain scoped token is not supported" +msgstr "作用域限定到域的令牌不受支持" + +#, python-format +msgid "" +"Domain: %(domain)s already has a configuration defined - ignoring file: " +"%(file)s." +msgstr "域 %(domain)s 已定义配置 - 正在忽略以下文件:%(file)s。" + +msgid "Domains are read-only against LDAP" +msgstr "对于 LDAP,域为只读" + +msgid "Duplicate Entry" +msgstr "重复条目" + +#, python-format +msgid "Duplicate ID, %s." +msgstr "标识 %s 重复。" + +#, python-format +msgid "Duplicate name, %s." +msgstr "名称 %s 重复。" + +msgid "Enabled field must be a boolean" +msgstr "已启用的字段必须为布尔值" + +msgid "Enabled field should be a boolean" +msgstr "已启用的字段应该为布尔值" + +#, python-format +msgid "Endpoint %(endpoint_id)s not found in project %(project_id)s" +msgstr "在项目 %(project_id)s 中找不到端点 %(endpoint_id)s" + +msgid "Endpoint Group Project Association not found" +msgstr "找不到端点组项目关联" + +msgid "Ensure configuration option idp_entity_id is set." +msgstr "请确保设置了配置选项 idp_entity_id。" + +msgid "Ensure configuration option idp_sso_endpoint is set." +msgstr "请确保设置了配置选项 idp_sso_endpoint。" + +#, python-format +msgid "" +"Error parsing configuration file for domain: %(domain)s, file: %(file)s." +msgstr "解析域 %(domain)s 的配置文件时出错,文件为 %(file)s。" + +#, python-format +msgid "Error while reading metadata file, %(reason)s" +msgstr "读取元数据文件时出错,原因为 %(reason)s" + +#, python-format +msgid "Expected dict or list: %s" +msgstr "期望字典或者列表: %s" + +msgid "" +"Expected signing certificates are not available on the server. Please check " +"Keystone configuration." +msgstr "在服务器上,期望的签名证书不可用。请检查 Keystone 配置。" + +#, python-format +msgid "" +"Expecting to find %(attribute)s in %(target)s - the server could not comply " +"with the request since it is either malformed or otherwise incorrect. The " +"client is assumed to be in error." +msgstr "" +"期望在 %(target)s 中找到 %(attribute)s - 服务器未能遵照请求,因为它的格式或者" +"其他方面不正确。客户机被认为发生错误。" + +#, python-format +msgid "Failed to start the %(name)s server" +msgstr "未能启动 %(name)s 服务器" + +msgid "Failed to validate token" +msgstr "token验证失败" + +msgid "Federation token is expired" +msgstr "联合令牌已到期" + +#, python-format +msgid "" +"Field \"remaining_uses\" is set to %(value)s while it must not be set in " +"order to redelegate a trust" +msgstr "" +"字段“remaining_uses”已设置为 %(value)s,尽管为了重新委派信任,不能设置该字段" + +msgid "Found invalid token: scoped to both project and domain." +msgstr "发现无效令牌:范围同时为项目和域。" + +#, python-format +msgid "Group %(group)s is not supported for domain specific configurations" +msgstr "特定于域的配置不支持组 %(group)s" + +#, python-format +msgid "" +"Group %(group_id)s returned by mapping %(mapping_id)s was not found in the " +"backend." +msgstr "在后端中,找不到由映射 %(mapping_id)s 返回的组 %(group_id)s。" + +#, python-format +msgid "" +"Group membership across backend boundaries is not allowed, group in question " +"is %(group_id)s, user is %(user_id)s" +msgstr "" +"不允许使用跨后端边界的组成员资格,所提到的组为%(group_id)s,用户为 " +"%(user_id)s" + +#, python-format +msgid "ID attribute %(id_attr)s not found in LDAP object %(dn)s" +msgstr "在 LDAP 对象 %(dn)s 中,找不到标识属性 %(id_attr)s" + +#, python-format +msgid "Identity Provider %(idp)s is disabled" +msgstr "身份提供者 %(idp)s 已禁用" + +msgid "" +"Incoming identity provider identifier not included among the accepted " +"identifiers." +msgstr "新的“身份提供者”标识未包含在已接受的标识中。" + +#, python-format +msgid "Invalid LDAP TLS certs option: %(option)s. Choose one of: %(options)s" +msgstr "LDAP TLS 证书选项 %(option)s 无效。请选择下列其中一项:%(options)s" + +#, python-format +msgid "Invalid LDAP TLS_AVAIL option: %s. TLS not available" +msgstr "无效的LDAP TLS_AVAIL 选项: %s.TLS无效" + +#, python-format +msgid "Invalid LDAP deref option: %(option)s. Choose one of: %(options)s" +msgstr "LDAP deref 选项 %(option)s 无效。请选择下列其中一项:%(options)s" + +#, python-format +msgid "Invalid LDAP scope: %(scope)s. Choose one of: %(options)s" +msgstr "无效的 LDAP作用域: %(scope)s. 选择以下选项之一: %(options)s" + +msgid "Invalid TLS / LDAPS combination" +msgstr "无效的 TLS / LDAPS 组合" + +#, python-format +msgid "Invalid audit info data type: %(data)s (%(type)s)" +msgstr "无效审计信息数据类型:%(data)s (%(type)s)" + +msgid "Invalid blob in credential" +msgstr "凭证中的 BLOB 无效" + +#, python-format +msgid "" +"Invalid domain name: %(domain)s found in config file name: %(file)s - " +"ignoring this file." +msgstr "在配置文件名 %(file)s 中找到的域名 %(domain)s 无效 - 正在忽略此文件。" + +#, python-format +msgid "Invalid domain specific configuration: %(reason)s" +msgstr "特定于域的配置无效:%(reason)s" + +#, python-format +msgid "Invalid input for field '%(path)s'. The value is '%(value)s'." +msgstr "对字段“%(path)s”的输入无效。值为“%(value)s”。" + +msgid "Invalid limit value" +msgstr "限制值无效" + +#, python-format +msgid "" +"Invalid mix of entities for policy association - only Endpoint, Service or " +"Region+Service allowed. Request was - Endpoint: %(endpoint_id)s, Service: " +"%(service_id)s, Region: %(region_id)s" +msgstr "" +"用于策略关联的实体混合无效 - 仅允许“端点”、“服务”或“区域 + 服务”。请求为 - 端" +"点:%(endpoint_id)s,服务:%(service_id)s,区域:%(region_id)s" + +#, python-format +msgid "" +"Invalid rule: %(identity_value)s. Both 'groups' and 'domain' keywords must " +"be specified." +msgstr "规则 %(identity_value)s 无效。必须同时指定关键字“groups”和“domain”。" + +msgid "Invalid signature" +msgstr "签名无效" + +#, python-format +msgid "" +"Invalid ssl_cert_reqs value of %s, must be one of \"NONE\", \"OPTIONAL\", " +"\"REQUIRED\"" +msgstr "" +"ssl_cert_reqs 值 %s 无效,必须是下列其中一项:“NONE”、“OPTIONAL”和“REQUIRED”" + +msgid "Invalid user / password" +msgstr "用户/密码无效" + +msgid "Invalid username or password" +msgstr "无效用户名或密码" + +#, python-format +msgid "KVS region %s is already configured. Cannot reconfigure." +msgstr "KVS 区域 %s 已配置。无法重新配置。" + +#, python-format +msgid "Key Value Store not configured: %s" +msgstr "未配置键值存储:%s" + +#, python-format +msgid "LDAP %s create" +msgstr "LDAP %s 创建" + +#, python-format +msgid "LDAP %s delete" +msgstr "LDAP %s 删除" + +#, python-format +msgid "LDAP %s update" +msgstr "LDAP %s 更新" + +#, python-format +msgid "Lock Timeout occurred for key, %(target)s" +msgstr "对于键 %(target)s,发生锁定超时" + +#, python-format +msgid "Lock key must match target key: %(lock)s != %(target)s" +msgstr "锁定键必须与目标键匹配:%(lock)s != %(target)s" + +#, python-format +msgid "Malformed endpoint URL (%(endpoint)s), see ERROR log for details." +msgstr "不正确的端点URL(%(endpoint)s), 查看错误日志获取详情" + +msgid "Marker could not be found" +msgstr "找不到标记符" + +#, python-format +msgid "Maximum lock attempts on %s occurred." +msgstr "已达到对 %s 的最大锁定尝试次数。" + +#, python-format +msgid "Member %(member)s is already a member of group %(group)s" +msgstr "成员 %(member)s 已属于组 %(group)s" + +#, python-format +msgid "Method not callable: %s" +msgstr "方法不可调用:%s" + +msgid "Missing entity ID from environment" +msgstr "环境中缺少实体标识" + +msgid "" +"Modifying \"redelegation_count\" upon redelegation is forbidden. Omitting " +"this parameter is advised." +msgstr "正在修改“redelegation_count”(当禁止重新委派时)。建议省略此参数。" + +msgid "Multiple domains are not supported" +msgstr "多个域不受支持" + +msgid "Must be called within an active lock context." +msgstr "必须在处于活动状态的锁定上下文内调用。" + +msgid "Must specify either domain or project" +msgstr "必须指定 domain 或 project" + +msgid "Name field is required and cannot be empty" +msgstr "名称字段是必填字段,不能为空" + +msgid "" +"No Authorization headers found, cannot proceed with OAuth related calls, if " +"running under HTTPd or Apache, ensure WSGIPassAuthorization is set to On." +msgstr "" +"找不到任何授权头,无法继续进行与 OAuth 相关的调用,如果是通过 HTTP 或 Apache " +"运行,请确保 WSGIPassAuthorization 设置为开启。" + +msgid "No authenticated user" +msgstr "不存在任何已认证的用户" + +msgid "" +"No encryption keys found; run keystone-manage fernet_setup to bootstrap one." +msgstr "" +"找不到任何加密密钥;请针对引导程序 1 运行 keystone-manage fernet_setup。" + +msgid "No options specified" +msgstr "无选项指定" + +#, python-format +msgid "No policy is associated with endpoint %(endpoint_id)s." +msgstr "没有任何策略与端点 %(endpoint_id)s 关联。" + +#, python-format +msgid "No remaining uses for trust: %(trust_id)s" +msgstr "对于信任 %(trust_id)s,不存在其余使用" + +msgid "Non-default domain is not supported" +msgstr "非缺省域不受支持" + +msgid "One of the trust agents is disabled or deleted" +msgstr "其中一个信任代理已禁用或删除" + +#, python-format +msgid "" +"Option %(option)s found with no group specified while checking domain " +"configuration request" +msgstr "在检查域配置请求时,找到选项 %(option)s,但未指定任何组" + +#, python-format +msgid "" +"Option %(option)s in group %(group)s is not supported for domain specific " +"configurations" +msgstr "特定于域的配置不支持组 %(group)s 中的选项 %(option)s" + +#, python-format +msgid "Project (%s)" +msgstr "项目 (%s)" + +#, python-format +msgid "Project is disabled: %s" +msgstr "项目已禁用:%s" + +msgid "Redelegation allowed for delegated by trust only" +msgstr "仅允许对“委派者”信任进行重新委派" + +#, python-format +msgid "" +"Remaining redelegation depth of %(redelegation_depth)d out of allowed range " +"of [0..%(max_count)d]" +msgstr "" +"其余重新委派深度 %(redelegation_depth)d 超出允许的范围 [0..%(max_count)d]" + +msgid "Request Token does not have an authorizing user id" +msgstr "请求令牌没有授权用户标识" + +#, python-format +msgid "" +"Request attribute %(attribute)s must be less than or equal to %(size)i. The " +"server could not comply with the request because the attribute size is " +"invalid (too large). The client is assumed to be in error." +msgstr "" +"请求属性 %(attribute)s 必须小于或等于 %(size)i。服务器未能遵照请求,因为属性" +"大小无效(太大)。客户机被认为发生错误。" + +msgid "Request must have an origin query parameter" +msgstr "请求必须具有源查询参数" + +msgid "Request token is expired" +msgstr "请求令牌已过期" + +msgid "Request token not found" +msgstr "找不到请求令牌" + +msgid "Requested expiration time is more than redelegated trust can provide" +msgstr "请求的到期时间超过重新委派的信任可提供的到期时间" + +#, python-format +msgid "" +"Requested redelegation depth of %(requested_count)d is greater than allowed " +"%(max_count)d" +msgstr "请求的重新委派深度 %(requested_count)d 超过允许的 %(max_count)d" + +#, python-format +msgid "Role %s not found" +msgstr "找不到角色 %s" + +msgid "" +"Running keystone via eventlet is deprecated as of Kilo in favor of running " +"in a WSGI server (e.g. mod_wsgi). Support for keystone under eventlet will " +"be removed in the \"M\"-Release." +msgstr "" +"自 Kilo 开始,建议不要通过 eventlet 运行 keystone,改为在 WSGI 服务器(例如 " +"mod_wsgi)中运行。在“M”发行版中,将移除对在 eventlet 下运行 keystone 的支持。" + +msgid "Scoping to both domain and project is not allowed" +msgstr "不允许同时将作用域限定到域和项目" + +msgid "Scoping to both domain and trust is not allowed" +msgstr "不允许同时将作用域限定到域和信任" + +msgid "Scoping to both project and trust is not allowed" +msgstr "不允许同时将作用域限定到项目和信任" + +#, python-format +msgid "Service Provider %(sp)s is disabled" +msgstr "服务提供程序 %(sp)s 已禁用" + +msgid "Some of requested roles are not in redelegated trust" +msgstr "某些所请求角色未在重新委派的信任中" + +msgid "Specify a domain or project, not both" +msgstr "请指定域或项目,但不是同时指定这两者" + +msgid "Specify a user or group, not both" +msgstr "请指定用户或组,但不是同时指定这两者" + +msgid "Specify one of domain or project" +msgstr "请指定域或项目" + +msgid "Specify one of user or group" +msgstr "请指定用户或组" + +#, python-format +msgid "" +"String length exceeded.The length of string '%(string)s' exceeded the limit " +"of column %(type)s(CHAR(%(length)d))." +msgstr "" +"字符串长度过长.字符串'%(string)s' 的长度超过列限制 %(type)s(字符" +"(%(length)d))." + +msgid "The --all option cannot be used with the --domain-name option" +msgstr "--all 选项不能与 --domain-name 选项配合使用" + +#, python-format +msgid "The Keystone configuration file %(config_file)s could not be found." +msgstr "找不到 Keystone 配置文件 %(config_file)s。" + +#, python-format +msgid "" +"The Keystone domain-specific configuration has specified more than one SQL " +"driver (only one is permitted): %(source)s." +msgstr "" +"特定于 Keystone 域的配置已指定多个 SQL 驱动程序(仅允许指定一个):" +"%(source)s。" + +msgid "The action you have requested has not been implemented." +msgstr "您请求的操作暂未被执行" + +msgid "The authenticated user should match the trustor." +msgstr "认证用户应匹配信任者。" + +msgid "" +"The certificates you requested are not available. It is likely that this " +"server does not use PKI tokens otherwise this is the result of " +"misconfiguration." +msgstr "" +"已请求的证书不可用。可能此服务器未使用 PKI 令牌,或者这是因为配置错误。" + +#, python-format +msgid "" +"The password length must be less than or equal to %(size)i. The server could " +"not comply with the request because the password is invalid." +msgstr "密码长度必须小于或等于 %(size)i。服务器未能遵照请求,因为密码无效。" + +msgid "The request you have made requires authentication." +msgstr "你的请求需要先授权" + +msgid "The resource could not be found." +msgstr "找不到该资源。" + +msgid "" +"The revoke call must not have both domain_id and project_id. This is a bug " +"in the Keystone server. The current request is aborted." +msgstr "" +"撤销调用不能同时具有 domain_id 和 project_id。这是 Keystone 服务器中的错误。" +"当前请求已异常中止。" + +msgid "The service you have requested is no longer available on this server." +msgstr "在此服务器上,已请求的服务不再可用。" + +#, python-format +msgid "" +"The specified parent region %(parent_region_id)s would create a circular " +"region hierarchy." +msgstr "指定的父区域 %(parent_region_id)s 将创建循环区域层次结构。" + +#, python-format +msgid "" +"The value of group %(group)s specified in the config should be a dictionary " +"of options" +msgstr "在配置中指定的组 %(group)s 的值应该是选项的字典" + +msgid "There should not be any non-oauth parameters" +msgstr "不应该存在任何非 oauth 参数" + +#, python-format +msgid "This is not a recognized Fernet payload version: %s" +msgstr "这不是可识别的 Fernet 有效内容版本:%s" + +msgid "" +"This is not a v2.0 Fernet token. Use v3 for trust, domain, or federated " +"tokens." +msgstr "这不是 V2.0 Fernet 令牌。请将 V3 用于信任、域或联合的令牌。" + +msgid "" +"Timestamp not in expected format. The server could not comply with the " +"request since it is either malformed or otherwise incorrect. The client is " +"assumed to be in error." +msgstr "" +"时间戳记未采用所需格式。服务器未能遵照请求,因为它的格式或者其他方面不正确。" +"客户机被认为发生错误。" + +#, python-format +msgid "" +"To get a more detailed information on this error, re-run this command for " +"the specific domain, i.e.: keystone-manage domain_config_upload --domain-" +"name %s" +msgstr "" +"要获取有关此错误的更详细信息,请针对特定域重新运行此命令,即:keystone-" +"manage domain_config_upload --domain-name %s" + +msgid "Token belongs to another user" +msgstr "令牌属于另一用户" + +msgid "Token does not belong to specified tenant." +msgstr "令牌不属于指定的租户。" + +msgid "Trustee has no delegated roles." +msgstr "托管人没有任何已委派的角色。" + +msgid "Trustor is disabled." +msgstr "Trustor被禁用" + +#, python-format +msgid "" +"Trying to update group %(group)s, so that, and only that, group must be " +"specified in the config" +msgstr "正在尝试更新组 %(group)s,因此仅存在以下要求:必须在配置中指定组" + +#, python-format +msgid "" +"Trying to update option %(option)s in group %(group)s, but config provided " +"contains option %(option_other)s instead" +msgstr "" +"正在尝试更新组 %(group)s 中的选项 %(option)s,但所提供配置反而包含选项 " +"%(option_other)s" + +#, python-format +msgid "" +"Trying to update option %(option)s in group %(group)s, so that, and only " +"that, option must be specified in the config" +msgstr "" +"正在尝试更新组 %(group)s 中的选项 %(option)s,因此仅存在以下要求:必须在配置" +"中指定选项" + +msgid "" +"Unable to access the keystone database, please check it is configured " +"correctly." +msgstr "无法访问 keystone 数据库,请检查它是否正确配置。" + +#, python-format +msgid "Unable to consume trust %(trust_id)s, unable to acquire lock." +msgstr "无法使用信任 %(trust_id)s,无法获取锁定。" + +#, python-format +msgid "" +"Unable to delete region %(region_id)s because it or its child regions have " +"associated endpoints." +msgstr "无法删除区域 %(region_id)s,因为它或它的子区域具有关联的端点。" + +#, python-format +msgid "Unable to find valid groups while using mapping %(mapping_id)s" +msgstr "使用映射 %(mapping_id)s 时,找不到有效组" + +#, python-format +msgid "" +"Unable to get a connection from pool id %(id)s after %(seconds)s seconds." +msgstr "在 %(seconds)s 秒之后,无法根据池标识 %(id)s 获取连接。" + +#, python-format +msgid "Unable to locate domain config directory: %s" +msgstr "找不到指定的域配置目录:%s" + +#, python-format +msgid "Unable to lookup user %s" +msgstr "无法查找用户 %s" + +#, python-format +msgid "" +"Unable to reconcile identity attribute %(attribute)s as it has conflicting " +"values %(new)s and %(old)s" +msgstr "无法协调身份属性 %(attribute)s,因为它具有冲突值%(new)s 和 %(old)s" + +#, python-format +msgid "" +"Unable to sign SAML assertion. It is likely that this server does not have " +"xmlsec1 installed, or this is the result of misconfiguration. Reason " +"%(reason)s" +msgstr "" +"无法对 SAML 断言进行签名。此服务器可能未安装 xmlsec1,或者这可能是由于配置错" +"误导致的。原因 %(reason)s" + +msgid "Unable to sign token." +msgstr "无法对令牌进行签名。" + +#, python-format +msgid "Unexpected assignment type encountered, %s" +msgstr "遇到意外的指派类型 %s" + +#, python-format +msgid "" +"Unexpected combination of grant attributes - User: %(user_id)s, Group: " +"%(group_id)s, Project: %(project_id)s, Domain: %(domain_id)s" +msgstr "" +"存在以下 grant 属性的意外组合 - 用户 %(user_id)s、组 %(group_id)s、项目 " +"%(project_id)s 和域 %(domain_id)s" + +#, python-format +msgid "Unexpected status requested for JSON Home response, %s" +msgstr "请求 JSON 主页响应时处于意外状态,%s" + +msgid "Unknown Target" +msgstr "目标未知" + +#, python-format +msgid "Unknown domain '%(name)s' specified by --domain-name" +msgstr "--domain-name 指定的“%(name)s”是未知域" + +#, python-format +msgid "Unknown token version %s" +msgstr "令牌版本 %s 未知" + +#, python-format +msgid "Unregistered dependency: %(name)s for %(targets)s" +msgstr "已针对 %(targets)s 注销依赖关系 %(name)s" + +msgid "Update of `parent_id` is not allowed." +msgstr "不允许更新“parent_id”。" + +msgid "Use a project scoped token when attempting to create a SAML assertion" +msgstr "当尝试创建 SAML 断言时,请使用项目范围的令牌" + +#, python-format +msgid "User %(u_id)s is unauthorized for tenant %(t_id)s" +msgstr "没有授权给用户%(u_id)s项目%(t_id)s的权限" + +#, python-format +msgid "User %(user_id)s already has role %(role_id)s in tenant %(tenant_id)s" +msgstr "在租户 %(tenant_id)s 中,用户 %(user_id)s 已具有角色 %(role_id)s" + +#, python-format +msgid "User %(user_id)s has no access to domain %(domain_id)s" +msgstr "用户%(user_id)s对域%(domain_id)s没有任何访问权限" + +#, python-format +msgid "User %(user_id)s has no access to project %(project_id)s" +msgstr "用户%(user_id)s 没有访问项目 %(project_id)s的权限" + +#, python-format +msgid "User %(user_id)s is already a member of group %(group_id)s" +msgstr "用户%(user_id)s 已是组 %(group_id)s 的成员" + +#, python-format +msgid "User '%(user_id)s' not found in group '%(group_id)s'" +msgstr "在组“%(group_id)s”中找不到用户“%(user_id)s”" + +msgid "User IDs do not match" +msgstr "用户ID不匹配" + +#, python-format +msgid "User is disabled: %s" +msgstr "用户已禁用:%s" + +msgid "User is not a member of the requested project" +msgstr "用户不是所请求项目的成员" + +msgid "User is not a trustee." +msgstr "用户不是受托人。" + +msgid "User not found" +msgstr "找不到用户" + +#, python-format +msgid "User type %s not supported" +msgstr "用户类型 %s 不受支持" + +msgid "You are not authorized to perform the requested action." +msgstr "您没有授权完成所请求的操作。" + +#, python-format +msgid "You are not authorized to perform the requested action: %(action)s" +msgstr "您无权执行请求的操作:%(action)s" + +msgid "`key_mangler` functions must be callable." +msgstr "“key_mangler”函数必须可调用。" + +msgid "`key_mangler` option must be a function reference" +msgstr "“key_mangler”选项必须为函数引用" + +msgid "any options" +msgstr "任何选项" + +msgid "auth_type is not Negotiate" +msgstr "auth_type 不是“Negotiate”" + +msgid "authorizing user does not have role required" +msgstr "授权用户没有必需的角色" + +msgid "cache_collection name is required" +msgstr "需要 cache_collection 名称" + +#, python-format +msgid "cannot create a project in a branch containing a disabled project: %s" +msgstr "无法在包含已禁用项目的分支中创建项目:%s" + +msgid "cannot create a project within a different domain than its parents." +msgstr "如果某个域不是项目的父代所在的域,那么无法在该域内创建该项目。" + +msgid "cannot delete a domain that is enabled, please disable it first." +msgstr "无法删除已启用的域,请先禁用该域。" + +#, python-format +msgid "cannot delete the project %s since it is not a leaf in the hierarchy." +msgstr "无法删除项目 %s,因为它不是层次结构中的叶。" + +#, python-format +msgid "cannot disable project %s since its subtree contains enabled projects" +msgstr "无法禁用项目 %s,因为其子树包含已启用的项目" + +#, python-format +msgid "cannot enable project %s since it has disabled parents" +msgstr "无法启用项目 %s,因为它具有已禁用的父代" + +msgid "database db_name is required" +msgstr "需要数据库 db_name" + +msgid "db_hosts value is required" +msgstr "需要 db_hosts 值" + +msgid "delete the default domain" +msgstr "请删除缺省域" + +#, python-format +msgid "group %(group)s" +msgstr "组 %(group)s" + +msgid "" +"idp_contact_type must be one of: [technical, other, support, administrative " +"or billing." +msgstr "" +"idp_contact_type 必须是下列其中一项:technical、other、support、" +"administrative 或 billing。" + +msgid "integer value expected for mongo_ttl_seconds" +msgstr "mongo_ttl_seconds 需要整数值" + +msgid "integer value expected for w (write concern attribute)" +msgstr "w(写相关属性)需要整数值" + +#, python-format +msgid "invalid date format %s" +msgstr "日期格式 %s 无效" + +#, python-format +msgid "max hierarchy depth reached for %s branch." +msgstr "对于 %s 分支,已达到最大层次结构深度。" + +msgid "no ssl support available" +msgstr "未提供 ssl 支持" + +#, python-format +msgid "option %(option)s in group %(group)s" +msgstr "组 %(group)s 中的选项 %(option)s" + +msgid "pad must be single character" +msgstr "填充项必须是单个字符" + +msgid "padded base64url text must be multiple of 4 characters" +msgstr "填充的 base64url 文本的字符数必须是 4 的倍数" + +msgid "provided consumer key does not match stored consumer key" +msgstr "提供的用户密钥与存储的用户密钥不符" + +msgid "provided request key does not match stored request key" +msgstr "提供的请求密钥与存储的请求密钥不匹配" + +msgid "provided verifier does not match stored verifier" +msgstr "提供的验证器与存储的验证器不匹配" + +msgid "region not type dogpile.cache.CacheRegion" +msgstr "区域的类型不是 dogpile.cache.CacheRegion" + +msgid "remaining_uses must be a positive integer or null." +msgstr "remaining_uses 必须为正整数或 Null。" + +msgid "remaining_uses must not be set if redelegation is allowed" +msgstr "如果允许重新委派,那么不能设置 remaining_uses" + +msgid "replicaset_name required when use_replica is True" +msgstr "当 use_replica 为 True 时,需要 replicaset_name" + +#, python-format +msgid "" +"request to update group %(group)s, but config provided contains group " +"%(group_other)s instead" +msgstr "请求更新组 %(group)s,但所提供配置反而包含组 %(group_other)s" + +msgid "rescope a scoped token" +msgstr "请重新确定带范围的令牌的范围" + +#, python-format +msgid "text is multiple of 4, but pad \"%s\" occurs before 2nd to last char" +msgstr "文本的字符数是 4 的倍数,但填充项“%s”出现在倒数第 2 个字符前面" + +#, python-format +msgid "text is multiple of 4, but pad \"%s\" occurs before non-pad last char" +msgstr "文本的字符数是 4 的倍数,但填充项“%s”出现在非填充的最后一个字符前面" + +#, python-format +msgid "text is not a multiple of 4, but contains pad \"%s\"" +msgstr "文本的字符数不是 4 的倍数,但文本包含填充项“%s”" + +#, python-format +msgid "tls_cacertdir %s not found or is not a directory" +msgstr "tls_cacertdir %s 未找到或者不是一个目录" + +#, python-format +msgid "tls_cacertfile %s not found or is not a file" +msgstr "tls_cacertfile %s 未找到或者不是一个文件" + +#, python-format +msgid "token reference must be a KeystoneToken type, got: %s" +msgstr "令牌引用必须为 KeystoneToken 类型,但收到:%s" diff --git a/keystone-moon/keystone/locale/zh_TW/LC_MESSAGES/keystone-log-critical.po b/keystone-moon/keystone/locale/zh_TW/LC_MESSAGES/keystone-log-critical.po index c2e8b9ea..fda12669 100644 --- a/keystone-moon/keystone/locale/zh_TW/LC_MESSAGES/keystone-log-critical.po +++ b/keystone-moon/keystone/locale/zh_TW/LC_MESSAGES/keystone-log-critical.po @@ -3,21 +3,23 @@ # This file is distributed under the same license as the keystone project. # # Translators: +# OpenStack Infra <zanata@openstack.org>, 2015. #zanata msgid "" msgstr "" -"Project-Id-Version: Keystone\n" +"Project-Id-Version: keystone 8.0.0.0b4.dev56\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" -"POT-Creation-Date: 2015-08-06 06:28+0000\n" -"PO-Revision-Date: 2014-08-31 15:19+0000\n" +"POT-Creation-Date: 2015-09-21 06:08+0000\n" +"PO-Revision-Date: 2014-08-31 03:19+0000\n" "Last-Translator: openstackjenkins <jenkins@openstack.org>\n" "Language-Team: Chinese (Taiwan) (http://www.transifex.com/openstack/keystone/" "language/zh_TW/)\n" -"Language: zh_TW\n" +"Language: zh-TW\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" "Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Zanata 3.7.1\n" #, python-format msgid "Unable to open template file %s" diff --git a/keystone-moon/keystone/locale/zh_TW/LC_MESSAGES/keystone.po b/keystone-moon/keystone/locale/zh_TW/LC_MESSAGES/keystone.po new file mode 100644 index 00000000..7a51b29a --- /dev/null +++ b/keystone-moon/keystone/locale/zh_TW/LC_MESSAGES/keystone.po @@ -0,0 +1,1149 @@ +# Chinese (Traditional, Taiwan) translations for keystone. +# Copyright (C) 2015 OpenStack Foundation +# This file is distributed under the same license as the keystone project. +# +# Translators: +# Lucas Palm <lapalm@us.ibm.com>, 2015. #zanata +# OpenStack Infra <zanata@openstack.org>, 2015. #zanata +msgid "" +msgstr "" +"Project-Id-Version: keystone 9.0.0.dev14\n" +"Report-Msgid-Bugs-To: https://bugs.launchpad.net/keystone\n" +"POT-Creation-Date: 2015-09-24 06:09+0000\n" +"PO-Revision-Date: 2015-09-03 12:54+0000\n" +"Last-Translator: openstackjenkins <jenkins@openstack.org>\n" +"Language-Team: Chinese (Taiwan) (http://www.transifex.com/openstack/keystone/" +"language/zh_TW/)\n" +"Plural-Forms: nplurals=1; plural=0\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" + +#, python-format +msgid "%(detail)s" +msgstr "%(detail)s" + +#, python-format +msgid "" +"%(event)s is not a valid notification event, must be one of: %(actions)s" +msgstr "%(event)s 不是有效的通知事件,必須是 %(actions)s 的其中之一" + +#, python-format +msgid "%(host)s is not a trusted dashboard host" +msgstr "%(host)s 不是授信儀表板主機" + +#, python-format +msgid "%(message)s %(amendment)s" +msgstr "%(message)s %(amendment)s" + +#, python-format +msgid "" +"%(mod_name)s doesn't provide database migrations. The migration repository " +"path at %(path)s doesn't exist or isn't a directory." +msgstr "" +"%(mod_name)s 未提供資料庫移轉。%(path)s 處的移轉儲存庫路徑不存在或者不是目" +"錄。" + +#, python-format +msgid "%(property_name)s cannot be less than %(min_length)s characters." +msgstr "%(property_name)s 不能少於 %(min_length)s 個字元。" + +#, python-format +msgid "%(property_name)s is not a %(display_expected_type)s" +msgstr "%(property_name)s 不是 %(display_expected_type)s" + +#, python-format +msgid "%(property_name)s should not be greater than %(max_length)s characters." +msgstr "%(property_name)s 不應超過 %(max_length)s 個字元。" + +#, python-format +msgid "%s cannot be empty." +msgstr "%s 不能是空的。" + +#, python-format +msgid "%s extension does not exist." +msgstr "%s 延伸不存在。" + +#, python-format +msgid "%s field is required and cannot be empty" +msgstr "%s 欄位是必要欄位,因此不能是空的" + +#, python-format +msgid "%s field(s) cannot be empty" +msgstr "%s 欄位不能是空的" + +msgid "(Disable debug mode to suppress these details.)" +msgstr "(停用除錯模式,以暫停這些詳細資料。)" + +msgid "--all option cannot be mixed with other options" +msgstr "--all 選項不能與其他選項混合" + +msgid "A project-scoped token is required to produce a service catalog." +msgstr "需要專案範圍的記號來產生服務型錄。" + +msgid "Access token is expired" +msgstr "存取記號過期" + +msgid "Access token not found" +msgstr "找不到存取記號" + +msgid "Additional authentications steps required." +msgstr "需要其他鑑別步驟。" + +msgid "An unexpected error occurred when retrieving domain configs" +msgstr "擷取網域配置時發生非預期的錯誤" + +#, python-format +msgid "An unexpected error occurred when trying to store %s" +msgstr "嘗試儲存 %s 時發生非預期的錯誤" + +msgid "An unexpected error prevented the server from fulfilling your request." +msgstr "發生非預期的錯誤,造成伺服器無法履行要求。" + +#, python-format +msgid "" +"An unexpected error prevented the server from fulfilling your request: " +"%(exception)s" +msgstr "發生非預期的錯誤,造成伺服器無法履行要求:%(exception)s" + +msgid "An unhandled exception has occurred: Could not find metadata." +msgstr "發生無法處理的異常狀況:找不到 meta 資料。" + +msgid "At least one option must be provided" +msgstr "必須提供至少一個選項" + +msgid "At least one option must be provided, use either --all or --domain-name" +msgstr "必須提供至少一個選項,請使用 --all 或 --domain-name" + +msgid "At least one role should be specified." +msgstr "應該至少指定一個角色。" + +msgid "Attempted to authenticate with an unsupported method." +msgstr "已嘗試使用不支援的方法進行鑑別。" + +msgid "" +"Attempting to use OS-FEDERATION token with V2 Identity Service, use V3 " +"Authentication" +msgstr "" +"正在嘗試使用具有第 2 版身分服務的 OS-FEDERATION 記號,請使用第 3 版鑑別" + +msgid "Authentication plugin error." +msgstr "鑑別外掛程式錯誤。" + +msgid "Cannot authorize a request token with a token issued via delegation." +msgstr "無法使用透過委派發出之記號授權要求記號。" + +#, python-format +msgid "Cannot change %(option_name)s %(attr)s" +msgstr "無法變更 %(option_name)s %(attr)s" + +msgid "Cannot change Domain ID" +msgstr "無法變更網域 ID" + +msgid "Cannot change consumer secret" +msgstr "無法變更消費者密碼" + +msgid "Cannot change user ID" +msgstr "無法變更使用者 ID" + +msgid "Cannot change user name" +msgstr "無法變更使用者名稱" + +#, python-format +msgid "Cannot create project with parent: %(project_id)s" +msgstr "無法建立具有母項的專案:%(project_id)s" + +#, python-format +msgid "Cannot duplicate name %s" +msgstr "無法複製名稱 %s" + +msgid "Cannot list request tokens with a token issued via delegation." +msgstr "無法列出含有透過委派發出之記號的要求記號。" + +#, python-format +msgid "Cannot open certificate %(cert_file)s. Reason: %(reason)s" +msgstr "無法開啟憑證 %(cert_file)s。原因:%(reason)s" + +#, python-format +msgid "Cannot remove role that has not been granted, %s" +msgstr "無法移除尚未授權的角色,%s" + +msgid "" +"Cannot truncate a driver call without hints list as first parameter after " +"self " +msgstr "屬性 limit 不在 hints 清單時,無法截斷驅動程式呼叫" + +msgid "" +"Cannot use parents_as_list and parents_as_ids query params at the same time." +msgstr "無法同時使用 parents_as_list 與 parents_as_ids查詢參數。" + +msgid "" +"Cannot use subtree_as_list and subtree_as_ids query params at the same time." +msgstr "無法同時使用 subtree_as_list 與 subtree_as_ids 查詢參數。" + +msgid "" +"Combining effective and group filter will always result in an empty list." +msgstr "結合作用中的過濾器和群組過濾器將一律導致空清單。" + +msgid "" +"Combining effective, domain and inherited filters will always result in an " +"empty list." +msgstr "結合作用中的過濾器、網域過濾器及繼承的過濾器將一律導致空清單。" + +#, python-format +msgid "Conflict occurred attempting to store %(type)s - %(details)s" +msgstr "嘗試儲存 %(type)s 時發生衝突 - %(details)s" + +#, python-format +msgid "Conflicting region IDs specified: \"%(url_id)s\" != \"%(ref_id)s\"" +msgstr "指定了相衝突的區域 ID:\"%(url_id)s\" != \"%(ref_id)s\"" + +msgid "Consumer not found" +msgstr "找不到消費者" + +#, python-format +msgid "" +"Could not change immutable attribute(s) '%(attributes)s' in target %(target)s" +msgstr "無法變更目標 %(target)s 中固定不變的屬性 '%(attributes)s'" + +#, python-format +msgid "" +"Could not find %(group_or_option)s in domain configuration for domain " +"%(domain_id)s" +msgstr "在下列網域的網域配置中找不到 %(group_or_option)s:%(domain_id)s" + +#, python-format +msgid "Could not find Endpoint Group: %(endpoint_group_id)s" +msgstr "找不到端點群組:%(endpoint_group_id)s" + +msgid "Could not find Identity Provider identifier in environment" +msgstr "在環境中找不到身分提供者 ID" + +#, python-format +msgid "Could not find Identity Provider: %(idp_id)s" +msgstr "找不到身分提供者:%(idp_id)s" + +#, python-format +msgid "Could not find Service Provider: %(sp_id)s" +msgstr "找不到服務提供者:%(sp_id)s" + +#, python-format +msgid "Could not find credential: %(credential_id)s" +msgstr "找不到認證:%(credential_id)s" + +#, python-format +msgid "Could not find domain: %(domain_id)s" +msgstr "找不到網域:%(domain_id)s" + +#, python-format +msgid "Could not find endpoint: %(endpoint_id)s" +msgstr "找不到端點:%(endpoint_id)s" + +#, python-format +msgid "" +"Could not find federated protocol %(protocol_id)s for Identity Provider: " +"%(idp_id)s" +msgstr "找不到下列身分提供者的聯合通訊協定 %(protocol_id)s:%(idp_id)s" + +#, python-format +msgid "Could not find group: %(group_id)s" +msgstr "找不到群組:%(group_id)s" + +#, python-format +msgid "Could not find mapping: %(mapping_id)s" +msgstr "找不到對映:%(mapping_id)s" + +msgid "Could not find policy association" +msgstr "找不到原則關聯" + +#, python-format +msgid "Could not find policy: %(policy_id)s" +msgstr "找不到原則:%(policy_id)s" + +#, python-format +msgid "Could not find project: %(project_id)s" +msgstr "找不到專案:%(project_id)s" + +#, python-format +msgid "Could not find region: %(region_id)s" +msgstr "找不到區域:%(region_id)s" + +msgid "Could not find role" +msgstr "找不到角色" + +#, python-format +msgid "" +"Could not find role assignment with role: %(role_id)s, user or group: " +"%(actor_id)s, project or domain: %(target_id)s" +msgstr "" +"找不到具有角色 %(role_id)s、使用者或群組 %(actor_id)s、專案或網域 " +"%(target_id)s 的角色指派" + +#, python-format +msgid "Could not find role: %(role_id)s" +msgstr "找不到角色:%(role_id)s" + +#, python-format +msgid "Could not find service: %(service_id)s" +msgstr "找不到服務:%(service_id)s" + +#, python-format +msgid "Could not find token: %(token_id)s" +msgstr "找不到記號:%(token_id)s" + +#, python-format +msgid "Could not find trust: %(trust_id)s" +msgstr "找不到信任:%(trust_id)s" + +#, python-format +msgid "Could not find user: %(user_id)s" +msgstr "找不到使用者:%(user_id)s" + +#, python-format +msgid "Could not find version: %(version)s" +msgstr "找不到版本:%(version)s" + +#, python-format +msgid "Could not find: %(target)s" +msgstr "找不到:%(target)s" + +msgid "Could not validate the access token" +msgstr "無法驗證存取記號" + +msgid "Credential belongs to another user" +msgstr "認證屬於另一個使用者" + +#, python-format +msgid "Database at /domains/%s/config" +msgstr "位於 /domains/%s/config 中的資料庫" + +msgid "" +"Disabling an entity where the 'enable' attribute is ignored by configuration." +msgstr "正在停用配置已忽略其 'enable' 屬性的實體。" + +#, python-format +msgid "Domain (%s)" +msgstr "網域 (%s)" + +#, python-format +msgid "Domain cannot be named %s" +msgstr "網域不能命名為 %s" + +#, python-format +msgid "Domain cannot have ID %s" +msgstr "網域不能具有 ID %s" + +#, python-format +msgid "Domain is disabled: %s" +msgstr "已停用網域:%s" + +msgid "Domain metadata not supported by LDAP" +msgstr "LDAP 不支援網域 meta 資料" + +msgid "Domain scoped token is not supported" +msgstr "不支援網域範圍的記號" + +#, python-format +msgid "" +"Domain: %(domain)s already has a configuration defined - ignoring file: " +"%(file)s." +msgstr "網域 %(domain)s 已定義配置 - 正在忽略檔案 %(file)s。" + +msgid "Domains are read-only against LDAP" +msgstr "網域對於 LDAP 而言是唯讀的" + +msgid "Duplicate Entry" +msgstr "項目重複" + +#, python-format +msgid "Duplicate ID, %s." +msgstr "重複的 ID,%s。" + +#, python-format +msgid "Duplicate name, %s." +msgstr "重複的名稱,%s。" + +msgid "Enabled field must be a boolean" +msgstr "「已啟用」欄位必須是布林值" + +msgid "Enabled field should be a boolean" +msgstr "「已啟用」欄位應該是布林值" + +#, python-format +msgid "Endpoint %(endpoint_id)s not found in project %(project_id)s" +msgstr "在專案 %(project_id)s 中找不到端點 %(endpoint_id)s" + +msgid "Endpoint Group Project Association not found" +msgstr "找不到端點群組專案關聯" + +msgid "Ensure configuration option idp_entity_id is set." +msgstr "請確保已設定配置選項 idp_entity_id。" + +msgid "Ensure configuration option idp_sso_endpoint is set." +msgstr "請確保已設定配置選項 idp_sso_endpoint。" + +#, python-format +msgid "" +"Error parsing configuration file for domain: %(domain)s, file: %(file)s." +msgstr "剖析網域 %(domain)s 的配置檔時發生錯誤,檔案:%(file)s。" + +#, python-format +msgid "Error while reading metadata file, %(reason)s" +msgstr "讀取 meta 資料檔時發生錯誤,%(reason)s" + +#, python-format +msgid "Expected dict or list: %s" +msgstr "預期字典或清單:%s" + +msgid "" +"Expected signing certificates are not available on the server. Please check " +"Keystone configuration." +msgstr "伺服器上無法使用預期的簽署憑證。請檢查 Keystone 配置。" + +#, python-format +msgid "" +"Expecting to find %(attribute)s in %(target)s - the server could not comply " +"with the request since it is either malformed or otherwise incorrect. The " +"client is assumed to be in error." +msgstr "" +"預期在 %(target)s 中找到 %(attribute)s - 伺服器無法遵守要求,因為它的格式不正" +"確。系統會假定用戶端處於錯誤狀態。" + +#, python-format +msgid "Failed to start the %(name)s server" +msgstr "無法啟動 %(name)s 伺服器" + +msgid "Failed to validate token" +msgstr "無法驗證記號" + +msgid "Federation token is expired" +msgstr "聯合記號過期" + +#, python-format +msgid "" +"Field \"remaining_uses\" is set to %(value)s while it must not be set in " +"order to redelegate a trust" +msgstr "" +"欄位 \"remaining_uses\" 設定為 %(value)s,但為了重新委派信任,不能設定該欄位" + +msgid "Found invalid token: scoped to both project and domain." +msgstr "找到無效記號:已將範圍限定為專案及網域。" + +#, python-format +msgid "Group %(group)s is not supported for domain specific configurations" +msgstr "網域特定配置不支援群組 %(group)s" + +#, python-format +msgid "" +"Group %(group_id)s returned by mapping %(mapping_id)s was not found in the " +"backend." +msgstr "在後端找不到對映 %(mapping_id)s 所傳回的群組 %(group_id)s。" + +#, python-format +msgid "" +"Group membership across backend boundaries is not allowed, group in question " +"is %(group_id)s, user is %(user_id)s" +msgstr "" +"不容許後端界限之間的群組成員資格,有問題的群組為%(group_id)s,使用者為 " +"%(user_id)s" + +#, python-format +msgid "ID attribute %(id_attr)s not found in LDAP object %(dn)s" +msgstr "在 LDAP 物件 %(dn)s 中找不到 ID 屬性 %(id_attr)s" + +#, python-format +msgid "Identity Provider %(idp)s is disabled" +msgstr "已停用身分提供者 %(idp)s" + +msgid "" +"Incoming identity provider identifier not included among the accepted " +"identifiers." +msgstr "送入的身分提供者 ID 未包括在接受的 ID 中。" + +#, python-format +msgid "Invalid LDAP TLS certs option: %(option)s. Choose one of: %(options)s" +msgstr "無效的 LDAP TLS 憑證選項:%(option)s。請選擇 %(options)s 的其中之一" + +#, python-format +msgid "Invalid LDAP TLS_AVAIL option: %s. TLS not available" +msgstr "無效的 LDAP TLS_AVAIL 選項:%s。TLS 無法使用" + +#, python-format +msgid "Invalid LDAP deref option: %(option)s. Choose one of: %(options)s" +msgstr "無效的 LDAP deref 選項:%(option)s。請選擇 %(options)s 的其中之一" + +#, python-format +msgid "Invalid LDAP scope: %(scope)s. Choose one of: %(options)s" +msgstr "無效的 LDAP 範圍:%(scope)s。請選擇 %(options)s 的其中之一" + +msgid "Invalid TLS / LDAPS combination" +msgstr "無效的 TLS/LDAPS 組合" + +#, python-format +msgid "Invalid audit info data type: %(data)s (%(type)s)" +msgstr "審核資訊資料類型無效:%(data)s (%(type)s)" + +msgid "Invalid blob in credential" +msgstr "認證中的二進位大型物件無效" + +#, python-format +msgid "" +"Invalid domain name: %(domain)s found in config file name: %(file)s - " +"ignoring this file." +msgstr "" +"在配置檔名稱 %(file)s 中找到的網域名稱 %(domain)s 無效 - 正在忽略此檔案。" + +#, python-format +msgid "Invalid domain specific configuration: %(reason)s" +msgstr "網域特定配置無效:%(reason)s" + +#, python-format +msgid "Invalid input for field '%(path)s'. The value is '%(value)s'." +msgstr "欄位 '%(path)s' 的輸入無效。值為 '%(value)s'。" + +msgid "Invalid limit value" +msgstr "無效的限制值" + +#, python-format +msgid "" +"Invalid mix of entities for policy association - only Endpoint, Service or " +"Region+Service allowed. Request was - Endpoint: %(endpoint_id)s, Service: " +"%(service_id)s, Region: %(region_id)s" +msgstr "" +"原則關聯的混合實體無效 - 僅容許「端點」、「服務」或「區域+服務」。要求為 -" +"「端點」:%(endpoint_id)s,「服務」:%(service_id)s,「區域」:%(region_id)s" + +#, python-format +msgid "" +"Invalid rule: %(identity_value)s. Both 'groups' and 'domain' keywords must " +"be specified." +msgstr "規則 %(identity_value)s 無效。必須指定 'groups' 及 'domain' 關鍵字。" + +msgid "Invalid signature" +msgstr "無效的簽章" + +#, python-format +msgid "" +"Invalid ssl_cert_reqs value of %s, must be one of \"NONE\", \"OPTIONAL\", " +"\"REQUIRED\"" +msgstr "" +"%s 的 ssl_cert_reqs 值無效,必須是 \"NONE\"、\"OPTIONAL\" 及\"REQUIRED\" 的其" +"中之一" + +msgid "Invalid user / password" +msgstr "無效的使用者/密碼" + +msgid "Invalid username or password" +msgstr "無效的使用者名稱或密碼" + +#, python-format +msgid "KVS region %s is already configured. Cannot reconfigure." +msgstr "KVS 區域 %s 已配置。無法重新配置。" + +#, python-format +msgid "Key Value Store not configured: %s" +msgstr "未配置金鑰值儲存庫:%s" + +#, python-format +msgid "LDAP %s create" +msgstr "LDAP %s 建立" + +#, python-format +msgid "LDAP %s delete" +msgstr "LDAP %s 刪除" + +#, python-format +msgid "LDAP %s update" +msgstr "LDAP %s 更新" + +#, python-format +msgid "Lock Timeout occurred for key, %(target)s" +msgstr "金鑰 %(target)s 發生鎖定逾時" + +#, python-format +msgid "Lock key must match target key: %(lock)s != %(target)s" +msgstr "鎖定金鑰必須與目標金鑰相符:%(lock)s 不等於 %(target)s" + +#, python-format +msgid "Malformed endpoint URL (%(endpoint)s), see ERROR log for details." +msgstr "端點 URL (%(endpoint)s) 的格式不正確,請參閱錯誤日誌以取得詳細資料。" + +msgid "Marker could not be found" +msgstr "找不到標記" + +#, python-format +msgid "Maximum lock attempts on %s occurred." +msgstr "已達到 %s 的鎖定嘗試次數上限。" + +#, python-format +msgid "Member %(member)s is already a member of group %(group)s" +msgstr "成員 %(member)s 已是群組 %(group)s 的成員" + +#, python-format +msgid "Method not callable: %s" +msgstr "方法不可呼叫:%s" + +msgid "Missing entity ID from environment" +msgstr "環境中遺漏實體 ID" + +msgid "" +"Modifying \"redelegation_count\" upon redelegation is forbidden. Omitting " +"this parameter is advised." +msgstr "禁止在重新委派時修改 \"redelegation_count\"。建議省略此參數。" + +msgid "Multiple domains are not supported" +msgstr "不支援多個網域" + +msgid "Must be called within an active lock context." +msgstr "必須在作用中鎖定環境定義內呼叫。" + +msgid "Must specify either domain or project" +msgstr "必須指定 Domain 或 Project" + +msgid "Name field is required and cannot be empty" +msgstr "名稱欄位是必要欄位,因此不能是空的" + +msgid "" +"No Authorization headers found, cannot proceed with OAuth related calls, if " +"running under HTTPd or Apache, ensure WSGIPassAuthorization is set to On." +msgstr "" +"找不到授權標頭,無法繼續進行 OAuth 相關呼叫,如果在 HTTPd 或 Apache 下執行," +"請確保 WSGIPassAuthorization 設定為 On。" + +msgid "No authenticated user" +msgstr "沒有已鑑別使用者" + +msgid "" +"No encryption keys found; run keystone-manage fernet_setup to bootstrap one." +msgstr "找不到加密金鑰;請執行 keystone-manage fernet_setup 以引導一個。" + +msgid "No options specified" +msgstr "未指定選項" + +#, python-format +msgid "No policy is associated with endpoint %(endpoint_id)s." +msgstr "沒有原則與端點 %(endpoint_id)s 相關聯。" + +#, python-format +msgid "No remaining uses for trust: %(trust_id)s" +msgstr "沒有信任 %(trust_id)s 的剩餘使用情形" + +msgid "Non-default domain is not supported" +msgstr "不支援非預設網域" + +msgid "One of the trust agents is disabled or deleted" +msgstr "已停用或刪除其中一個信任代理程式" + +#, python-format +msgid "" +"Option %(option)s found with no group specified while checking domain " +"configuration request" +msgstr "檢查網域配置要求時,發現選項 %(option)s 未指定任何群組" + +#, python-format +msgid "" +"Option %(option)s in group %(group)s is not supported for domain specific " +"configurations" +msgstr "網域特定配置不支援群組 %(group)s 中的選項 %(option)s" + +#, python-format +msgid "Project (%s)" +msgstr "專案 (%s)" + +#, python-format +msgid "Project is disabled: %s" +msgstr "已停用專案:%s" + +msgid "Redelegation allowed for delegated by trust only" +msgstr "僅委派為信任時,才容許重新委派" + +#, python-format +msgid "" +"Remaining redelegation depth of %(redelegation_depth)d out of allowed range " +"of [0..%(max_count)d]" +msgstr "" +"剩餘的重新委派深度 %(redelegation_depth)d 超出容許的範圍 [0..%(max_count)d]" + +msgid "Request Token does not have an authorizing user id" +msgstr "要求記號不具有授權使用者 ID" + +#, python-format +msgid "" +"Request attribute %(attribute)s must be less than or equal to %(size)i. The " +"server could not comply with the request because the attribute size is " +"invalid (too large). The client is assumed to be in error." +msgstr "" +"要求屬性 %(attribute)s 必須少於或等於 %(size)i。伺服器無法遵守要求,因為屬性" +"大小無效(太大)。系統會假定用戶端處於錯誤狀態。" + +msgid "Request must have an origin query parameter" +msgstr "要求必須具有原始查詢參數" + +msgid "Request token is expired" +msgstr "要求記號過期" + +msgid "Request token not found" +msgstr "找不到要求記號" + +msgid "Requested expiration time is more than redelegated trust can provide" +msgstr "所要求的有效期限超過重新委派之信任可提供的有效期限" + +#, python-format +msgid "" +"Requested redelegation depth of %(requested_count)d is greater than allowed " +"%(max_count)d" +msgstr "所要求的重新委派深度 %(requested_count)d 大於容許的 %(max_count)d" + +#, python-format +msgid "Role %s not found" +msgstr "找不到角色 %s" + +msgid "" +"Running keystone via eventlet is deprecated as of Kilo in favor of running " +"in a WSGI server (e.g. mod_wsgi). Support for keystone under eventlet will " +"be removed in the \"M\"-Release." +msgstr "" +"透過 eventlet 執行 Keystone 這一做法已遭淘汰,因為 Kilo 偏好在 WSGI 伺服器" +"(例如,mod_wsgi)中執行 Keystone。將在\"M\" 版本中移除對在 eventlet 下執行 " +"Keystone 的支援。" + +msgid "Scoping to both domain and project is not allowed" +msgstr "不容許將範圍同時設定為網域及專案" + +msgid "Scoping to both domain and trust is not allowed" +msgstr "不容許將範圍同時設定為網域及信任" + +msgid "Scoping to both project and trust is not allowed" +msgstr "不容許將範圍同時設定為專案及信任" + +#, python-format +msgid "Service Provider %(sp)s is disabled" +msgstr "已停用服務提供者 %(sp)s" + +msgid "Some of requested roles are not in redelegated trust" +msgstr "所要求的部分角色不在重新委派的信任中" + +msgid "Specify a domain or project, not both" +msgstr "指定網域或專案,但不要同時指定兩者" + +msgid "Specify a user or group, not both" +msgstr "指定使用者或群組,但不要同時指定兩者" + +msgid "Specify one of domain or project" +msgstr "指定網域或專案" + +msgid "Specify one of user or group" +msgstr "指定使用者或群組" + +#, python-format +msgid "" +"String length exceeded.The length of string '%(string)s' exceeded the limit " +"of column %(type)s(CHAR(%(length)d))." +msgstr "" +"已超出字串長度。字串 '%(string)s' 的長度已超出直欄 %(type)s 的限制 " +"(CHAR(%(length)d))。" + +msgid "The --all option cannot be used with the --domain-name option" +msgstr "--all 選項不能與 --domain-name 選項搭配使用" + +#, python-format +msgid "The Keystone configuration file %(config_file)s could not be found." +msgstr "找不到 Keystone 配置檔 %(config_file)s。" + +#, python-format +msgid "" +"The Keystone domain-specific configuration has specified more than one SQL " +"driver (only one is permitted): %(source)s." +msgstr "" +"Keystone 網域特定配置指定了多個SQL 驅動程式(僅允許一個):%(source)s。" + +msgid "The action you have requested has not been implemented." +msgstr "尚未實作所要求的動作。" + +msgid "The authenticated user should match the trustor." +msgstr "已鑑別使用者應該與委託人相符。" + +msgid "" +"The certificates you requested are not available. It is likely that this " +"server does not use PKI tokens otherwise this is the result of " +"misconfiguration." +msgstr "" +"所要求的憑證無法使用。可能是此伺服器沒有使用 PKI 記號,否則,這是由於配置錯誤" +"所造成。" + +#, python-format +msgid "" +"The password length must be less than or equal to %(size)i. The server could " +"not comply with the request because the password is invalid." +msgstr "密碼長度必須小於或等於 %(size)i。伺服器無法遵守要求,因為密碼無效。" + +msgid "The request you have made requires authentication." +msgstr "您發出的要求需要鑑別。" + +msgid "The resource could not be found." +msgstr "找不到資源。" + +msgid "" +"The revoke call must not have both domain_id and project_id. This is a bug " +"in the Keystone server. The current request is aborted." +msgstr "" +"撤銷呼叫不得同時具有 domain_id 和 project_id。這是Keystone 伺服器中的錯誤。已" +"中斷現行要求。" + +msgid "The service you have requested is no longer available on this server." +msgstr "此伺服器上無法再使用所要求的服務。" + +#, python-format +msgid "" +"The specified parent region %(parent_region_id)s would create a circular " +"region hierarchy." +msgstr "指定的母項區域 %(parent_region_id)s 會建立循環區域階層。" + +#, python-format +msgid "" +"The value of group %(group)s specified in the config should be a dictionary " +"of options" +msgstr "在配置中指定的群組 %(group)s 的值應該為選項字典" + +msgid "There should not be any non-oauth parameters" +msgstr "不應該具有任何 non-oauth 參數" + +#, python-format +msgid "This is not a recognized Fernet payload version: %s" +msgstr "這不是已辨識的 Fernet 內容版本:%s" + +msgid "" +"This is not a v2.0 Fernet token. Use v3 for trust, domain, or federated " +"tokens." +msgstr "這不是 2.0 版 Fernet 記號。請對信任、網域或聯合記號使用第 3 版。" + +msgid "" +"Timestamp not in expected format. The server could not comply with the " +"request since it is either malformed or otherwise incorrect. The client is " +"assumed to be in error." +msgstr "" +"時間戳記的格式不符合預期。伺服器無法遵守要求,因為它的格式不正確。系統會假定" +"用戶端處於錯誤狀態。" + +#, python-format +msgid "" +"To get a more detailed information on this error, re-run this command for " +"the specific domain, i.e.: keystone-manage domain_config_upload --domain-" +"name %s" +msgstr "" +"若要取得此錯誤的更詳細資訊,請針對特定的網域重新執行此指令,例如:keystone-" +"manage domain_config_upload --domain-name %s" + +msgid "Token belongs to another user" +msgstr "記號屬於另一個使用者" + +msgid "Token does not belong to specified tenant." +msgstr "記號不屬於所指定的 Tenant。" + +msgid "Trustee has no delegated roles." +msgstr "受託人沒有委派的角色。" + +msgid "Trustor is disabled." +msgstr "委託人已停用。" + +#, python-format +msgid "" +"Trying to update group %(group)s, so that, and only that, group must be " +"specified in the config" +msgstr "" +"正在嘗試更新群組 %(group)s,因此必須在配置中指定該群組且必須僅指定該群組" + +#, python-format +msgid "" +"Trying to update option %(option)s in group %(group)s, but config provided " +"contains option %(option_other)s instead" +msgstr "" +"正在嘗試更新群組 %(group)s 中的選項 %(option)s,但提供的配置卻包含選項 " +"%(option_other)s" + +#, python-format +msgid "" +"Trying to update option %(option)s in group %(group)s, so that, and only " +"that, option must be specified in the config" +msgstr "" +"正在嘗試更新群組 %(group)s 中的選項 %(option)s,因此必須在配置中指定該選項且" +"必須僅指定該選項" + +msgid "" +"Unable to access the keystone database, please check it is configured " +"correctly." +msgstr "無法存取 Keystone 資料庫,請檢查它是否已正確配置。" + +#, python-format +msgid "Unable to consume trust %(trust_id)s, unable to acquire lock." +msgstr "無法耗用信任 %(trust_id)s,無法獲得鎖定。" + +#, python-format +msgid "" +"Unable to delete region %(region_id)s because it or its child regions have " +"associated endpoints." +msgstr "無法刪除區域 %(region_id)s,因為此區域或其子區域具有相關聯的端點。" + +#, python-format +msgid "Unable to find valid groups while using mapping %(mapping_id)s" +msgstr "使用對映 %(mapping_id)s 時找不到有效的群組" + +#, python-format +msgid "" +"Unable to get a connection from pool id %(id)s after %(seconds)s seconds." +msgstr "在 %(seconds)s 秒之後,無法從儲存區 ID %(id)s 取得連線。" + +#, python-format +msgid "Unable to locate domain config directory: %s" +msgstr "找不到網域配置目錄:%s" + +#, python-format +msgid "Unable to lookup user %s" +msgstr "無法查閱使用者 %s" + +#, python-format +msgid "" +"Unable to reconcile identity attribute %(attribute)s as it has conflicting " +"values %(new)s and %(old)s" +msgstr "" +"無法核對身分屬性 %(attribute)s,因為該屬性具有衝突的值 %(new)s 和 %(old)s" + +#, python-format +msgid "" +"Unable to sign SAML assertion. It is likely that this server does not have " +"xmlsec1 installed, or this is the result of misconfiguration. Reason " +"%(reason)s" +msgstr "" +"無法簽署 SAML 主張。此伺服器可能未安裝xmlsec1,或者這是配置錯誤的結果。原" +"因: %(reason)s" + +msgid "Unable to sign token." +msgstr "無法簽署記號。" + +#, python-format +msgid "Unexpected assignment type encountered, %s" +msgstr "發現非預期的指派類型:%s" + +#, python-format +msgid "" +"Unexpected combination of grant attributes - User: %(user_id)s, Group: " +"%(group_id)s, Project: %(project_id)s, Domain: %(domain_id)s" +msgstr "" +"非預期的授權屬性組合 - 使用者:%(user_id)s,群組:%(group_id)s,專案:" +"%(project_id)s,網域:%(domain_id)s" + +#, python-format +msgid "Unexpected status requested for JSON Home response, %s" +msgstr "針對「JSON 起始目錄」回應要求了非預期狀態 %s" + +msgid "Unknown Target" +msgstr "不明的目標" + +#, python-format +msgid "Unknown domain '%(name)s' specified by --domain-name" +msgstr "由 --domain-name 指定的網域 '%(name)s' 不明" + +#, python-format +msgid "Unknown token version %s" +msgstr "不明的記號版本 %s" + +#, python-format +msgid "Unregistered dependency: %(name)s for %(targets)s" +msgstr "已取消登錄 %(targets)s 的相依關係:%(name)s" + +msgid "Update of `parent_id` is not allowed." +msgstr "不容許更新 'parent_id'。" + +msgid "Use a project scoped token when attempting to create a SAML assertion" +msgstr "嘗試建立 SAML 主張時,使用專案範圍的記號" + +#, python-format +msgid "User %(u_id)s is unauthorized for tenant %(t_id)s" +msgstr "使用者 %(u_id)s 未獲承租人 %(t_id)s 的授權" + +#, python-format +msgid "User %(user_id)s already has role %(role_id)s in tenant %(tenant_id)s" +msgstr "使用者 %(user_id)s 在承租人 %(tenant_id)s 中已經具有角色 %(role_id)s" + +#, python-format +msgid "User %(user_id)s has no access to domain %(domain_id)s" +msgstr "使用者 %(user_id)s 無法存取網域 %(domain_id)s" + +#, python-format +msgid "User %(user_id)s has no access to project %(project_id)s" +msgstr "使用者 %(user_id)s 無法存取專案 %(project_id)s" + +#, python-format +msgid "User %(user_id)s is already a member of group %(group_id)s" +msgstr "使用者 %(user_id)s 已是群組 %(group_id)s 的成員" + +#, python-format +msgid "User '%(user_id)s' not found in group '%(group_id)s'" +msgstr "在群組 '%(group_id)s' 中找不到使用者 '%(user_id)s'" + +msgid "User IDs do not match" +msgstr "使用者 ID 不符" + +#, python-format +msgid "User is disabled: %s" +msgstr "已停用使用者:%s" + +msgid "User is not a member of the requested project" +msgstr "使用者並不隸屬於所要求的專案" + +msgid "User is not a trustee." +msgstr "使用者不是受託人。" + +msgid "User not found" +msgstr "找不到使用者" + +#, python-format +msgid "User type %s not supported" +msgstr "使用者類型 %s 不受支援" + +msgid "You are not authorized to perform the requested action." +msgstr "您未獲授權來執行所要求的動作。" + +#, python-format +msgid "You are not authorized to perform the requested action: %(action)s" +msgstr "您未獲授權來執行所要求的動作:%(action)s" + +msgid "`key_mangler` functions must be callable." +msgstr "`key_mangler` 函數必須可呼叫。" + +msgid "`key_mangler` option must be a function reference" +msgstr "`key_mangler` 選項必須是函數參照" + +msgid "any options" +msgstr "任何選項" + +msgid "auth_type is not Negotiate" +msgstr "auth_type 不是 Negotiate" + +msgid "authorizing user does not have role required" +msgstr "授權使用者不具有必要的角色" + +msgid "cache_collection name is required" +msgstr "需要 cache_collection 名稱" + +#, python-format +msgid "cannot create a project in a branch containing a disabled project: %s" +msgstr "無法在包含已停用專案的分支中建立專案:%s" + +msgid "cannot create a project within a different domain than its parents." +msgstr "無法在不同於其母項的網域內建立專案。" + +msgid "cannot delete a domain that is enabled, please disable it first." +msgstr "無法刪除已啟用的網域,請先停用該網域。" + +#, python-format +msgid "cannot delete the project %s since it is not a leaf in the hierarchy." +msgstr "無法刪除專案 %s,因為它不是階層中的葉節點。" + +#, python-format +msgid "cannot disable project %s since its subtree contains enabled projects" +msgstr "無法停用專案 %s,因為其子樹狀結構包含已啟用的專案" + +#, python-format +msgid "cannot enable project %s since it has disabled parents" +msgstr "無法啟用專案 %s,因為它具有已停用的母項" + +msgid "database db_name is required" +msgstr "需要資料庫 db_name" + +msgid "db_hosts value is required" +msgstr "需要 db_hosts 值" + +msgid "delete the default domain" +msgstr "刪除預設網域" + +#, python-format +msgid "group %(group)s" +msgstr "群組 %(group)s" + +msgid "" +"idp_contact_type must be one of: [technical, other, support, administrative " +"or billing." +msgstr "idp_contact_type 必須是下列其中一個:技術、其他、支援、管理或計費。" + +msgid "integer value expected for mongo_ttl_seconds" +msgstr "mongo_ttl_seconds 預期整數值" + +msgid "integer value expected for w (write concern attribute)" +msgstr "w(WriteConcern 屬性)預期整數值" + +#, python-format +msgid "invalid date format %s" +msgstr "無效的日期格式 %s" + +#, python-format +msgid "max hierarchy depth reached for %s branch." +msgstr "已達到 %s 分支的最大階層深度。" + +msgid "no ssl support available" +msgstr "無法使用 SSL 支援" + +#, python-format +msgid "option %(option)s in group %(group)s" +msgstr "群組 %(group)s 中的選項 %(option)s" + +msgid "pad must be single character" +msgstr "填補必須是單一字元" + +msgid "padded base64url text must be multiple of 4 characters" +msgstr "填補 base64url 文字必須是 4 個字元的倍數" + +msgid "provided consumer key does not match stored consumer key" +msgstr "提供的消費者金鑰,與儲存的消費者金鑰不符" + +msgid "provided request key does not match stored request key" +msgstr "提供的要求金鑰,與儲存的要求金鑰不符" + +msgid "provided verifier does not match stored verifier" +msgstr "提供的驗證器,與儲存的驗證器不符" + +msgid "region not type dogpile.cache.CacheRegion" +msgstr "區域不是 dogpile.cache.CacheRegion 類型" + +msgid "remaining_uses must be a positive integer or null." +msgstr "remaining_uses 必須是正整數或空值。" + +msgid "remaining_uses must not be set if redelegation is allowed" +msgstr "如果容許重新委派,則不得設定 remaining_uses" + +msgid "replicaset_name required when use_replica is True" +msgstr "use_replica 為 True 時需要 replicaset_name" + +#, python-format +msgid "" +"request to update group %(group)s, but config provided contains group " +"%(group_other)s instead" +msgstr "要求更新群組 %(group)s,但提供的配置卻包含群組 %(group_other)s" + +msgid "rescope a scoped token" +msgstr "重新劃定已限定範圍之記號的範圍" + +#, python-format +msgid "text is multiple of 4, but pad \"%s\" occurs before 2nd to last char" +msgstr "文字是 4 的倍數,但填補 \"%s\" 出現於倒數第二個字元之前" + +#, python-format +msgid "text is multiple of 4, but pad \"%s\" occurs before non-pad last char" +msgstr "文字是 4 的倍數,但填補 \"%s\" 出現於最後一個字元(不可填補)之前" + +#, python-format +msgid "text is not a multiple of 4, but contains pad \"%s\"" +msgstr "文字不是 4 的倍數,但包含填補 \"%s\"" + +#, python-format +msgid "tls_cacertdir %s not found or is not a directory" +msgstr "tls_cacertdir %s 找不到,或者不是目錄" + +#, python-format +msgid "tls_cacertfile %s not found or is not a file" +msgstr "tls_cacertfile %s 找不到,或者不是檔案" + +#, python-format +msgid "token reference must be a KeystoneToken type, got: %s" +msgstr "記號參照必須是 KeystoneToken 類型,但卻取得:%s" diff --git a/keystone-moon/keystone/middleware/core.py b/keystone-moon/keystone/middleware/core.py index 62ff291a..75be5b27 100644 --- a/keystone-moon/keystone/middleware/core.py +++ b/keystone-moon/keystone/middleware/core.py @@ -13,16 +13,21 @@ # under the License. from oslo_config import cfg +from oslo_context import context as oslo_context from oslo_log import log from oslo_log import versionutils from oslo_middleware import sizelimit from oslo_serialization import jsonutils from keystone.common import authorization +from keystone.common import tokenless_auth from keystone.common import wsgi +from keystone.contrib.federation import constants as federation_constants +from keystone.contrib.federation import utils from keystone import exception -from keystone.i18n import _LW +from keystone.i18n import _, _LI, _LW from keystone.models import token_model +from keystone.token.providers import common CONF = cfg.CONF @@ -194,17 +199,117 @@ class AuthContextMiddleware(wsgi.Middleware): LOG.warning(_LW('RBAC: Invalid token')) raise exception.Unauthorized() + def _build_tokenless_auth_context(self, env): + """Build the authentication context. + + The context is built from the attributes provided in the env, + such as certificate and scope attributes. + """ + tokenless_helper = tokenless_auth.TokenlessAuthHelper(env) + + (domain_id, project_id, trust_ref, unscoped) = ( + tokenless_helper.get_scope()) + user_ref = tokenless_helper.get_mapped_user( + project_id, + domain_id) + + # NOTE(gyee): if it is an ephemeral user, the + # given X.509 SSL client cert does not need to map to + # an existing user. + if user_ref['type'] == utils.UserType.EPHEMERAL: + auth_context = {} + auth_context['group_ids'] = user_ref['group_ids'] + auth_context[federation_constants.IDENTITY_PROVIDER] = ( + user_ref[federation_constants.IDENTITY_PROVIDER]) + auth_context[federation_constants.PROTOCOL] = ( + user_ref[federation_constants.PROTOCOL]) + if domain_id and project_id: + msg = _('Scoping to both domain and project is not allowed') + raise ValueError(msg) + if domain_id: + auth_context['domain_id'] = domain_id + if project_id: + auth_context['project_id'] = project_id + auth_context['roles'] = user_ref['roles'] + else: + # it's the local user, so token data is needed. + token_helper = common.V3TokenDataHelper() + token_data = token_helper.get_token_data( + user_id=user_ref['id'], + method_names=[CONF.tokenless_auth.protocol], + domain_id=domain_id, + project_id=project_id) + + auth_context = {'user_id': user_ref['id']} + auth_context['is_delegated_auth'] = False + if domain_id: + auth_context['domain_id'] = domain_id + if project_id: + auth_context['project_id'] = project_id + auth_context['roles'] = [role['name'] for role + in token_data['token']['roles']] + return auth_context + + def _validate_trusted_issuer(self, env): + """To further filter the certificates that are trusted. + + If the config option 'trusted_issuer' is absent or does + not contain the trusted issuer DN, no certificates + will be allowed in tokenless authorization. + + :param env: The env contains the client issuer's attributes + :type env: dict + :returns: True if client_issuer is trusted; otherwise False + """ + + if not CONF.tokenless_auth.trusted_issuer: + return False + + client_issuer = env.get(CONF.tokenless_auth.issuer_attribute) + if not client_issuer: + msg = _LI('Cannot find client issuer in env by the ' + 'issuer attribute - %s.') + LOG.info(msg, CONF.tokenless_auth.issuer_attribute) + return False + + if client_issuer in CONF.tokenless_auth.trusted_issuer: + return True + + msg = _LI('The client issuer %(client_issuer)s does not match with ' + 'the trusted issuer %(trusted_issuer)s') + LOG.info( + msg, {'client_issuer': client_issuer, + 'trusted_issuer': CONF.tokenless_auth.trusted_issuer}) + + return False + def process_request(self, request): - if AUTH_TOKEN_HEADER not in request.headers: - LOG.debug(('Auth token not in the request header. ' - 'Will not build auth context.')) - return + + # The request context stores itself in thread-local memory for logging. + oslo_context.RequestContext( + request_id=request.environ.get('openstack.request_id')) if authorization.AUTH_CONTEXT_ENV in request.environ: - msg = _LW('Auth context already exists in the request environment') + msg = _LW('Auth context already exists in the request ' + 'environment; it will be used for authorization ' + 'instead of creating a new one.') LOG.warning(msg) return - auth_context = self._build_auth_context(request) + # NOTE(gyee): token takes precedence over SSL client certificates. + # This will preserve backward compatibility with the existing + # behavior. Tokenless authorization with X.509 SSL client + # certificate is effectively disabled if no trusted issuers are + # provided. + if AUTH_TOKEN_HEADER in request.headers: + auth_context = self._build_auth_context(request) + elif self._validate_trusted_issuer(request.environ): + auth_context = self._build_tokenless_auth_context( + request.environ) + else: + LOG.debug('There is either no auth token in the request or ' + 'the certificate issuer is not trusted. No auth ' + 'context will be set.') + return LOG.debug('RBAC: auth_context: %s', auth_context) request.environ[authorization.AUTH_CONTEXT_ENV] = auth_context diff --git a/keystone-moon/keystone/notifications.py b/keystone-moon/keystone/notifications.py index 801dd737..bea09d3c 100644 --- a/keystone-moon/keystone/notifications.py +++ b/keystone-moon/keystone/notifications.py @@ -242,19 +242,20 @@ def internal(*args, **kwargs): def _get_callback_info(callback): """Return list containing callback's module and name. - If the callback is an instance method also return the class name. + If the callback is a bound instance method also return the class name. :param callback: Function to call :type callback: function :returns: List containing parent module, (optional class,) function name :rtype: list """ - if getattr(callback, 'im_class', None): - return [getattr(callback, '__module__', None), - callback.im_class.__name__, - callback.__name__] + module_name = getattr(callback, '__module__', None) + func_name = callback.__name__ + if inspect.ismethod(callback): + class_name = callback.__self__.__class__.__name__ + return [module_name, class_name, func_name] else: - return [getattr(callback, '__module__', None), callback.__name__] + return [module_name, func_name] def register_event_callback(event, resource_type, callbacks): diff --git a/keystone-moon/keystone/policy/backends/rules.py b/keystone-moon/keystone/policy/backends/rules.py index 011dd542..a4150575 100644 --- a/keystone-moon/keystone/policy/backends/rules.py +++ b/keystone-moon/keystone/policy/backends/rules.py @@ -69,7 +69,7 @@ def enforce(credentials, action, target, do_raise=True): return _ENFORCER.enforce(action, target, credentials, **extra) -class Policy(policy.Driver): +class Policy(policy.PolicyDriverV8): def enforce(self, credentials, action, target): LOG.debug('enforce %(action)s: %(credentials)s', { 'action': action, diff --git a/keystone-moon/keystone/policy/core.py b/keystone-moon/keystone/policy/core.py index 7943b59e..dfd6ff2d 100644 --- a/keystone-moon/keystone/policy/core.py +++ b/keystone-moon/keystone/policy/core.py @@ -82,7 +82,7 @@ class Manager(manager.Manager): @six.add_metaclass(abc.ABCMeta) -class Driver(object): +class PolicyDriverV8(object): def _get_list_limit(self): return CONF.policy.list_limit or CONF.list_limit @@ -136,3 +136,6 @@ class Driver(object): """ raise exception.NotImplemented() # pragma: no cover + + +Driver = manager.create_legacy_driver(PolicyDriverV8) diff --git a/keystone-moon/keystone/resource/backends/ldap.py b/keystone-moon/keystone/resource/backends/ldap.py index 43684035..566adc5d 100644 --- a/keystone-moon/keystone/resource/backends/ldap.py +++ b/keystone-moon/keystone/resource/backends/ldap.py @@ -16,6 +16,7 @@ import uuid from oslo_config import cfg from oslo_log import log +from oslo_log import versionutils from keystone.common import clean from keystone.common import driver_hints @@ -31,7 +32,11 @@ CONF = cfg.CONF LOG = log.getLogger(__name__) -class Resource(resource.Driver): +class Resource(resource.ResourceDriverV8): + @versionutils.deprecated( + versionutils.deprecated.LIBERTY, + remove_in=+1, + what='ldap resource') def __init__(self): super(Resource, self).__init__() self.LDAP_URL = CONF.ldap.url diff --git a/keystone-moon/keystone/resource/backends/sql.py b/keystone-moon/keystone/resource/backends/sql.py index 3a0d8cea..59bab372 100644 --- a/keystone-moon/keystone/resource/backends/sql.py +++ b/keystone-moon/keystone/resource/backends/sql.py @@ -24,7 +24,7 @@ CONF = cfg.CONF LOG = log.getLogger(__name__) -class Resource(keystone_resource.Driver): +class Resource(keystone_resource.ResourceDriverV8): def default_assignment_driver(self): return 'sql' diff --git a/keystone-moon/keystone/resource/config_backends/sql.py b/keystone-moon/keystone/resource/config_backends/sql.py index e54bf22b..7c296074 100644 --- a/keystone-moon/keystone/resource/config_backends/sql.py +++ b/keystone-moon/keystone/resource/config_backends/sql.py @@ -42,7 +42,13 @@ class SensitiveConfig(sql.ModelBase, sql.ModelDictMixin): return d -class DomainConfig(resource.DomainConfigDriver): +class ConfigRegister(sql.ModelBase, sql.ModelDictMixin): + __tablename__ = 'config_register' + type = sql.Column(sql.String(64), primary_key=True) + domain_id = sql.Column(sql.String(64), nullable=False) + + +class DomainConfig(resource.DomainConfigDriverV8): def choose_table(self, sensitive): if sensitive: @@ -117,3 +123,30 @@ class DomainConfig(resource.DomainConfigDriver): if option: query = query.filter_by(option=option) query.delete(False) + + def obtain_registration(self, domain_id, type): + try: + with sql.transaction() as session: + ref = ConfigRegister(type=type, domain_id=domain_id) + session.add(ref) + return True + except sql.DBDuplicateEntry: + pass + return False + + def read_registration(self, type): + with sql.transaction() as session: + ref = session.query(ConfigRegister).get(type) + if not ref: + raise exception.ConfigRegistrationNotFound() + return ref.domain_id + + def release_registration(self, domain_id, type=None): + """Silently delete anything registered for the domain specified.""" + + with sql.transaction() as session: + query = session.query(ConfigRegister) + if type: + query = query.filter_by(type=type) + query = query.filter_by(domain_id=domain_id) + query.delete(False) diff --git a/keystone-moon/keystone/resource/core.py b/keystone-moon/keystone/resource/core.py index ca69b729..6015107d 100644 --- a/keystone-moon/keystone/resource/core.py +++ b/keystone-moon/keystone/resource/core.py @@ -118,7 +118,7 @@ class Manager(manager.Manager): def assert_domain_enabled(self, domain_id, domain=None): """Assert the Domain is enabled. - :raise AssertionError if domain is disabled. + :raise AssertionError: if domain is disabled. """ if domain is None: domain = self.get_domain(domain_id) @@ -133,7 +133,7 @@ class Manager(manager.Manager): If config's option is empty the default hardcoded value 'Federated' will be used. - :raise AssertionError if domain named match the value in the config. + :raise AssertionError: if domain named match the value in the config. """ # NOTE(marek-denis): We cannot create this attribute in the __init__ as @@ -149,7 +149,7 @@ class Manager(manager.Manager): def assert_project_enabled(self, project_id, project=None): """Assert the project is enabled and its associated domain is enabled. - :raise AssertionError if the project or domain is disabled. + :raise AssertionError: if the project or domain is disabled. """ if project is None: project = self.get_project(project_id) @@ -460,6 +460,7 @@ class Manager(manager.Manager): # Delete any database stored domain config self.domain_config_api.delete_config_options(domain_id) self.domain_config_api.delete_config_options(domain_id, sensitive=True) + self.domain_config_api.release_registration(domain_id) # TODO(henry-nash): Although the controller will ensure deletion of # all users & groups within the domain (which will cause all # assignments for those users/groups to also be deleted), there @@ -541,7 +542,7 @@ class Manager(manager.Manager): @six.add_metaclass(abc.ABCMeta) -class Driver(object): +class ResourceDriverV8(object): def _get_list_limit(self): return CONF.resource.list_limit or CONF.list_limit @@ -551,7 +552,8 @@ class Driver(object): """Get a tenant by name. :returns: tenant_ref - :raises: keystone.exception.ProjectNotFound + :raises keystone.exception.ProjectNotFound: if a project with the + tenant_name does not exist within the domain """ raise exception.NotImplemented() # pragma: no cover @@ -561,7 +563,8 @@ class Driver(object): def create_domain(self, domain_id, domain): """Creates a new domain. - :raises: keystone.exception.Conflict + :raises keystone.exception.Conflict: if the domain_id or domain name + already exists """ raise exception.NotImplemented() # pragma: no cover @@ -597,7 +600,7 @@ class Driver(object): """Get a domain by ID. :returns: domain_ref - :raises: keystone.exception.DomainNotFound + :raises keystone.exception.DomainNotFound: if domain_id does not exist """ raise exception.NotImplemented() # pragma: no cover @@ -607,7 +610,8 @@ class Driver(object): """Get a domain by name. :returns: domain_ref - :raises: keystone.exception.DomainNotFound + :raises keystone.exception.DomainNotFound: if domain_name does not + exist """ raise exception.NotImplemented() # pragma: no cover @@ -616,8 +620,8 @@ class Driver(object): def update_domain(self, domain_id, domain): """Updates an existing domain. - :raises: keystone.exception.DomainNotFound, - keystone.exception.Conflict + :raises keystone.exception.DomainNotFound: if domain_id does not exist + :raises keystone.exception.Conflict: if domain name already exists """ raise exception.NotImplemented() # pragma: no cover @@ -626,7 +630,7 @@ class Driver(object): def delete_domain(self, domain_id): """Deletes an existing domain. - :raises: keystone.exception.DomainNotFound + :raises keystone.exception.DomainNotFound: if domain_id does not exist """ raise exception.NotImplemented() # pragma: no cover @@ -636,7 +640,8 @@ class Driver(object): def create_project(self, project_id, project): """Creates a new project. - :raises: keystone.exception.Conflict + :raises keystone.exception.Conflict: if project_id or project name + already exists """ raise exception.NotImplemented() # pragma: no cover @@ -698,7 +703,8 @@ class Driver(object): """Get a project by ID. :returns: project_ref - :raises: keystone.exception.ProjectNotFound + :raises keystone.exception.ProjectNotFound: if project_id does not + exist """ raise exception.NotImplemented() # pragma: no cover @@ -707,8 +713,9 @@ class Driver(object): def update_project(self, project_id, project): """Updates an existing project. - :raises: keystone.exception.ProjectNotFound, - keystone.exception.Conflict + :raises keystone.exception.ProjectNotFound: if project_id does not + exist + :raises keystone.exception.Conflict: if project name already exists """ raise exception.NotImplemented() # pragma: no cover @@ -717,7 +724,8 @@ class Driver(object): def delete_project(self, project_id): """Deletes an existing project. - :raises: keystone.exception.ProjectNotFound + :raises keystone.exception.ProjectNotFound: if project_id does not + exist """ raise exception.NotImplemented() # pragma: no cover @@ -730,7 +738,8 @@ class Driver(object): project. :returns: a list of project_refs or an empty list. - :raises: keystone.exception.ProjectNotFound + :raises keystone.exception.ProjectNotFound: if project_id does not + exist """ raise exception.NotImplemented() @@ -744,7 +753,8 @@ class Driver(object): this project. :returns: a list of project_refs or an empty list - :raises: keystone.exception.ProjectNotFound + :raises keystone.exception.ProjectNotFound: if project_id does not + exist """ raise exception.NotImplemented() @@ -756,7 +766,8 @@ class Driver(object): :param project_id: the driver will check if this project is a leaf in the hierarchy. - :raises: keystone.exception.ProjectNotFound + :raises keystone.exception.ProjectNotFound: if project_id does not + exist """ raise exception.NotImplemented() @@ -796,6 +807,9 @@ class Driver(object): raise exception.DomainNotFound(domain_id=domain_id) +Driver = manager.create_legacy_driver(ResourceDriverV8) + + MEMOIZE_CONFIG = cache.get_memoization_decorator(section='domain_config') @@ -1272,7 +1286,7 @@ class DomainConfigManager(manager.Manager): @six.add_metaclass(abc.ABCMeta) -class DomainConfigDriver(object): +class DomainConfigDriverV8(object): """Interface description for a Domain Config driver.""" @abc.abstractmethod @@ -1287,7 +1301,7 @@ class DomainConfigDriver(object): :param sensitive: whether the option is sensitive :returns: dict containing group, option and value - :raises: keystone.exception.Conflict + :raises keystone.exception.Conflict: when the option already exists """ raise exception.NotImplemented() # pragma: no cover @@ -1302,8 +1316,8 @@ class DomainConfigDriver(object): :param sensitive: whether the option is sensitive :returns: dict containing group, option and value - :raises: keystone.exception.DomainConfigNotFound: the option doesn't - exist. + :raises keystone.exception.DomainConfigNotFound: the option doesn't + exist. """ raise exception.NotImplemented() # pragma: no cover @@ -1336,8 +1350,8 @@ class DomainConfigDriver(object): :param sensitive: whether the option is sensitive :returns: dict containing updated group, option and value - :raises: keystone.exception.DomainConfigNotFound: the option doesn't - exist. + :raises keystone.exception.DomainConfigNotFound: the option doesn't + exist. """ raise exception.NotImplemented() # pragma: no cover @@ -1359,3 +1373,45 @@ class DomainConfigDriver(object): """ raise exception.NotImplemented() # pragma: no cover + + @abc.abstractmethod + def obtain_registration(self, domain_id, type): + """Try and register this domain to use the type specified. + + :param domain_id: the domain required + :param type: type of registration + :returns: True if the domain was registered, False otherwise. Failing + to register means that someone already has it (which could + even be the domain being requested). + + """ + raise exception.NotImplemented() # pragma: no cover + + @abc.abstractmethod + def read_registration(self, type): + """Get the domain ID of who is registered to use this type. + + :param type: type of registration + :returns: domain_id of who is registered. + :raises: keystone.exception.ConfigRegistrationNotFound: nobody is + registered. + + """ + raise exception.NotImplemented() # pragma: no cover + + @abc.abstractmethod + def release_registration(self, domain_id, type=None): + """Release registration if it is held by the domain specified. + + If the specified domain is registered for this domain then free it, + if it is not then do nothing - no exception is raised. + + :param domain_id: the domain in question + :param type: type of registration, if None then all registrations + for this domain will be freed + + """ + raise exception.NotImplemented() # pragma: no cover + + +DomainConfigDriver = manager.create_legacy_driver(DomainConfigDriverV8) diff --git a/keystone-moon/keystone/server/common.py b/keystone-moon/keystone/server/common.py index 2de6d39e..7bc5958e 100644 --- a/keystone-moon/keystone/server/common.py +++ b/keystone-moon/keystone/server/common.py @@ -13,14 +13,17 @@ from oslo_config import cfg +from oslo_log import log from keystone.common import dependency from keystone.common import sql from keystone import config +from keystone.i18n import _LW from keystone.server import backends CONF = cfg.CONF +LOG = log.getLogger(__name__) def configure(version=None, config_files=None, @@ -35,6 +38,11 @@ def configure(version=None, config_files=None, pre_setup_logging_fn() config.setup_logging() + if CONF.debug: + LOG.warn(_LW( + 'debug is enabled so responses may include sensitive ' + 'information.')) + def setup_backends(load_extra_backends_fn=lambda: {}, startup_application_fn=lambda: None): diff --git a/keystone-moon/keystone/server/wsgi.py b/keystone-moon/keystone/server/wsgi.py index dbdad326..ae24c48e 100644 --- a/keystone-moon/keystone/server/wsgi.py +++ b/keystone-moon/keystone/server/wsgi.py @@ -16,6 +16,7 @@ import logging from oslo_config import cfg import oslo_i18n +import oslo_middleware.cors as cors # NOTE(dstanek): i18n.enable_lazy() must be called before @@ -27,12 +28,24 @@ oslo_i18n.enable_lazy() from keystone.common import environment from keystone import config +import keystone.middleware.core as middleware_core from keystone.server import common from keystone import service as keystone_service CONF = cfg.CONF +KEYSTONE_HEADERS = [ + middleware_core.AUTH_TOKEN_HEADER, + middleware_core.SUBJECT_TOKEN_HEADER, + 'X-Project-Id', + 'X-Project-Name', + 'X-Project-Domain-Id', + 'X-Project-Domain-Name', + 'X-Domain-Id', + 'X-Domain-Name' +] + def initialize_application(name): common.configure() @@ -49,6 +62,15 @@ def initialize_application(name): _unused, application = common.setup_backends( startup_application_fn=loadapp) + + # Create a CORS wrapper, and attach keystone-specific defaults that must be + # included in all CORS responses + application = cors.CORS(application, CONF) + application.set_latent( + allow_headers=KEYSTONE_HEADERS, + allow_methods=['GET', 'PUT', 'POST', 'DELETE', 'PATCH'], + expose_headers=KEYSTONE_HEADERS + ) return application diff --git a/keystone-moon/keystone/tests/unit/__init__.py b/keystone-moon/keystone/tests/unit/__init__.py index 837afe69..52af8dfc 100644 --- a/keystone-moon/keystone/tests/unit/__init__.py +++ b/keystone-moon/keystone/tests/unit/__init__.py @@ -13,22 +13,6 @@ # under the License. import oslo_i18n -import six - - -if six.PY3: - # NOTE(dstanek): This block will monkey patch libraries that are not - # yet supported in Python3. We do this that that it is possible to - # execute any tests at all. Without monkey patching modules the - # tests will fail with import errors. - - import sys - from unittest import mock # noqa: our import detection is naive? - - sys.modules['ldappool'] = mock.Mock() - sys.modules['memcache'] = mock.Mock() - sys.modules['oslo_messaging'] = mock.Mock() - sys.modules['paste'] = mock.Mock() # NOTE(dstanek): oslo_i18n.enable_lazy() must be called before # keystone.i18n._() is called to ensure it has the desired lazy lookup diff --git a/keystone-moon/keystone/tests/unit/backend/core_ldap.py b/keystone-moon/keystone/tests/unit/backend/core_ldap.py index a6cd0802..869bb620 100644 --- a/keystone-moon/keystone/tests/unit/backend/core_ldap.py +++ b/keystone-moon/keystone/tests/unit/backend/core_ldap.py @@ -17,7 +17,7 @@ from oslo_config import cfg from keystone.common import cache from keystone.common import ldap as common_ldap from keystone.common.ldap import core as common_ldap_core -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import default_fixtures from keystone.tests.unit import fakeldap from keystone.tests.unit.ksfixtures import database @@ -66,7 +66,7 @@ class BaseBackendLdapCommon(object): def config_files(self): config_files = super(BaseBackendLdapCommon, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_ldap.conf')) + config_files.append(unit.dirs.tests_conf('backend_ldap.conf')) return config_files def get_user_enabled_vals(self, user): @@ -99,13 +99,13 @@ class BaseBackendLdap(object): super(BaseBackendLdap, self).load_fixtures(fixtures) -class BaseBackendLdapIdentitySqlEverythingElse(tests.SQLDriverOverrides): +class BaseBackendLdapIdentitySqlEverythingElse(unit.SQLDriverOverrides): """Mixin base for Identity LDAP, everything else SQL backend tests.""" def config_files(self): config_files = super(BaseBackendLdapIdentitySqlEverythingElse, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_ldap_sql.conf')) + config_files.append(unit.dirs.tests_conf('backend_ldap_sql.conf')) return config_files def setUp(self): diff --git a/keystone-moon/keystone/tests/unit/backend/core_sql.py b/keystone-moon/keystone/tests/unit/backend/core_sql.py index 9cbd858e..8c9f4957 100644 --- a/keystone-moon/keystone/tests/unit/backend/core_sql.py +++ b/keystone-moon/keystone/tests/unit/backend/core_sql.py @@ -13,12 +13,12 @@ import sqlalchemy from keystone.common import sql -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import default_fixtures from keystone.tests.unit.ksfixtures import database -class BaseBackendSqlTests(tests.SQLDriverOverrides, tests.TestCase): +class BaseBackendSqlTests(unit.SQLDriverOverrides, unit.TestCase): def setUp(self): super(BaseBackendSqlTests, self).setUp() @@ -32,7 +32,7 @@ class BaseBackendSqlTests(tests.SQLDriverOverrides, tests.TestCase): def config_files(self): config_files = super(BaseBackendSqlTests, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_sql.conf')) + config_files.append(unit.dirs.tests_conf('backend_sql.conf')) return config_files diff --git a/keystone-moon/keystone/tests/unit/backend/domain_config/core.py b/keystone-moon/keystone/tests/unit/backend/domain_config/core.py index c53d99b7..7bbbf313 100644 --- a/keystone-moon/keystone/tests/unit/backend/domain_config/core.py +++ b/keystone-moon/keystone/tests/unit/backend/domain_config/core.py @@ -17,7 +17,7 @@ import mock from testtools import matchers from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit class DomainConfigTests(object): @@ -523,7 +523,7 @@ class DomainConfigTests(object): # The escaping '%' should have been removed self.assertEqual('my_url/%(password)s', res['ldap']['url']) - @tests.skip_if_cache_disabled('domain_config') + @unit.skip_if_cache_disabled('domain_config') def test_cache_layer_get_sensitive_config(self): config = {'ldap': {'url': uuid.uuid4().hex, 'user_tree_dn': uuid.uuid4().hex, @@ -549,3 +549,53 @@ class DomainConfigTests(object): {}, self.domain_config_api.get_config_with_sensitive_info( self.domain['id'])) + + def test_config_registration(self): + type = uuid.uuid4().hex + self.domain_config_api.obtain_registration( + self.domain['id'], type) + self.domain_config_api.release_registration( + self.domain['id'], type=type) + + # Make sure that once someone has it, nobody else can get it. + # This includes the domain who already has it. + self.domain_config_api.obtain_registration( + self.domain['id'], type) + self.assertFalse( + self.domain_config_api.obtain_registration( + self.domain['id'], type)) + + # Make sure we can read who does have it + self.assertEqual( + self.domain['id'], + self.domain_config_api.read_registration(type)) + + # Make sure releasing it is silent if the domain specified doesn't + # have the registration + domain2 = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex} + self.resource_api.create_domain(domain2['id'], domain2) + self.domain_config_api.release_registration( + domain2['id'], type=type) + + # If nobody has the type registered, then trying to read it should + # raise ConfigRegistrationNotFound + self.domain_config_api.release_registration( + self.domain['id'], type=type) + self.assertRaises(exception.ConfigRegistrationNotFound, + self.domain_config_api.read_registration, + type) + + # Finally check multiple registrations are cleared if you free the + # registration without specifying the type + type2 = uuid.uuid4().hex + self.domain_config_api.obtain_registration( + self.domain['id'], type) + self.domain_config_api.obtain_registration( + self.domain['id'], type2) + self.domain_config_api.release_registration(self.domain['id']) + self.assertRaises(exception.ConfigRegistrationNotFound, + self.domain_config_api.read_registration, + type) + self.assertRaises(exception.ConfigRegistrationNotFound, + self.domain_config_api.read_registration, + type2) diff --git a/keystone-moon/keystone/tests/unit/backend/role/core.py b/keystone-moon/keystone/tests/unit/backend/role/core.py index f6e47fe9..d6e0d65c 100644 --- a/keystone-moon/keystone/tests/unit/backend/role/core.py +++ b/keystone-moon/keystone/tests/unit/backend/role/core.py @@ -16,7 +16,7 @@ import copy import uuid from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import default_fixtures @@ -87,7 +87,7 @@ class RoleTests(object): expected_role_ids = set(role['id'] for role in default_fixtures.ROLES) self.assertEqual(expected_role_ids, role_ids) - @tests.skip_if_cache_disabled('role') + @unit.skip_if_cache_disabled('role') def test_cache_layer_role_crud(self): role = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex} role_id = role['id'] diff --git a/keystone-moon/keystone/tests/unit/backend/role/test_ldap.py b/keystone-moon/keystone/tests/unit/backend/role/test_ldap.py index ba4b7c6e..44f2b612 100644 --- a/keystone-moon/keystone/tests/unit/backend/role/test_ldap.py +++ b/keystone-moon/keystone/tests/unit/backend/role/test_ldap.py @@ -16,7 +16,7 @@ import uuid from oslo_config import cfg from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit.backend import core_ldap from keystone.tests.unit.backend.role import core as core_role from keystone.tests.unit import default_fixtures @@ -35,7 +35,7 @@ class LdapRoleCommon(core_ldap.BaseBackendLdapCommon, core_role.RoleTests): pass -class LdapRole(LdapRoleCommon, core_ldap.BaseBackendLdap, tests.TestCase): +class LdapRole(LdapRoleCommon, core_ldap.BaseBackendLdap, unit.TestCase): """Test in an all-LDAP configuration. Include additional tests that are unique to LDAP (or need to be overridden) @@ -149,7 +149,7 @@ class LdapRole(LdapRoleCommon, core_ldap.BaseBackendLdap, tests.TestCase): class LdapIdentitySqlEverythingElseRole( core_ldap.BaseBackendLdapIdentitySqlEverythingElse, LdapRoleCommon, - tests.TestCase): + unit.TestCase): """Test Identity in LDAP, Everything else in SQL.""" pass diff --git a/keystone-moon/keystone/tests/unit/common/test_base64utils.py b/keystone-moon/keystone/tests/unit/common/test_base64utils.py index b0b75578..355a2e03 100644 --- a/keystone-moon/keystone/tests/unit/common/test_base64utils.py +++ b/keystone-moon/keystone/tests/unit/common/test_base64utils.py @@ -13,7 +13,7 @@ # under the License. from keystone.common import base64utils -from keystone.tests import unit as tests +from keystone.tests import unit base64_alphabet = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' @@ -26,7 +26,7 @@ base64url_alphabet = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ' '-_=') # includes pad char -class TestValid(tests.BaseTestCase): +class TestValid(unit.BaseTestCase): def test_valid_base64(self): self.assertTrue(base64utils.is_valid_base64('+/==')) self.assertTrue(base64utils.is_valid_base64('+/+=')) @@ -68,7 +68,7 @@ class TestValid(tests.BaseTestCase): self.assertTrue(base64utils.is_valid_base64url('-_==')) -class TestBase64Padding(tests.BaseTestCase): +class TestBase64Padding(unit.BaseTestCase): def test_filter(self): self.assertEqual('', base64utils.filter_formatting('')) @@ -189,7 +189,7 @@ class TestBase64Padding(tests.BaseTestCase): base64utils.base64url_percent_decode, 'AB%3D%3') -class TestTextWrap(tests.BaseTestCase): +class TestTextWrap(unit.BaseTestCase): def test_wrapping(self): raw_text = 'abcdefgh' diff --git a/keystone-moon/keystone/tests/unit/common/test_injection.py b/keystone-moon/keystone/tests/unit/common/test_injection.py index b4c23a84..9a5d1e7d 100644 --- a/keystone-moon/keystone/tests/unit/common/test_injection.py +++ b/keystone-moon/keystone/tests/unit/common/test_injection.py @@ -15,10 +15,10 @@ import uuid from keystone.common import dependency -from keystone.tests import unit as tests +from keystone.tests import unit -class TestDependencyInjection(tests.BaseTestCase): +class TestDependencyInjection(unit.BaseTestCase): def setUp(self): super(TestDependencyInjection, self).setUp() dependency.reset() diff --git a/keystone-moon/keystone/tests/unit/common/test_json_home.py b/keystone-moon/keystone/tests/unit/common/test_json_home.py index fb7f8448..94e2d138 100644 --- a/keystone-moon/keystone/tests/unit/common/test_json_home.py +++ b/keystone-moon/keystone/tests/unit/common/test_json_home.py @@ -18,10 +18,10 @@ import copy from testtools import matchers from keystone.common import json_home -from keystone.tests import unit as tests +from keystone.tests import unit -class JsonHomeTest(tests.BaseTestCase): +class JsonHomeTest(unit.BaseTestCase): def test_build_v3_resource_relation(self): resource_name = self.getUniqueString() relation = json_home.build_v3_resource_relation(resource_name) diff --git a/keystone-moon/keystone/tests/unit/common/test_ldap.py b/keystone-moon/keystone/tests/unit/common/test_ldap.py index d3ce8cd2..e6e2c732 100644 --- a/keystone-moon/keystone/tests/unit/common/test_ldap.py +++ b/keystone-moon/keystone/tests/unit/common/test_ldap.py @@ -24,7 +24,7 @@ from testtools import matchers from keystone.common import driver_hints from keystone.common import ldap as ks_ldap from keystone.common.ldap import core as common_ldap_core -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import default_fixtures from keystone.tests.unit import fakeldap @@ -32,7 +32,7 @@ from keystone.tests.unit import fakeldap CONF = cfg.CONF -class DnCompareTest(tests.BaseTestCase): +class DnCompareTest(unit.BaseTestCase): """Tests for the DN comparison functions in keystone.common.ldap.core.""" def test_prep(self): @@ -200,7 +200,7 @@ class DnCompareTest(tests.BaseTestCase): self.assertTrue(ks_ldap.dn_startswith(child, parent)) -class LDAPDeleteTreeTest(tests.TestCase): +class LDAPDeleteTreeTest(unit.TestCase): def setUp(self): super(LDAPDeleteTreeTest, self).setUp() @@ -223,7 +223,7 @@ class LDAPDeleteTreeTest(tests.TestCase): def config_files(self): config_files = super(LDAPDeleteTreeTest, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_ldap.conf')) + config_files.append(unit.dirs.tests_conf('backend_ldap.conf')) return config_files def test_deleteTree(self): @@ -283,7 +283,7 @@ class LDAPDeleteTreeTest(tests.TestCase): conn.search_s, grandchild_dn, ldap.SCOPE_BASE) -class SslTlsTest(tests.TestCase): +class SslTlsTest(unit.TestCase): """Tests for the SSL/TLS functionality in keystone.common.ldap.core.""" @mock.patch.object(ks_ldap.core.KeystoneLDAPHandler, 'simple_bind_s') @@ -350,7 +350,7 @@ class SslTlsTest(tests.TestCase): self.assertEqual(certdir, ldap.get_option(ldap.OPT_X_TLS_CACERTDIR)) -class LDAPPagedResultsTest(tests.TestCase): +class LDAPPagedResultsTest(unit.TestCase): """Tests the paged results functionality in keystone.common.ldap.core.""" def setUp(self): @@ -373,7 +373,7 @@ class LDAPPagedResultsTest(tests.TestCase): def config_files(self): config_files = super(LDAPPagedResultsTest, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_ldap.conf')) + config_files.append(unit.dirs.tests_conf('backend_ldap.conf')) return config_files @mock.patch.object(fakeldap.FakeLdap, 'search_ext') @@ -390,7 +390,7 @@ class LDAPPagedResultsTest(tests.TestCase): 'objectclass=*') -class CommonLdapTestCase(tests.BaseTestCase): +class CommonLdapTestCase(unit.BaseTestCase): """These test cases call functions in keystone.common.ldap.""" def test_binary_attribute_values(self): @@ -497,7 +497,7 @@ class CommonLdapTestCase(tests.BaseTestCase): self.assertEqual(user_name, py_result[0][1]['user_name'][0]) -class LDAPFilterQueryCompositionTest(tests.TestCase): +class LDAPFilterQueryCompositionTest(unit.TestCase): """These test cases test LDAP filter generation.""" def setUp(self): diff --git a/keystone-moon/keystone/tests/unit/common/test_manager.py b/keystone-moon/keystone/tests/unit/common/test_manager.py new file mode 100644 index 00000000..1bc19763 --- /dev/null +++ b/keystone-moon/keystone/tests/unit/common/test_manager.py @@ -0,0 +1,39 @@ +# 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 mock + +from keystone import catalog +from keystone.common import manager +from keystone.tests import unit + + +class TestCreateLegacyDriver(unit.BaseTestCase): + + @mock.patch('oslo_log.versionutils.report_deprecated_feature') + def test_class_is_properly_deprecated(self, mock_reporter): + Driver = manager.create_legacy_driver(catalog.CatalogDriverV8) + + # NOTE(dstanek): I want to subvert the requirement for this + # class to implement all of the abstractmethods. + Driver.__abstractmethods__ = set() + impl = Driver() + + details = { + 'as_of': 'Liberty', + 'what': 'keystone.catalog.core.Driver', + 'in_favor_of': 'keystone.catalog.core.CatalogDriverV8', + 'remove_in': 'N', + } + mock_reporter.assert_called_with(mock.ANY, mock.ANY, details) + + self.assertIsInstance(impl, catalog.CatalogDriverV8) diff --git a/keystone-moon/keystone/tests/unit/common/test_notifications.py b/keystone-moon/keystone/tests/unit/common/test_notifications.py index 2d872733..ec087c41 100644 --- a/keystone-moon/keystone/tests/unit/common/test_notifications.py +++ b/keystone-moon/keystone/tests/unit/common/test_notifications.py @@ -1007,7 +1007,7 @@ class TestCallbackRegistration(unit.BaseTestCase): with mock.patch('keystone.notifications.LOG', self.mock_log): notifications.register_event_callback( - CREATED_OPERATION, 'thing', C.callback) + CREATED_OPERATION, 'thing', C().callback) callback = 'keystone.tests.unit.common.test_notifications.C.callback' expected_log_data = { @@ -1026,7 +1026,7 @@ class TestCallbackRegistration(unit.BaseTestCase): with mock.patch('keystone.notifications.LOG', self.mock_log): notifications.register_event_callback( - CREATED_OPERATION, 'thing', [callback, C.callback]) + CREATED_OPERATION, 'thing', [callback, C().callback]) callback_1 = 'keystone.tests.unit.common.test_notifications.callback' callback_2 = 'keystone.tests.unit.common.test_notifications.C.callback' diff --git a/keystone-moon/keystone/tests/unit/common/test_sql_core.py b/keystone-moon/keystone/tests/unit/common/test_sql_core.py index 1f33cfc3..b110ed08 100644 --- a/keystone-moon/keystone/tests/unit/common/test_sql_core.py +++ b/keystone-moon/keystone/tests/unit/common/test_sql_core.py @@ -14,7 +14,7 @@ from sqlalchemy.ext import declarative from keystone.common import sql -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import utils @@ -27,7 +27,7 @@ class TestModel(ModelBase, sql.ModelDictMixin): text = sql.Column(sql.String(64), nullable=False) -class TestModelDictMixin(tests.BaseTestCase): +class TestModelDictMixin(unit.BaseTestCase): def test_creating_a_model_instance_from_a_dict(self): d = {'id': utils.new_uuid(), 'text': utils.new_uuid()} diff --git a/keystone-moon/keystone/tests/unit/common/test_utils.py b/keystone-moon/keystone/tests/unit/common/test_utils.py index e8bac3c0..d52eb729 100644 --- a/keystone-moon/keystone/tests/unit/common/test_utils.py +++ b/keystone-moon/keystone/tests/unit/common/test_utils.py @@ -20,7 +20,7 @@ from oslo_serialization import jsonutils from keystone.common import utils as common_utils from keystone import exception from keystone import service -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import utils @@ -29,7 +29,7 @@ CONF = cfg.CONF TZ = utils.TZ -class UtilsTestCase(tests.BaseTestCase): +class UtilsTestCase(unit.BaseTestCase): OPTIONAL = object() def setUp(self): @@ -154,11 +154,11 @@ class UtilsTestCase(tests.BaseTestCase): self.assertEqual(expected_json, json) -class ServiceHelperTests(tests.BaseTestCase): +class ServiceHelperTests(unit.BaseTestCase): @service.fail_gracefully def _do_test(self): raise Exception("Test Exc") def test_fail_gracefully(self): - self.assertRaises(tests.UnexpectedExit, self._do_test) + self.assertRaises(unit.UnexpectedExit, self._do_test) diff --git a/keystone-moon/keystone/tests/unit/contrib/federation/test_utils.py b/keystone-moon/keystone/tests/unit/contrib/federation/test_utils.py index a8b4ae76..5804f1c0 100644 --- a/keystone-moon/keystone/tests/unit/contrib/federation/test_utils.py +++ b/keystone-moon/keystone/tests/unit/contrib/federation/test_utils.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +import uuid from keystone.auth.plugins import mapped from keystone.contrib.federation import utils as mapping_utils @@ -609,3 +610,25 @@ class MappingRuleEngineTests(unit.BaseTestCase): self.assertEqual(exp_user_name, mapped_properties['user']['name']) self.assertEqual('abc123%40example.com', mapped_properties['user']['id']) + + def test_whitelist_pass_through(self): + mapping = mapping_fixtures.MAPPING_GROUPS_WHITELIST_PASS_THROUGH + rp = mapping_utils.RuleProcessor(mapping['rules']) + assertion = mapping_fixtures.DEVELOPER_ASSERTION + mapped_properties = rp.process(assertion) + self.assertValidMappedUserObject(mapped_properties) + + self.assertEqual('developacct', mapped_properties['user']['name']) + self.assertEqual('Developer', + mapped_properties['group_names'][0]['name']) + + def test_type_not_in_assertion(self): + """Test that if the remote "type" is not in the assertion it fails.""" + mapping = mapping_fixtures.MAPPING_GROUPS_WHITELIST_PASS_THROUGH + rp = mapping_utils.RuleProcessor(mapping['rules']) + assertion = {uuid.uuid4().hex: uuid.uuid4().hex} + mapped_properties = rp.process(assertion) + self.assertValidMappedUserObject(mapped_properties) + + self.assertNotIn('id', mapped_properties['user']) + self.assertNotIn('name', mapped_properties['user']) diff --git a/keystone-moon/keystone/tests/unit/core.py b/keystone-moon/keystone/tests/unit/core.py index e999b641..eb8b9f65 100644 --- a/keystone-moon/keystone/tests/unit/core.py +++ b/keystone-moon/keystone/tests/unit/core.py @@ -14,6 +14,7 @@ from __future__ import absolute_import import atexit +import datetime import functools import logging import os @@ -21,18 +22,21 @@ import re import shutil import socket import sys +import uuid import warnings import fixtures from oslo_config import cfg from oslo_config import fixture as config_fixture +from oslo_log import fixture as log_fixture from oslo_log import log +from oslo_utils import timeutils import oslotest.base as oslotest from oslotest import mockpatch +from paste.deploy import loadwsgi import six from sqlalchemy import exc from testtools import testcase -import webob # NOTE(ayoung) # environment.use_eventlet must run before any of the code that will @@ -82,6 +86,8 @@ rules.init() IN_MEM_DB_CONN_STRING = 'sqlite://' +TIME_FORMAT = '%Y-%m-%dT%H:%M:%S.%fZ' + exception._FATAL_EXCEPTION_FORMAT_ERRORS = True os.makedirs(TMPDIR) atexit.register(shutil.rmtree, TMPDIR) @@ -113,6 +119,26 @@ class dirs(object): DEFAULT_TEST_DB_FILE = dirs.tmp('test.db') +class EggLoader(loadwsgi.EggLoader): + _basket = {} + + def find_egg_entry_point(self, object_type, name=None): + egg_key = '%s:%s' % (object_type, name) + egg_ep = self._basket.get(egg_key) + if not egg_ep: + egg_ep = super(EggLoader, self).find_egg_entry_point( + object_type, name=name) + self._basket[egg_key] = egg_ep + return egg_ep + + +# NOTE(dstanek): class paths were remove from the keystone-paste.ini in +# favor of using entry points. This caused tests to slow to a crawl +# since we reload the application object for each RESTful test. This +# monkey-patching adds caching to paste deploy's egg lookup. +loadwsgi.EggLoader = EggLoader + + @atexit.register def remove_test_databases(): db = dirs.tmp('test.db') @@ -197,39 +223,135 @@ class UnexpectedExit(Exception): pass -class BadLog(Exception): - """Raised on invalid call to logging (parameter mismatch).""" - pass - - -class TestClient(object): - def __init__(self, app=None, token=None): - self.app = app - self.token = token - - def request(self, method, path, headers=None, body=None): - if headers is None: - headers = {} - - if self.token: - headers.setdefault('X-Auth-Token', self.token) - - req = webob.Request.blank(path) - req.method = method - for k, v in headers.items(): - req.headers[k] = v - if body: - req.body = body - return req.get_response(self.app) - - def get(self, path, headers=None): - return self.request('GET', path=path, headers=headers) - - def post(self, path, headers=None, body=None): - return self.request('POST', path=path, headers=headers, body=body) - - def put(self, path, headers=None, body=None): - return self.request('PUT', path=path, headers=headers, body=body) +def new_ref(): + """Populates a ref with attributes common to some API entities.""" + return { + 'id': uuid.uuid4().hex, + 'name': uuid.uuid4().hex, + 'description': uuid.uuid4().hex, + 'enabled': True} + + +def new_region_ref(): + ref = new_ref() + # Region doesn't have name or enabled. + del ref['name'] + del ref['enabled'] + ref['parent_region_id'] = None + return ref + + +def new_service_ref(): + ref = new_ref() + ref['type'] = uuid.uuid4().hex + return ref + + +def new_endpoint_ref(service_id, interface='public', default_region_id=None, + **kwargs): + ref = new_ref() + del ref['enabled'] # enabled is optional + ref['interface'] = interface + ref['service_id'] = service_id + ref['url'] = 'https://' + uuid.uuid4().hex + '.com' + ref['region_id'] = default_region_id + ref.update(kwargs) + return ref + + +def new_domain_ref(): + ref = new_ref() + return ref + + +def new_project_ref(domain_id=None, parent_id=None, is_domain=False): + ref = new_ref() + ref['domain_id'] = domain_id + ref['parent_id'] = parent_id + ref['is_domain'] = is_domain + return ref + + +def new_user_ref(domain_id, project_id=None): + ref = new_ref() + ref['domain_id'] = domain_id + ref['email'] = uuid.uuid4().hex + ref['password'] = uuid.uuid4().hex + if project_id: + ref['default_project_id'] = project_id + return ref + + +def new_group_ref(domain_id): + ref = new_ref() + ref['domain_id'] = domain_id + return ref + + +def new_credential_ref(user_id, project_id=None, cred_type=None): + ref = dict() + ref['id'] = uuid.uuid4().hex + ref['user_id'] = user_id + if cred_type == 'ec2': + ref['type'] = 'ec2' + ref['blob'] = uuid.uuid4().hex + else: + ref['type'] = 'cert' + ref['blob'] = uuid.uuid4().hex + if project_id: + ref['project_id'] = project_id + return ref + + +def new_role_ref(): + ref = new_ref() + # Roles don't have a description or the enabled flag + del ref['description'] + del ref['enabled'] + return ref + + +def new_policy_ref(): + ref = new_ref() + ref['blob'] = uuid.uuid4().hex + ref['type'] = uuid.uuid4().hex + return ref + + +def new_trust_ref(trustor_user_id, trustee_user_id, project_id=None, + impersonation=None, expires=None, role_ids=None, + role_names=None, remaining_uses=None, + allow_redelegation=False): + ref = dict() + ref['id'] = uuid.uuid4().hex + ref['trustor_user_id'] = trustor_user_id + ref['trustee_user_id'] = trustee_user_id + ref['impersonation'] = impersonation or False + ref['project_id'] = project_id + ref['remaining_uses'] = remaining_uses + ref['allow_redelegation'] = allow_redelegation + + if isinstance(expires, six.string_types): + ref['expires_at'] = expires + elif isinstance(expires, dict): + ref['expires_at'] = ( + timeutils.utcnow() + datetime.timedelta(**expires) + ).strftime(TIME_FORMAT) + elif expires is None: + pass + else: + raise NotImplementedError('Unexpected value for "expires"') + + role_ids = role_ids or [] + role_names = role_names or [] + if role_ids or role_names: + ref['roles'] = [] + for role_id in role_ids: + ref['roles'].append({'id': role_id}) + for role_name in role_names: + ref['roles'].append({'name': role_name}) + + return ref class BaseTestCase(oslotest.BaseTestCase): @@ -245,8 +367,7 @@ class BaseTestCase(oslotest.BaseTestCase): super(BaseTestCase, self).setUp() self.useFixture(mockpatch.PatchObject(sys, 'exit', side_effect=UnexpectedExit)) - self.useFixture(mockpatch.PatchObject(logging.Handler, 'handleError', - side_effect=BadLog)) + self.useFixture(log_fixture.get_logging_handle_error_fixture()) warnings.filterwarnings('error', category=DeprecationWarning, module='^keystone\\.') diff --git a/keystone-moon/keystone/tests/unit/identity/test_core.py b/keystone-moon/keystone/tests/unit/identity/test_core.py index fa95ec50..e9845401 100644 --- a/keystone-moon/keystone/tests/unit/identity/test_core.py +++ b/keystone-moon/keystone/tests/unit/identity/test_core.py @@ -22,20 +22,20 @@ from oslo_config import fixture as config_fixture from keystone import exception from keystone import identity -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit.ksfixtures import database CONF = cfg.CONF -class TestDomainConfigs(tests.BaseTestCase): +class TestDomainConfigs(unit.BaseTestCase): def setUp(self): super(TestDomainConfigs, self).setUp() self.addCleanup(CONF.reset) - self.tmp_dir = tests.dirs.tmp() + self.tmp_dir = unit.dirs.tmp() self.config_fixture = self.useFixture(config_fixture.Config(CONF)) self.config_fixture.config(domain_config_dir=self.tmp_dir, @@ -125,7 +125,7 @@ class TestDomainConfigs(tests.BaseTestCase): self.assertEqual(3, load_driver_mock.call_count) -class TestDatabaseDomainConfigs(tests.TestCase): +class TestDatabaseDomainConfigs(unit.TestCase): def setUp(self): super(TestDatabaseDomainConfigs, self).setUp() diff --git a/keystone-moon/keystone/tests/unit/ksfixtures/database.py b/keystone-moon/keystone/tests/unit/ksfixtures/database.py index 0012df74..6f23a99d 100644 --- a/keystone-moon/keystone/tests/unit/ksfixtures/database.py +++ b/keystone-moon/keystone/tests/unit/ksfixtures/database.py @@ -19,7 +19,7 @@ from oslo_config import cfg from oslo_db import options as db_options from keystone.common import sql -from keystone.tests import unit as tests +from keystone.tests import unit CONF = cfg.CONF @@ -47,7 +47,7 @@ def initialize_sql_session(): # test cases. db_options.set_defaults( CONF, - connection=tests.IN_MEM_DB_CONN_STRING) + connection=unit.IN_MEM_DB_CONN_STRING) @run_once diff --git a/keystone-moon/keystone/tests/unit/mapping_fixtures.py b/keystone-moon/keystone/tests/unit/mapping_fixtures.py index f86d9245..94b07133 100644 --- a/keystone-moon/keystone/tests/unit/mapping_fixtures.py +++ b/keystone-moon/keystone/tests/unit/mapping_fixtures.py @@ -901,6 +901,290 @@ MAPPING_GROUPS_WHITELIST_AND_BLACKLIST = { ] } +# Mapping used by tokenless test cases, it maps the user_name +# and domain_name. +MAPPING_WITH_USERNAME_AND_DOMAINNAME = { + 'rules': [ + { + 'local': [ + { + 'user': { + 'name': '{0}', + 'domain': { + 'name': '{1}' + }, + 'type': 'local' + } + } + ], + 'remote': [ + { + 'type': 'SSL_CLIENT_USER_NAME' + }, + { + 'type': 'SSL_CLIENT_DOMAIN_NAME' + } + ] + } + ] +} + +# Mapping used by tokenless test cases, it maps the user_id +# and domain_name. +MAPPING_WITH_USERID_AND_DOMAINNAME = { + 'rules': [ + { + 'local': [ + { + 'user': { + 'id': '{0}', + 'domain': { + 'name': '{1}' + }, + 'type': 'local' + } + } + ], + 'remote': [ + { + 'type': 'SSL_CLIENT_USER_ID' + }, + { + 'type': 'SSL_CLIENT_DOMAIN_NAME' + } + ] + } + ] +} + +# Mapping used by tokenless test cases, it maps the user_name +# and domain_id. +MAPPING_WITH_USERNAME_AND_DOMAINID = { + 'rules': [ + { + 'local': [ + { + 'user': { + 'name': '{0}', + 'domain': { + 'id': '{1}' + }, + 'type': 'local' + } + } + ], + 'remote': [ + { + 'type': 'SSL_CLIENT_USER_NAME' + }, + { + 'type': 'SSL_CLIENT_DOMAIN_ID' + } + ] + } + ] +} + +# Mapping used by tokenless test cases, it maps the user_id +# and domain_id. +MAPPING_WITH_USERID_AND_DOMAINID = { + 'rules': [ + { + 'local': [ + { + 'user': { + 'id': '{0}', + 'domain': { + 'id': '{1}' + }, + 'type': 'local' + } + } + ], + 'remote': [ + { + 'type': 'SSL_CLIENT_USER_ID' + }, + { + 'type': 'SSL_CLIENT_DOMAIN_ID' + } + ] + } + ] +} + +# Mapping used by tokenless test cases, it maps the domain_id only. +MAPPING_WITH_DOMAINID_ONLY = { + 'rules': [ + { + 'local': [ + { + 'user': { + 'domain': { + 'id': '{0}' + }, + 'type': 'local' + } + } + ], + 'remote': [ + { + 'type': 'SSL_CLIENT_DOMAIN_ID' + } + ] + } + ] +} + +# Mapping used by tokenless test cases, it maps the domain_name only. +MAPPING_WITH_DOMAINNAME_ONLY = { + 'rules': [ + { + 'local': [ + { + 'user': { + 'domain': { + 'name': '{0}' + }, + 'type': 'local' + } + } + ], + 'remote': [ + { + 'type': 'SSL_CLIENT_DOMAIN_NAME' + } + ] + } + ] +} + +# Mapping used by tokenless test cases, it maps the user_name only. +MAPPING_WITH_USERNAME_ONLY = { + 'rules': [ + { + 'local': [ + { + 'user': { + 'name': '{0}', + 'type': 'local' + } + } + ], + 'remote': [ + { + 'type': 'SSL_CLIENT_USER_NAME' + } + ] + } + ] +} + +# Mapping used by tokenless test cases, it maps the user_id only. +MAPPING_WITH_USERID_ONLY = { + 'rules': [ + { + 'local': [ + { + 'user': { + 'id': '{0}', + 'type': 'local' + } + } + ], + 'remote': [ + { + 'type': 'SSL_CLIENT_USER_ID' + } + ] + } + ] +} + +MAPPING_FOR_EPHEMERAL_USER = { + 'rules': [ + { + 'local': [ + { + 'user': { + 'name': '{0}', + 'type': 'ephemeral' + }, + 'group': { + 'id': 'dummy' + } + } + ], + 'remote': [ + { + 'type': 'SSL_CLIENT_USER_NAME' + } + ] + } + ] +} + +MAPPING_FOR_DEFAULT_EPHEMERAL_USER = { + 'rules': [ + { + 'local': [ + { + 'user': { + 'name': '{0}' + }, + 'group': { + 'id': 'dummy' + } + } + ], + 'remote': [ + { + 'type': 'SSL_CLIENT_USER_NAME' + } + ] + } + ] +} + +MAPPING_GROUPS_WHITELIST_PASS_THROUGH = { + "rules": [ + { + "remote": [ + { + "type": "UserName" + } + ], + "local": [ + { + "user": { + "name": "{0}", + "domain": { + "id": DEVELOPER_GROUP_DOMAIN_ID + } + } + } + ] + }, + { + "remote": [ + { + "type": "orgPersonType", + "whitelist": ['Developer'] + } + ], + "local": [ + { + "groups": "{0}", + "domain": { + "id": DEVELOPER_GROUP_DOMAIN_ID + } + } + ] + } + ] +} + + EMPLOYEE_ASSERTION = { 'Email': 'tim@example.com', 'UserName': 'tbo', diff --git a/keystone-moon/keystone/tests/unit/rest.py b/keystone-moon/keystone/tests/unit/rest.py index bfa52354..da24019f 100644 --- a/keystone-moon/keystone/tests/unit/rest.py +++ b/keystone-moon/keystone/tests/unit/rest.py @@ -13,15 +13,16 @@ # under the License. from oslo_serialization import jsonutils +from six.moves import http_client import webtest from keystone.auth import controllers as auth_controllers -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import default_fixtures from keystone.tests.unit.ksfixtures import database -class RestfulTestCase(tests.TestCase): +class RestfulTestCase(unit.TestCase): """Performs restful tests against the WSGI app over HTTP. This class launches public & admin WSGI servers for every test, which can @@ -113,7 +114,7 @@ class RestfulTestCase(tests.TestCase): example:: - self.assertResponseStatus(response, 204) + self.assertResponseStatus(response, http_client.NO_CONTENT) """ self.assertEqual( response.status_code, @@ -125,7 +126,8 @@ class RestfulTestCase(tests.TestCase): """Ensures that response headers appear as expected.""" self.assertIn('X-Auth-Token', response.headers.get('Vary')) - def assertValidErrorResponse(self, response, expected_status=400): + def assertValidErrorResponse(self, response, + expected_status=http_client.BAD_REQUEST): """Verify that the error response is valid. Subclasses can override this function based on the expected response. @@ -184,7 +186,8 @@ class RestfulTestCase(tests.TestCase): self._from_content_type(response, content_type=response_content_type) # we can save some code & improve coverage by always doing this - if method != 'HEAD' and response.status_code >= 400: + if (method != 'HEAD' and + response.status_code >= http_client.BAD_REQUEST): self.assertValidErrorResponse(response) # Contains the decoded response.body diff --git a/keystone-moon/keystone/tests/unit/test_associate_project_endpoint_extension.py b/keystone-moon/keystone/tests/unit/test_associate_project_endpoint_extension.py index 9cde704e..4c574549 100644 --- a/keystone-moon/keystone/tests/unit/test_associate_project_endpoint_extension.py +++ b/keystone-moon/keystone/tests/unit/test_associate_project_endpoint_extension.py @@ -15,6 +15,7 @@ import copy import uuid +from six.moves import http_client from testtools import matchers from keystone.tests.unit import test_v3 @@ -47,8 +48,7 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): Valid endpoint and project id test case. """ - self.put(self.default_request_url, - expected_status=204) + self.put(self.default_request_url) def test_create_endpoint_project_association_with_invalid_project(self): """PUT OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id} @@ -60,7 +60,7 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): '/endpoints/%(endpoint_id)s' % { 'project_id': uuid.uuid4().hex, 'endpoint_id': self.endpoint_id}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_create_endpoint_project_association_with_invalid_endpoint(self): """PUT /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id} @@ -72,7 +72,7 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): '/endpoints/%(endpoint_id)s' % { 'project_id': self.default_domain_project_id, 'endpoint_id': uuid.uuid4().hex}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_create_endpoint_project_association_with_unexpected_body(self): """PUT /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id} @@ -81,8 +81,7 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): """ self.put(self.default_request_url, - body={'project_id': self.default_domain_project_id}, - expected_status=204) + body={'project_id': self.default_domain_project_id}) def test_check_endpoint_project_association(self): """HEAD /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id} @@ -90,13 +89,11 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): Valid project and endpoint id test case. """ - self.put(self.default_request_url, - expected_status=204) + self.put(self.default_request_url) self.head('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.default_domain_project_id, - 'endpoint_id': self.endpoint_id}, - expected_status=204) + 'endpoint_id': self.endpoint_id}) def test_check_endpoint_project_association_with_invalid_project(self): """HEAD /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id} @@ -109,7 +106,7 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): '/endpoints/%(endpoint_id)s' % { 'project_id': uuid.uuid4().hex, 'endpoint_id': self.endpoint_id}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_check_endpoint_project_association_with_invalid_endpoint(self): """HEAD /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id} @@ -122,7 +119,7 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): '/endpoints/%(endpoint_id)s' % { 'project_id': self.default_domain_project_id, 'endpoint_id': uuid.uuid4().hex}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_list_endpoints_associated_with_valid_project(self): """GET /OS-EP-FILTER/projects/{project_id}/endpoints @@ -146,7 +143,7 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): self.put(self.default_request_url) self.get('/OS-EP-FILTER/projects/%(project_id)s/endpoints' % { 'project_id': uuid.uuid4().hex}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_list_projects_associated_with_endpoint(self): """GET /OS-EP-FILTER/endpoints/{endpoint_id}/projects @@ -168,8 +165,7 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): """ r = self.get('/OS-EP-FILTER/endpoints/%(endpoint_id)s/projects' % - {'endpoint_id': self.endpoint_id}, - expected_status=200) + {'endpoint_id': self.endpoint_id}) self.assertValidProjectListResponse(r, expected_length=0) def test_list_projects_associated_with_invalid_endpoint(self): @@ -180,7 +176,7 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): """ self.get('/OS-EP-FILTER/endpoints/%(endpoint_id)s/projects' % {'endpoint_id': uuid.uuid4().hex}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_remove_endpoint_project_association(self): """DELETE /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id} @@ -192,8 +188,7 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): self.delete('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.default_domain_project_id, - 'endpoint_id': self.endpoint_id}, - expected_status=204) + 'endpoint_id': self.endpoint_id}) def test_remove_endpoint_project_association_with_invalid_project(self): """DELETE /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id} @@ -206,7 +201,7 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): '/endpoints/%(endpoint_id)s' % { 'project_id': uuid.uuid4().hex, 'endpoint_id': self.endpoint_id}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_remove_endpoint_project_association_with_invalid_endpoint(self): """DELETE /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id} @@ -219,32 +214,32 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): '/endpoints/%(endpoint_id)s' % { 'project_id': self.default_domain_project_id, 'endpoint_id': uuid.uuid4().hex}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_endpoint_project_association_cleanup_when_project_deleted(self): self.put(self.default_request_url) association_url = ('/OS-EP-FILTER/endpoints/%(endpoint_id)s/projects' % {'endpoint_id': self.endpoint_id}) - r = self.get(association_url, expected_status=200) + r = self.get(association_url) self.assertValidProjectListResponse(r, expected_length=1) self.delete('/projects/%(project_id)s' % { 'project_id': self.default_domain_project_id}) - r = self.get(association_url, expected_status=200) + r = self.get(association_url) self.assertValidProjectListResponse(r, expected_length=0) def test_endpoint_project_association_cleanup_when_endpoint_deleted(self): self.put(self.default_request_url) association_url = '/OS-EP-FILTER/projects/%(project_id)s/endpoints' % { 'project_id': self.default_domain_project_id} - r = self.get(association_url, expected_status=200) + r = self.get(association_url) self.assertValidEndpointListResponse(r, expected_length=1) self.delete('/endpoints/%(endpoint_id)s' % { 'endpoint_id': self.endpoint_id}) - r = self.get(association_url, expected_status=200) + r = self.get(association_url) self.assertValidEndpointListResponse(r, expected_length=0) @@ -275,8 +270,7 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): self.put('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': project['id'], - 'endpoint_id': self.endpoint_id}, - expected_status=204) + 'endpoint_id': self.endpoint_id}) # attempt to authenticate without requesting a project auth_data = self.build_authentication_request( @@ -296,8 +290,7 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): self.put('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.project['id'], - 'endpoint_id': self.endpoint_id}, - expected_status=204) + 'endpoint_id': self.endpoint_id}) auth_data = self.build_authentication_request( user_id=self.user['id'], @@ -317,8 +310,7 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): self.put('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.project['id'], - 'endpoint_id': self.endpoint_id}, - expected_status=204) + 'endpoint_id': self.endpoint_id}) auth_data = self.build_authentication_request( user_id=self.user['id'], @@ -337,8 +329,7 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): self.put('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.project['id'], - 'endpoint_id': self.endpoint_id}, - expected_status=204) + 'endpoint_id': self.endpoint_id}) # create a second temporary endpoint self.endpoint_id2 = uuid.uuid4().hex @@ -352,8 +343,7 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): self.put('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.project['id'], - 'endpoint_id': self.endpoint_id2}, - expected_status=204) + 'endpoint_id': self.endpoint_id2}) # remove the temporary reference # this will create inconsistency in the endpoint filter table @@ -379,8 +369,7 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): self.put('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.project['id'], - 'endpoint_id': self.endpoint_id}, - expected_status=204) + 'endpoint_id': self.endpoint_id}) # Add a disabled endpoint to the default project. @@ -398,8 +387,7 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): self.put('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.project['id'], - 'endpoint_id': disabled_endpoint_id}, - expected_status=204) + 'endpoint_id': disabled_endpoint_id}) # Authenticate to get token with catalog auth_data = self.build_authentication_request( @@ -428,13 +416,11 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): self.put('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.project['id'], - 'endpoint_id': endpoint_id1}, - expected_status=204) + 'endpoint_id': endpoint_id1}) self.put('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.project['id'], - 'endpoint_id': endpoint_id2}, - expected_status=204) + 'endpoint_id': endpoint_id2}) # there should be only two endpoints in token catalog auth_data = self.build_authentication_request( @@ -453,8 +439,7 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): self.put('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.project['id'], - 'endpoint_id': self.endpoint_id}, - expected_status=204) + 'endpoint_id': self.endpoint_id}) auth_data = self.build_authentication_request( user_id=self.user['id'], @@ -589,7 +574,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): invalid_body['endpoint_group']['filters'] = {'foobar': 'admin'} self.post(self.DEFAULT_ENDPOINT_GROUP_URL, body=invalid_body, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_get_endpoint_group(self): """GET /OS-EP-FILTER/endpoint_groups/{endpoint_group} @@ -624,7 +609,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): endpoint_group_id = 'foobar' url = '/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s' % { 'endpoint_group_id': endpoint_group_id} - self.get(url, expected_status=404) + self.get(url, expected_status=http_client.NOT_FOUND) def test_check_endpoint_group(self): """HEAD /OS-EP-FILTER/endpoint_groups/{endpoint_group_id} @@ -637,7 +622,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): self.DEFAULT_ENDPOINT_GROUP_URL, self.DEFAULT_ENDPOINT_GROUP_BODY) url = '/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s' % { 'endpoint_group_id': endpoint_group_id} - self.head(url, expected_status=200) + self.head(url, expected_status=http_client.OK) def test_check_invalid_endpoint_group(self): """HEAD /OS-EP-FILTER/endpoint_groups/{endpoint_group_id} @@ -648,7 +633,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): endpoint_group_id = 'foobar' url = '/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s' % { 'endpoint_group_id': endpoint_group_id} - self.head(url, expected_status=404) + self.head(url, expected_status=http_client.NOT_FOUND) def test_patch_endpoint_group(self): """PATCH /OS-EP-FILTER/endpoint_groups/{endpoint_group} @@ -685,7 +670,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): } url = '/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s' % { 'endpoint_group_id': 'ABC'} - self.patch(url, body=body, expected_status=404) + self.patch(url, body=body, expected_status=http_client.NOT_FOUND) def test_patch_invalid_endpoint_group(self): """PATCH /OS-EP-FILTER/endpoint_groups/{endpoint_group} @@ -707,7 +692,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): self.DEFAULT_ENDPOINT_GROUP_URL, self.DEFAULT_ENDPOINT_GROUP_BODY) url = '/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s' % { 'endpoint_group_id': endpoint_group_id} - self.patch(url, body=body, expected_status=400) + self.patch(url, body=body, expected_status=http_client.BAD_REQUEST) # Perform a GET call to ensure that the content remains # the same (as DEFAULT_ENDPOINT_GROUP_BODY) after attempting to update @@ -731,7 +716,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): url = '/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s' % { 'endpoint_group_id': endpoint_group_id} self.delete(url) - self.get(url, expected_status=404) + self.get(url, expected_status=http_client.NOT_FOUND) def test_delete_invalid_endpoint_group(self): """GET /OS-EP-FILTER/endpoint_groups/{endpoint_group} @@ -742,7 +727,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): endpoint_group_id = 'foobar' url = '/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s' % { 'endpoint_group_id': endpoint_group_id} - self.delete(url, expected_status=404) + self.delete(url, expected_status=http_client.NOT_FOUND) def test_add_endpoint_group_to_project(self): """Create a valid endpoint group and project association.""" @@ -761,7 +746,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): project_id = uuid.uuid4().hex url = self._get_project_endpoint_group_url( endpoint_group_id, project_id) - self.put(url, expected_status=404) + self.put(url, expected_status=http_client.NOT_FOUND) def test_get_endpoint_group_in_project(self): """Test retrieving project endpoint group association.""" @@ -787,7 +772,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): project_id = uuid.uuid4().hex url = self._get_project_endpoint_group_url( endpoint_group_id, project_id) - self.get(url, expected_status=404) + self.get(url, expected_status=http_client.NOT_FOUND) def test_list_endpoint_groups_in_project(self): """GET /OS-EP-FILTER/projects/{project_id}/endpoint_groups.""" @@ -813,7 +798,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): project_id = uuid.uuid4().hex url = ('/OS-EP-FILTER/projects/%(project_id)s/endpoint_groups' % {'project_id': project_id}) - self.get(url, expected_status=404) + self.get(url, expected_status=http_client.NOT_FOUND) def test_empty_endpoint_groups_in_project(self): """Test when no endpoint groups associated with the project.""" @@ -831,7 +816,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): self.project_id) url = self._get_project_endpoint_group_url( endpoint_group_id, self.project_id) - self.head(url, expected_status=200) + self.head(url, expected_status=http_client.OK) def test_check_endpoint_group_to_project_with_invalid_project_id(self): """Test HEAD with an invalid endpoint group and project association.""" @@ -848,7 +833,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): project_id = uuid.uuid4().hex url = self._get_project_endpoint_group_url( endpoint_group_id, project_id) - self.head(url, expected_status=404) + self.head(url, expected_status=http_client.NOT_FOUND) def test_list_endpoint_groups(self): """GET /OS-EP-FILTER/endpoint_groups.""" @@ -992,7 +977,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): # endpoint group association again self.delete('/projects/%(project_id)s' % { 'project_id': project['id']}) - self.get(url, expected_status=404) + self.get(url, expected_status=http_client.NOT_FOUND) def test_endpoint_group_project_cleanup_with_endpoint_group(self): # create endpoint group @@ -1012,7 +997,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): # now remove the project endpoint group association self.delete(url) - self.get(url, expected_status=404) + self.get(url, expected_status=http_client.NOT_FOUND) def test_removing_an_endpoint_group_project(self): # create an endpoint group @@ -1026,7 +1011,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): # remove the endpoint group project self.delete(url) - self.get(url, expected_status=404) + self.get(url, expected_status=http_client.NOT_FOUND) def test_remove_endpoint_group_with_project_association(self): # create an endpoint group @@ -1044,8 +1029,9 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): '%(endpoint_group_id)s' % {'endpoint_group_id': endpoint_group_id}) self.delete(endpoint_group_url) - self.get(endpoint_group_url, expected_status=404) - self.get(project_endpoint_group_url, expected_status=404) + self.get(endpoint_group_url, expected_status=http_client.NOT_FOUND) + self.get(project_endpoint_group_url, + expected_status=http_client.NOT_FOUND) def _create_valid_endpoint_group(self, url, body): r = self.post(url, body=body) diff --git a/keystone-moon/keystone/tests/unit/test_auth.py b/keystone-moon/keystone/tests/unit/test_auth.py index f253b02d..6dd52c8a 100644 --- a/keystone-moon/keystone/tests/unit/test_auth.py +++ b/keystone-moon/keystone/tests/unit/test_auth.py @@ -29,7 +29,7 @@ from keystone.common import authorization from keystone import config from keystone import exception from keystone.models import token_model -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import default_fixtures from keystone.tests.unit.ksfixtures import database from keystone import token @@ -72,7 +72,7 @@ def _build_user_auth(token=None, user_id=None, username=None, return auth_json -class AuthTest(tests.TestCase): +class AuthTest(unit.TestCase): def setUp(self): self.useFixture(database.Database()) super(AuthTest, self).setUp() @@ -460,6 +460,37 @@ class AuthWithToken(AuthTest): dict(is_admin=True, query_string={}), token_id=token_id) + def test_deleting_role_assignment_does_not_revoke_unscoped_token(self): + no_context = {} + admin_context = dict(is_admin=True, query_string={}) + + project = { + 'id': uuid.uuid4().hex, + 'name': uuid.uuid4().hex, + 'domain_id': DEFAULT_DOMAIN_ID} + self.resource_api.create_project(project['id'], project) + role = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex} + self.role_api.create_role(role['id'], role) + self.assignment_api.add_role_to_user_and_project( + self.user_foo['id'], project['id'], role['id']) + + # Get an unscoped token. + token = self.controller.authenticate(no_context, _build_user_auth( + username=self.user_foo['name'], + password=self.user_foo['password'])) + token_id = token['access']['token']['id'] + + # Ensure it is valid + self.controller.validate_token(admin_context, token_id=token_id) + + # Delete the role assignment, which should not invalidate the token, + # because we're not consuming it with just an unscoped token. + self.assignment_api.remove_role_from_user_and_project( + self.user_foo['id'], project['id'], role['id']) + + # Ensure it is still valid + self.controller.validate_token(admin_context, token_id=token_id) + def test_only_original_audit_id_is_kept(self): context = {} @@ -1285,14 +1316,14 @@ class TokenExpirationTest(AuthTest): self._maintain_token_expiration() -class AuthCatalog(tests.SQLDriverOverrides, AuthTest): +class AuthCatalog(unit.SQLDriverOverrides, AuthTest): """Tests for the catalog provided in the auth response.""" def config_files(self): config_files = super(AuthCatalog, self).config_files() # We need to use a backend that supports disabled endpoints, like the # SQL backend. - config_files.append(tests.dirs.tests_conf('backend_sql.conf')) + config_files.append(unit.dirs.tests_conf('backend_sql.conf')) return config_files def _create_endpoints(self): @@ -1402,7 +1433,7 @@ class AuthCatalog(tests.SQLDriverOverrides, AuthTest): self.assertEqual(exp_endpoint, endpoint) -class NonDefaultAuthTest(tests.TestCase): +class NonDefaultAuthTest(unit.TestCase): def test_add_non_default_auth_method(self): self.config_fixture.config(group='auth', diff --git a/keystone-moon/keystone/tests/unit/test_auth_plugin.py b/keystone-moon/keystone/tests/unit/test_auth_plugin.py index a259cc2a..8dd22aa8 100644 --- a/keystone-moon/keystone/tests/unit/test_auth_plugin.py +++ b/keystone-moon/keystone/tests/unit/test_auth_plugin.py @@ -18,7 +18,7 @@ import mock from keystone import auth from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit # for testing purposes only @@ -37,7 +37,7 @@ class SimpleChallengeResponse(auth.AuthMethodHandler): return {"challenge": "What's the name of your high school?"} -class TestAuthPlugin(tests.SQLDriverOverrides, tests.TestCase): +class TestAuthPlugin(unit.SQLDriverOverrides, unit.TestCase): def setUp(self): super(TestAuthPlugin, self).setUp() self.load_backends() @@ -121,11 +121,11 @@ class TestAuthPluginDynamicOptions(TestAuthPlugin): def config_files(self): config_files = super(TestAuthPluginDynamicOptions, self).config_files() - config_files.append(tests.dirs.tests_conf('test_auth_plugin.conf')) + config_files.append(unit.dirs.tests_conf('test_auth_plugin.conf')) return config_files -class TestMapped(tests.TestCase): +class TestMapped(unit.TestCase): def setUp(self): super(TestMapped, self).setUp() self.load_backends() @@ -134,7 +134,7 @@ class TestMapped(tests.TestCase): def config_files(self): config_files = super(TestMapped, self).config_files() - config_files.append(tests.dirs.tests_conf('test_auth_plugin.conf')) + config_files.append(unit.dirs.tests_conf('test_auth_plugin.conf')) return config_files def auth_plugin_config_override(self, methods=None, **method_classes): diff --git a/keystone-moon/keystone/tests/unit/test_backend.py b/keystone-moon/keystone/tests/unit/test_backend.py index 45b8e0b0..d3b51edd 100644 --- a/keystone-moon/keystone/tests/unit/test_backend.py +++ b/keystone-moon/keystone/tests/unit/test_backend.py @@ -28,7 +28,7 @@ from testtools import matchers from keystone.catalog import core from keystone.common import driver_hints from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import default_fixtures from keystone.tests.unit import filtering from keystone.tests.unit import utils as test_utils @@ -40,7 +40,379 @@ DEFAULT_DOMAIN_ID = CONF.identity.default_domain_id NULL_OBJECT = object() -class IdentityTests(object): +class AssignmentTestHelperMixin(object): + """Mixin class to aid testing of assignments. + + This class supports data driven test plans that enable: + + - Creation of initial entities, such as domains, users, groups, projects + and roles + - Creation of assignments referencing the above entities + - A set of input parameters and expected outputs to list_role_assignments + based on the above test data + + A test plan is a dict of the form: + + test_plan = { + entities: details and number of entities, + group_memberships: group-user entity memberships, + assignments: list of assignments to create, + tests: list of pairs of input params and expected outputs} + + An example test plan: + + test_plan = { + # First, create the entities required. Entities are specified by + # a dict with the key being the entity type and the value an + # entity specification which can be one of: + # + # - a simple number, e.g. {'users': 3} creates 3 users + # - a dict where more information regarding the contents of the entity + # is required, e.g. {'domains' : {'users : 3}} creates a domain + # with three users + # - a list of entity specifications if multiple are required + # + # The following creates a domain that contains a single user, group and + # project, as well as creating three roles. + + 'entities': {'domains': {'users': 1, 'groups': 1, 'projects': 1}, + 'roles': 3}, + + # If it is required that an existing domain be used for the new + # entities, then the id of that domain can be included in the + # domain dict. For example, if alternatively we wanted to add 3 users + # to the default domain, add a second domain containing 3 projects as + # well as 5 additional empty domains, the entities would be defined as: + # + # 'entities': {'domains': [{'id': DEFAULT_DOMAIN, 'users': 3}, + # {'projects': 3}, 5]}, + # + # A project hierarchy can be specified within the 'projects' section by + # nesting the 'project' key, for example to create a project with three + # sub-projects you would use: + + 'projects': {'project': 3} + + # A more complex hierarchy can also be defined, for example the + # following would define three projects each containing a + # sub-project, each of which contain a further three sub-projects. + + 'projects': [{'project': {'project': 3}}, + {'project': {'project': 3}}, + {'project': {'project': 3}}] + + # A list of groups and their members. In this case make users with + # index 0 and 1 members of group with index 0. Users and Groups are + # indexed in the order they appear in the 'entities' key above. + + 'group_memberships': [{'group': 0, 'users': [0, 1]}] + + # Next, create assignments between the entities, referencing the + # entities by index, i.e. 'user': 0 refers to user[0]. Entities are + # indexed in the order they appear in the 'entities' key above within + # their entity type. + + 'assignments': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}, + {'group': 0, 'role': 2, 'domain': 0}, + {'user': 0, 'role': 2, 'project': 0}], + + # Finally, define an array of tests where list_role_assignment() is + # called with the given input parameters and the results are then + # confirmed to be as given in 'results'. Again, all entities are + # referenced by index. + + 'tests': [ + {'params': {}, + 'results': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}, + {'group': 0, 'role': 2, 'domain': 0}, + {'user': 0, 'role': 2, 'project': 0}]}, + {'params': {'role': 2}, + 'results': [{'group': 0, 'role': 2, 'domain': 0}, + {'user': 0, 'role': 2, 'project': 0}]}] + + # The 'params' key also supports the 'effective' and + # 'inherited_to_projects' options to list_role_assignments.} + + """ + def _handle_project_spec(self, test_data, domain_id, project_spec, + parent_id=None): + """Handle the creation of a project or hierarchy of projects. + + project_spec may either be a count of the number of projects to + create, or it may be a list of the form: + + [{'project': project_spec}, {'project': project_spec}, ...] + + This method is called recursively to handle the creation of a + hierarchy of projects. + + """ + def _create_project(domain_id, parent_id): + new_project = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex, + 'domain_id': domain_id, 'parent_id': parent_id} + new_project = self.resource_api.create_project(new_project['id'], + new_project) + return new_project + + if isinstance(project_spec, list): + for this_spec in project_spec: + self._handle_project_spec( + test_data, domain_id, this_spec, parent_id=parent_id) + elif isinstance(project_spec, dict): + new_proj = _create_project(domain_id, parent_id) + test_data['projects'].append(new_proj) + self._handle_project_spec( + test_data, domain_id, project_spec['project'], + parent_id=new_proj['id']) + else: + for _ in range(project_spec): + test_data['projects'].append( + _create_project(domain_id, parent_id)) + + def _handle_domain_spec(self, test_data, domain_spec): + """Handle the creation of domains and their contents. + + domain_spec may either be a count of the number of empty domains to + create, a dict describing the domain contents, or a list of + domain_specs. + + In the case when a list is provided, this method calls itself + recursively to handle the list elements. + + This method will insert any entities created into test_data + + """ + def _create_domain(domain_id=None): + if domain_id is None: + new_domain = {'id': uuid.uuid4().hex, + 'name': uuid.uuid4().hex} + self.resource_api.create_domain(new_domain['id'], + new_domain) + return new_domain + else: + # The test plan specified an existing domain to use + return self.resource_api.get_domain(domain_id) + + def _create_entity_in_domain(entity_type, domain_id): + """Create a user or group entity in the domain.""" + + new_entity = {'name': uuid.uuid4().hex, 'domain_id': domain_id} + if entity_type == 'users': + new_entity = self.identity_api.create_user(new_entity) + elif entity_type == 'groups': + new_entity = self.identity_api.create_group(new_entity) + else: + # Must be a bad test plan + raise exception.NotImplemented() + return new_entity + + if isinstance(domain_spec, list): + for x in domain_spec: + self._handle_domain_spec(test_data, x) + elif isinstance(domain_spec, dict): + # If there is a domain ID specified, then use it + the_domain = _create_domain(domain_spec.get('id')) + test_data['domains'].append(the_domain) + for entity_type, value in domain_spec.items(): + if entity_type == 'id': + # We already used this above to determine whether to + # use and existing domain + continue + if entity_type == 'projects': + # If it's projects, we need to handle the potential + # specification of a project hierarchy + self._handle_project_spec( + test_data, the_domain['id'], value) + else: + # It's a count of number of entities + for _ in range(value): + test_data[entity_type].append( + _create_entity_in_domain( + entity_type, the_domain['id'])) + else: + for _ in range(domain_spec): + test_data['domains'].append(_create_domain()) + + def create_entities(self, entity_pattern): + """Create the entities specified in the test plan. + + Process the 'entities' key in the test plan, creating the requested + entities. Each created entity will be added to the array of entities + stored in the returned test_data object, e.g.: + + test_data['users'] = [user[0], user[1]....] + + """ + def _create_role(): + new_role = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex} + return self.role_api.create_role(new_role['id'], new_role) + + test_data = {} + for entity in ['users', 'groups', 'domains', 'projects', 'roles']: + test_data[entity] = [] + + # Create any domains requested and, if specified, any entities within + # those domains + if 'domains' in entity_pattern: + self._handle_domain_spec(test_data, entity_pattern['domains']) + + # Create any roles requested + if 'roles' in entity_pattern: + for _ in range(entity_pattern['roles']): + test_data['roles'].append(_create_role()) + + return test_data + + def _convert_entity_shorthand(self, key, shorthand_data, reference_data): + """Convert a shorthand entity description into a full ID reference. + + In test plan definitions, we allow a shorthand for referencing to an + entity of the form: + + 'user': 0 + + which is actually shorthand for: + + 'user_id': reference_data['users'][0]['id'] + + This method converts the shorthand version into the full reference. + + """ + expanded_key = '%s_id' % key + reference_index = '%ss' % key + index_value = ( + reference_data[reference_index][shorthand_data[key]]['id']) + return expanded_key, index_value + + def create_group_memberships(self, group_pattern, test_data): + """Create the group memberships specified in the test plan.""" + + for group_spec in group_pattern: + # Each membership specification is a dict of the form: + # + # {'group': 0, 'users': [list of user indexes]} + # + # Add all users in the list to the specified group, first + # converting from index to full entity ID. + group_value = test_data['groups'][group_spec['group']]['id'] + for user_index in group_spec['users']: + user_value = test_data['users'][user_index]['id'] + self.identity_api.add_user_to_group(user_value, group_value) + return test_data + + def create_assignments(self, assignment_pattern, test_data): + """Create the assignments specified in the test plan.""" + + # First store how many assignments are already in the system, + # so during the tests we can check the number of new assignments + # created. + test_data['initial_assignment_count'] = ( + len(self.assignment_api.list_role_assignments())) + + # Now create the new assignments in the test plan + for assignment in assignment_pattern: + # Each assignment is a dict of the form: + # + # { 'user': 0, 'project':1, 'role': 6} + # + # where the value of each item is the index into the array of + # entities created earlier. + # + # We process the assignment dict to create the args required to + # make the create_grant() call. + args = {} + for param in assignment: + if param == 'inherited_to_projects': + args[param] = assignment[param] + else: + # Turn 'entity : 0' into 'entity_id = ac6736ba873d' + # where entity in user, group, project or domain + key, value = self._convert_entity_shorthand( + param, assignment, test_data) + args[key] = value + self.assignment_api.create_grant(**args) + return test_data + + def execute_assignment_tests(self, test_plan, test_data): + """Execute the test plan, based on the created test_data.""" + + def check_results(expected, actual, param_arg_count): + if param_arg_count == 0: + # It was an unfiltered call, so default fixture assignments + # might be polluting our answer - so we take into account + # how many assignments there were before the test. + self.assertEqual( + len(expected) + test_data['initial_assignment_count'], + len(actual)) + else: + self.assertThat(actual, matchers.HasLength(len(expected))) + + for each_expected in expected: + expected_assignment = {} + for param in each_expected: + if param == 'inherited_to_projects': + expected_assignment[param] = each_expected[param] + elif param == 'indirect': + # We're expecting the result to contain an indirect + # dict with the details how the role came to be placed + # on this entity - so convert the key/value pairs of + # that dict into real entity references. + indirect_term = {} + for indirect_param in each_expected[param]: + key, value = self._convert_entity_shorthand( + indirect_param, each_expected[param], + test_data) + indirect_term[key] = value + expected_assignment[param] = indirect_term + else: + # Convert a simple shorthand entry into a full + # entity reference + key, value = self._convert_entity_shorthand( + param, each_expected, test_data) + expected_assignment[key] = value + self.assertIn(expected_assignment, actual) + + # Go through each test in the array, processing the input params, which + # we build into an args dict, and then call list_role_assignments. Then + # check the results against those specified in the test plan. + for test in test_plan.get('tests', []): + args = {} + for param in test['params']: + if param in ['effective', 'inherited']: + # Just pass the value into the args + args[param] = test['params'][param] + else: + # Turn 'entity : 0' into 'entity_id = ac6736ba873d' + # where entity in user, group, project or domain + key, value = self._convert_entity_shorthand( + param, test['params'], test_data) + args[key] = value + results = self.assignment_api.list_role_assignments(**args) + check_results(test['results'], results, len(args)) + + def execute_assignment_test_plan(self, test_plan): + """Create entities, assignments and execute the test plan. + + The standard method to call to create entities and assignments and + execute the tests as specified in the test_plan. The test_data + dict is returned so that, if required, the caller can execute + additional manual tests with the entities and assignments created. + + """ + test_data = self.create_entities(test_plan['entities']) + if 'group_memberships' in test_plan: + self.create_group_memberships(test_plan['group_memberships'], + test_data) + if 'assignments' in test_plan: + test_data = self.create_assignments(test_plan['assignments'], + test_data) + self.execute_assignment_tests(test_plan, test_data) + return test_data + + +class IdentityTests(AssignmentTestHelperMixin): def _get_domain_fixture(self): domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex} self.resource_api.create_domain(domain['id'], domain) @@ -227,7 +599,7 @@ class IdentityTests(object): self.user_foo.pop('password') self.assertDictEqual(user_ref, self.user_foo) - @tests.skip_if_cache_disabled('identity') + @unit.skip_if_cache_disabled('identity') def test_cache_layer_get_user(self): user = { 'name': uuid.uuid4().hex.lower(), @@ -279,7 +651,7 @@ class IdentityTests(object): self.user_foo.pop('password') self.assertDictEqual(user_ref, self.user_foo) - @tests.skip_if_cache_disabled('identity') + @unit.skip_if_cache_disabled('identity') def test_cache_layer_get_user_by_name(self): user = { 'name': uuid.uuid4().hex.lower(), @@ -506,168 +878,83 @@ class IdentityTests(object): 'fake2') def test_list_role_assignments_unfiltered(self): - """Test unfiltered listing of role assignments. - - Test Plan: - - - Create a domain, with a user, group & project - - Find how many role assignments already exist (from default - fixtures) - - Create a grant of each type (user/group on project/domain) - - Check the number of assignments has gone up by 4 and that - the entries we added are in the list returned - - Check that if we list assignments by role_id, then we get back - assignments that only contain that role. - - """ - new_domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex} - self.resource_api.create_domain(new_domain['id'], new_domain) - new_user = {'name': uuid.uuid4().hex, 'password': uuid.uuid4().hex, - 'enabled': True, 'domain_id': new_domain['id']} - new_user = self.identity_api.create_user(new_user) - new_group = {'domain_id': new_domain['id'], 'name': uuid.uuid4().hex} - new_group = self.identity_api.create_group(new_group) - new_project = {'id': uuid.uuid4().hex, - 'name': uuid.uuid4().hex, - 'domain_id': new_domain['id']} - self.resource_api.create_project(new_project['id'], new_project) - - # First check how many role grants already exist - existing_assignments = len(self.assignment_api.list_role_assignments()) - - # Now create the grants (roles are defined in default_fixtures) - self.assignment_api.create_grant(user_id=new_user['id'], - domain_id=new_domain['id'], - role_id='member') - self.assignment_api.create_grant(user_id=new_user['id'], - project_id=new_project['id'], - role_id='other') - self.assignment_api.create_grant(group_id=new_group['id'], - domain_id=new_domain['id'], - role_id='admin') - self.assignment_api.create_grant(group_id=new_group['id'], - project_id=new_project['id'], - role_id='admin') - - # Read back the full list of assignments - check it is gone up by 4 - assignment_list = self.assignment_api.list_role_assignments() - self.assertEqual(existing_assignments + 4, len(assignment_list)) - - # Now check that each of our four new entries are in the list - self.assertIn( - {'user_id': new_user['id'], 'domain_id': new_domain['id'], - 'role_id': 'member'}, - assignment_list) - self.assertIn( - {'user_id': new_user['id'], 'project_id': new_project['id'], - 'role_id': 'other'}, - assignment_list) - self.assertIn( - {'group_id': new_group['id'], 'domain_id': new_domain['id'], - 'role_id': 'admin'}, - assignment_list) - self.assertIn( - {'group_id': new_group['id'], 'project_id': new_project['id'], - 'role_id': 'admin'}, - assignment_list) + """Test unfiltered listing of role assignments.""" + + test_plan = { + # Create a domain, with a user, group & project + 'entities': {'domains': {'users': 1, 'groups': 1, 'projects': 1}, + 'roles': 3}, + # Create a grant of each type (user/group on project/domain) + 'assignments': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}, + {'group': 0, 'role': 2, 'domain': 0}, + {'group': 0, 'role': 2, 'project': 0}], + 'tests': [ + # Check that we get back the 4 assignments + {'params': {}, + 'results': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}, + {'group': 0, 'role': 2, 'domain': 0}, + {'group': 0, 'role': 2, 'project': 0}]} + ] + } + self.execute_assignment_test_plan(test_plan) def test_list_role_assignments_filtered_by_role(self): - """Test listing of role assignments filtered by role ID. - - Test Plan: - - - Create a user, group & project - - Find how many role assignments already exist (from default - fixtures) - - Create a grant of each type (user/group on project/domain) - - Check that if we list assignments by role_id, then we get back - assignments that only contain that role. - - """ - new_user = {'name': uuid.uuid4().hex, 'password': uuid.uuid4().hex, - 'enabled': True, 'domain_id': DEFAULT_DOMAIN_ID} - new_user = self.identity_api.create_user(new_user) - new_group = {'domain_id': DEFAULT_DOMAIN_ID, 'name': uuid.uuid4().hex} - new_group = self.identity_api.create_group(new_group) - new_project = {'id': uuid.uuid4().hex, - 'name': uuid.uuid4().hex, - 'domain_id': DEFAULT_DOMAIN_ID} - self.resource_api.create_project(new_project['id'], new_project) - - # First check how many role grants already exist - existing_assignments_for_role = len( - self.assignment_api.list_role_assignments_for_role( - role_id='admin')) - - # Now create the grants (roles are defined in default_fixtures) - self.assignment_api.create_grant(user_id=new_user['id'], - domain_id=DEFAULT_DOMAIN_ID, - role_id='member') - self.assignment_api.create_grant(user_id=new_user['id'], - project_id=new_project['id'], - role_id='other') - self.assignment_api.create_grant(group_id=new_group['id'], - domain_id=DEFAULT_DOMAIN_ID, - role_id='admin') - self.assignment_api.create_grant(group_id=new_group['id'], - project_id=new_project['id'], - role_id='admin') + """Test listing of role assignments filtered by role ID.""" + + test_plan = { + # Create a user, group & project in the default domain + 'entities': {'domains': {'id': DEFAULT_DOMAIN_ID, + 'users': 1, 'groups': 1, 'projects': 1}, + 'roles': 3}, + # Create a grant of each type (user/group on project/domain) + 'assignments': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}, + {'group': 0, 'role': 2, 'domain': 0}, + {'group': 0, 'role': 2, 'project': 0}], + 'tests': [ + # Check that when filtering by role, we only get back those + # that match + {'params': {'role': 2}, + 'results': [{'group': 0, 'role': 2, 'domain': 0}, + {'group': 0, 'role': 2, 'project': 0}]} + ] + } + test_data = self.execute_assignment_test_plan(test_plan) - # Read back the list of assignments for just the admin role, checking - # this only goes up by two. + # Also test that list_role_assignments_for_role() gives the same answer assignment_list = self.assignment_api.list_role_assignments_for_role( - role_id='admin') - self.assertEqual(existing_assignments_for_role + 2, - len(assignment_list)) + role_id=test_data['roles'][2]['id']) + self.assertThat(assignment_list, matchers.HasLength(2)) # Now check that each of our two new entries are in the list self.assertIn( - {'group_id': new_group['id'], 'domain_id': DEFAULT_DOMAIN_ID, - 'role_id': 'admin'}, + {'group_id': test_data['groups'][0]['id'], + 'domain_id': DEFAULT_DOMAIN_ID, + 'role_id': test_data['roles'][2]['id']}, assignment_list) self.assertIn( - {'group_id': new_group['id'], 'project_id': new_project['id'], - 'role_id': 'admin'}, + {'group_id': test_data['groups'][0]['id'], + 'project_id': test_data['projects'][0]['id'], + 'role_id': test_data['roles'][2]['id']}, assignment_list) def test_list_group_role_assignment(self): # When a group role assignment is created and the role assignments are # listed then the group role assignment is included in the list. - MEMBER_ROLE_ID = 'member' - - def get_member_assignments(): - assignments = self.assignment_api.list_role_assignments() - return [x for x in assignments if x['role_id'] == MEMBER_ROLE_ID] - - orig_member_assignments = get_member_assignments() - - # Create a group. - new_group = { - 'domain_id': DEFAULT_DOMAIN_ID, - 'name': self.getUniqueString(prefix='tlgra')} - new_group = self.identity_api.create_group(new_group) - - # Create a project. - new_project = { - 'id': uuid.uuid4().hex, - 'name': self.getUniqueString(prefix='tlgra'), - 'domain_id': DEFAULT_DOMAIN_ID} - self.resource_api.create_project(new_project['id'], new_project) - - # Assign a role to the group. - self.assignment_api.create_grant( - group_id=new_group['id'], project_id=new_project['id'], - role_id=MEMBER_ROLE_ID) - - # List role assignments - new_member_assignments = get_member_assignments() - - expected_member_assignments = orig_member_assignments + [{ - 'group_id': new_group['id'], 'project_id': new_project['id'], - 'role_id': MEMBER_ROLE_ID}] - self.assertItemsEqual(expected_member_assignments, - new_member_assignments) + test_plan = { + 'entities': {'domains': {'id': DEFAULT_DOMAIN_ID, + 'groups': 1, 'projects': 1}, + 'roles': 1}, + 'assignments': [{'group': 0, 'role': 0, 'project': 0}], + 'tests': [ + {'params': {}, + 'results': [{'group': 0, 'role': 0, 'project': 0}]} + ] + } + self.execute_assignment_test_plan(test_plan) def test_list_role_assignments_bad_role(self): assignment_list = self.assignment_api.list_role_assignments_for_role( @@ -1695,6 +1982,82 @@ class IdentityTests(object): self.identity_api.delete_group(group1['id']) self.identity_api.get_user(user1['id']) + def test_list_role_assignment_by_domain(self): + """Test listing of role assignment filtered by domain.""" + + test_plan = { + # A domain with 3 users, 1 group, a spoiler domain and 2 roles. + 'entities': {'domains': [{'users': 3, 'groups': 1}, 1], + 'roles': 2}, + # Users 1 & 2 are in the group + 'group_memberships': [{'group': 0, 'users': [1, 2]}], + # Assign a role for user 0 and the group + 'assignments': [{'user': 0, 'role': 0, 'domain': 0}, + {'group': 0, 'role': 1, 'domain': 0}], + 'tests': [ + # List all effective assignments for domain[0]. + # Should get one direct user role and user roles for each of + # the users in the group. + {'params': {'domain': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 1, 'role': 1, 'domain': 0, + 'indirect': {'group': 0}}, + {'user': 2, 'role': 1, 'domain': 0, + 'indirect': {'group': 0}} + ]}, + # Using domain[1] should return nothing + {'params': {'domain': 1, 'effective': True}, + 'results': []}, + ] + } + self.execute_assignment_test_plan(test_plan) + + def test_list_role_assignment_by_user_with_domain_group_roles(self): + """Test listing assignments by user, with group roles on a domain.""" + + test_plan = { + # A domain with 3 users, 3 groups, a spoiler domain + # plus 3 roles. + 'entities': {'domains': [{'users': 3, 'groups': 3}, 1], + 'roles': 3}, + # Users 1 & 2 are in the group 0, User 1 also in group 1 + 'group_memberships': [{'group': 0, 'users': [0, 1]}, + {'group': 1, 'users': [0]}], + 'assignments': [{'user': 0, 'role': 0, 'domain': 0}, + {'group': 0, 'role': 1, 'domain': 0}, + {'group': 1, 'role': 2, 'domain': 0}, + # ...and two spoiler assignments + {'user': 1, 'role': 1, 'domain': 0}, + {'group': 2, 'role': 2, 'domain': 0}], + 'tests': [ + # List all effective assignments for user[0]. + # Should get one direct user role and a user roles for each of + # groups 0 and 1 + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'domain': 0, + 'indirect': {'group': 0}}, + {'user': 0, 'role': 2, 'domain': 0, + 'indirect': {'group': 1}} + ]}, + # Adding domain[0] as a filter should return the same data + {'params': {'user': 0, 'domain': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'domain': 0, + 'indirect': {'group': 0}}, + {'user': 0, 'role': 2, 'domain': 0, + 'indirect': {'group': 1}} + ]}, + # Using domain[1] should return nothing + {'params': {'user': 0, 'domain': 1, 'effective': True}, + 'results': []}, + # Using user[2] should return nothing + {'params': {'user': 2, 'domain': 0, 'effective': True}, + 'results': []}, + ] + } + self.execute_assignment_test_plan(test_plan) + def test_delete_domain_with_user_group_project_links(self): # TODO(chungg):add test case once expected behaviour defined pass @@ -2162,7 +2525,7 @@ class IdentityTests(object): self.assertIn(self.tenant_mtu['id'], project_ids) self.assertIn(self.tenant_service['id'], project_ids) - @tests.skip_if_no_multiple_domains_support + @unit.skip_if_no_multiple_domains_support def test_list_projects_for_alternate_domain(self): domain1 = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex} self.resource_api.create_domain(domain1['id'], domain1) @@ -2219,7 +2582,7 @@ class IdentityTests(object): return projects - @tests.skip_if_no_multiple_domains_support + @unit.skip_if_no_multiple_domains_support def test_create_domain_with_project_api(self): project_id = uuid.uuid4().hex project = {'id': project_id, @@ -2233,7 +2596,7 @@ class IdentityTests(object): self.assertTrue(ref['is_domain']) self.assertEqual(DEFAULT_DOMAIN_ID, ref['domain_id']) - @tests.skip_if_no_multiple_domains_support + @unit.skip_if_no_multiple_domains_support @test_utils.wip('waiting for projects acting as domains implementation') def test_is_domain_sub_project_has_parent_domain_id(self): project = {'id': uuid.uuid4().hex, @@ -2258,7 +2621,7 @@ class IdentityTests(object): self.assertEqual(project['id'], ref['parent_id']) self.assertEqual(project['id'], ref['domain_id']) - @tests.skip_if_no_multiple_domains_support + @unit.skip_if_no_multiple_domains_support @test_utils.wip('waiting for projects acting as domains implementation') def test_delete_domain_with_project_api(self): project_id = uuid.uuid4().hex @@ -2283,7 +2646,7 @@ class IdentityTests(object): # Successfuly delete the project self.resource_api.delete_project(project['id']) - @tests.skip_if_no_multiple_domains_support + @unit.skip_if_no_multiple_domains_support @test_utils.wip('waiting for projects acting as domains implementation') def test_create_domain_under_regular_project_hierarchy_fails(self): # Creating a regular project hierarchy. Projects acting as domains @@ -2303,7 +2666,7 @@ class IdentityTests(object): self.resource_api.create_project, project['id'], project) - @tests.skip_if_no_multiple_domains_support + @unit.skip_if_no_multiple_domains_support @test_utils.wip('waiting for projects acting as domains implementation') def test_create_project_under_domain_hierarchy(self): projects_hierarchy = self._create_projects_hierarchy(is_domain=True) @@ -2917,7 +3280,7 @@ class IdentityTests(object): uuid.uuid4().hex, DEFAULT_DOMAIN_ID) - @tests.skip_if_cache_disabled('identity') + @unit.skip_if_cache_disabled('identity') def test_cache_layer_group_crud(self): group = {'domain_id': DEFAULT_DOMAIN_ID, 'name': uuid.uuid4().hex} group = self.identity_api.create_group(group) @@ -2992,7 +3355,7 @@ class IdentityTests(object): group1['id'], group1) - @tests.skip_if_no_multiple_domains_support + @unit.skip_if_no_multiple_domains_support def test_project_crud(self): domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex, 'enabled': True} @@ -3093,7 +3456,7 @@ class IdentityTests(object): project['id'], project) - @tests.skip_if_no_multiple_domains_support + @unit.skip_if_no_multiple_domains_support def test_create_leaf_project_with_different_domain(self): root_project = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex, @@ -3308,7 +3671,7 @@ class IdentityTests(object): self.resource_api.get_domain, domain['id']) - @tests.skip_if_no_multiple_domains_support + @unit.skip_if_no_multiple_domains_support def test_create_domain_case_sensitivity(self): # create a ref with a lowercase name ref = { @@ -3443,8 +3806,8 @@ class IdentityTests(object): user_projects = self.assignment_api.list_projects_for_user(user1['id']) self.assertEqual(3, len(user_projects)) - @tests.skip_if_cache_disabled('resource') - @tests.skip_if_no_multiple_domains_support + @unit.skip_if_cache_disabled('resource') + @unit.skip_if_no_multiple_domains_support def test_domain_rename_invalidates_get_domain_by_name_cache(self): domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex, 'enabled': True} @@ -3458,7 +3821,7 @@ class IdentityTests(object): self.resource_api.get_domain_by_name, domain_name) - @tests.skip_if_cache_disabled('resource') + @unit.skip_if_cache_disabled('resource') def test_cache_layer_domain_crud(self): domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex, 'enabled': True} @@ -3513,8 +3876,8 @@ class IdentityTests(object): self.resource_api.get_domain, domain_id) - @tests.skip_if_cache_disabled('resource') - @tests.skip_if_no_multiple_domains_support + @unit.skip_if_cache_disabled('resource') + @unit.skip_if_no_multiple_domains_support def test_project_rename_invalidates_get_project_by_name_cache(self): domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex, 'enabled': True} @@ -3533,8 +3896,8 @@ class IdentityTests(object): project_name, domain['id']) - @tests.skip_if_cache_disabled('resource') - @tests.skip_if_no_multiple_domains_support + @unit.skip_if_cache_disabled('resource') + @unit.skip_if_no_multiple_domains_support def test_cache_layer_project_crud(self): domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex, 'enabled': True} @@ -3646,7 +4009,7 @@ class IdentityTests(object): group_id=uuid.uuid4().hex, project_id=self.tenant_bar['id']) - @tests.skip_if_no_multiple_domains_support + @unit.skip_if_no_multiple_domains_support def test_get_default_domain_by_name(self): domain_name = 'default' @@ -4308,7 +4671,7 @@ class TokenTests(object): def test_list_revoked_tokens_for_multiple_tokens(self): self.check_list_revoked_tokens([self.delete_token() - for x in six.moves.range(2)]) + for x in range(2)]) def test_flush_expired_token(self): token_id = uuid.uuid4().hex @@ -4339,7 +4702,7 @@ class TokenTests(object): self.assertEqual(1, len(tokens)) self.assertIn(token_id, tokens) - @tests.skip_if_cache_disabled('token') + @unit.skip_if_cache_disabled('token') def test_revocation_list_cache(self): expire_time = timeutils.utcnow() + datetime.timedelta(minutes=10) token_id = uuid.uuid4().hex @@ -4769,7 +5132,7 @@ class CatalogTests(object): for region in regions: self.assertEqual(parent_id, region['parent_region_id']) - @tests.skip_if_cache_disabled('catalog') + @unit.skip_if_cache_disabled('catalog') def test_cache_layer_region_crud(self): region_id = uuid.uuid4().hex new_region = { @@ -4797,7 +5160,7 @@ class CatalogTests(object): self.assertRaises(exception.RegionNotFound, self.catalog_api.get_region, region_id) - @tests.skip_if_cache_disabled('catalog') + @unit.skip_if_cache_disabled('catalog') def test_invalidate_cache_when_updating_region(self): region_id = uuid.uuid4().hex new_region = { @@ -4875,7 +5238,7 @@ class CatalogTests(object): region_two['id'], {'parent_region_id': region_four['id']}) - @mock.patch.object(core.Driver, + @mock.patch.object(core.CatalogDriverV8, "_ensure_no_circle_in_hierarchical_regions") def test_circular_regions_can_be_deleted(self, mock_ensure_on_circle): # turn off the enforcement so that cycles can be created for the test @@ -5003,7 +5366,7 @@ class CatalogTests(object): self.catalog_api.delete_service(unrelated_service1['id']) self.catalog_api.delete_service(unrelated_service2['id']) - @tests.skip_if_cache_disabled('catalog') + @unit.skip_if_cache_disabled('catalog') def test_cache_layer_service_crud(self): service_id = uuid.uuid4().hex new_service = { @@ -5040,7 +5403,7 @@ class CatalogTests(object): self.catalog_api.get_service, service_id) - @tests.skip_if_cache_disabled('catalog') + @unit.skip_if_cache_disabled('catalog') def test_invalidate_cache_when_updating_service(self): service_id = uuid.uuid4().hex new_service = { @@ -5351,7 +5714,7 @@ class CatalogTests(object): endpoint_ids = [x['id'] for x in catalog[0]['endpoints']] self.assertEqual([enabled_endpoint_ref['id']], endpoint_ids) - @tests.skip_if_cache_disabled('catalog') + @unit.skip_if_cache_disabled('catalog') def test_invalidate_cache_when_updating_endpoint(self): service = { 'id': uuid.uuid4().hex, @@ -5468,7 +5831,71 @@ class PolicyTests(object): uuid.uuid4().hex) -class InheritanceTests(object): +class InheritanceTests(AssignmentTestHelperMixin): + + def test_role_assignments_user_domain_to_project_inheritance(self): + test_plan = { + 'entities': {'domains': {'users': 2, 'projects': 1}, + 'roles': 3}, + 'assignments': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}, + {'user': 0, 'role': 2, 'domain': 0, + 'inherited_to_projects': True}, + {'user': 1, 'role': 1, 'project': 0}], + 'tests': [ + # List all direct assignments for user[0] + {'params': {'user': 0}, + 'results': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}, + {'user': 0, 'role': 2, 'domain': 0, + 'inherited_to_projects': 'projects'}]}, + # Now the effective ones - so the domain role should turn into + # a project role + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}, + {'user': 0, 'role': 2, 'project': 0, + 'indirect': {'domain': 0}}]}, + # Narrow down to effective roles for user[0] and project[0] + {'params': {'user': 0, 'project': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 1, 'project': 0}, + {'user': 0, 'role': 2, 'project': 0, + 'indirect': {'domain': 0}}]} + ] + } + self.config_fixture.config(group='os_inherit', enabled=True) + self.execute_assignment_test_plan(test_plan) + + def test_inherited_role_assignments_excluded_if_os_inherit_false(self): + test_plan = { + 'entities': {'domains': {'users': 2, 'groups': 1, 'projects': 1}, + 'roles': 4}, + 'group_memberships': [{'group': 0, 'users': [0]}], + 'assignments': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}, + {'user': 0, 'role': 2, 'domain': 0, + 'inherited_to_projects': True}, + {'user': 1, 'role': 1, 'project': 0}, + {'group': 0, 'role': 3, 'project': 0}], + 'tests': [ + # List all direct assignments for user[0], since os-inherit is + # disabled, we should not see the inherited role + {'params': {'user': 0}, + 'results': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}]}, + # Same in effective mode - inherited roles should not be + # included or expanded...but the group role should now + # turn up as a user role, since group expansion is not + # part of os-inherit. + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}, + {'user': 0, 'role': 3, 'project': 0, + 'indirect': {'group': 0}}]}, + ] + } + self.config_fixture.config(group='os_inherit', enabled=False) + self.execute_assignment_test_plan(test_plan) def _test_crud_inherited_and_direct_assignment(self, **kwargs): """Tests inherited and direct assignments for the actor and target @@ -5616,6 +6043,42 @@ class InheritanceTests(object): self.assertEqual(1, len(combined_role_list)) self.assertIn(role_list[1]['id'], combined_role_list) + # TODO(henry-nash): The test above uses get_roles_for_user_and_project + # and get_roles_for_user_and_domain, which will, in a subsequent patch, + # be re-implemeted to simply call list_role_assignments (see blueprint + # remove-role-metadata). + # + # The test plan below therefore mirrors this test, to ensure that + # list_role_assignments works the same. Once get_roles_for_user_and + # project/domain have been re-implemented then the manual tests above + # can be refactored to simply ensure it gives the same answers. + test_plan = { + # A domain with a user & project, plus 3 roles. + 'entities': {'domains': {'users': 1, 'projects': 1}, + 'roles': 3}, + 'assignments': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 1, 'domain': 0}, + {'user': 0, 'role': 2, 'domain': 0, + 'inherited_to_projects': True}], + 'tests': [ + # List all effective assignments for user[0] on project[0]. + # Should get one direct role and one inherited role. + {'params': {'user': 0, 'project': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 2, 'project': 0, + 'indirect': {'domain': 0}}]}, + # Ensure effective mode on the domain does not list the + # inherited role on that domain + {'params': {'user': 0, 'domain': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 1, 'domain': 0}]}, + # Ensure non-inherited mode also only returns the non-inherited + # role on the domain + {'params': {'user': 0, 'domain': 0, 'inherited': False}, + 'results': [{'user': 0, 'role': 1, 'domain': 0}]}, + ] + } + self.execute_assignment_test_plan(test_plan) + def test_inherited_role_grants_for_group(self): """Test inherited group roles. @@ -5699,6 +6162,42 @@ class InheritanceTests(object): self.assertIn(role_list[2]['id'], combined_list) self.assertIn(role_list[3]['id'], combined_list) + # TODO(henry-nash): The test above uses get_roles_for_user_and_project + # which will, in a subsequent patch, be re-implemeted to simply call + # list_role_assignments (see blueprint remove-role-metadata). + # + # The test plan below therefore mirrors this test, to ensure that + # list_role_assignments works the same. Once + # get_roles_for_user_and_project has been re-implemented then the + # manual tests above can be refactored to simply ensure it gives + # the same answers. + test_plan = { + # A domain with a user and project, 2 groups, plus 4 roles. + 'entities': {'domains': {'users': 1, 'projects': 1, 'groups': 2}, + 'roles': 4}, + 'group_memberships': [{'group': 0, 'users': [0]}, + {'group': 1, 'users': [0]}], + 'assignments': [{'user': 0, 'role': 0, 'project': 0}, + {'group': 0, 'role': 1, 'domain': 0}, + {'group': 1, 'role': 2, 'domain': 0, + 'inherited_to_projects': True}, + {'group': 1, 'role': 3, 'domain': 0, + 'inherited_to_projects': True}], + 'tests': [ + # List all effective assignments for user[0] on project[0]. + # Should get one direct role and both inherited roles, but + # not the direct one on domain[0], even though user[0] is + # in group[0]. + {'params': {'user': 0, 'project': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 2, 'project': 0, + 'indirect': {'domain': 0, 'group': 1}}, + {'user': 0, 'role': 3, 'project': 0, + 'indirect': {'domain': 0, 'group': 1}}]} + ] + } + self.execute_assignment_test_plan(test_plan) + def test_list_projects_for_user_with_inherited_grants(self): """Test inherited user roles. @@ -5738,6 +6237,37 @@ class InheritanceTests(object): user_projects = self.assignment_api.list_projects_for_user(user1['id']) self.assertEqual(3, len(user_projects)) + # TODO(henry-nash): The test above uses list_projects_for_user + # which may, in a subsequent patch, be re-implemeted to call + # list_role_assignments and then report only the distinct projects. + # + # The test plan below therefore mirrors this test, to ensure that + # list_role_assignments works the same. Once list_projects_for_user + # has been re-implemented then the manual tests above can be + # refactored. + test_plan = { + # A domain with 1 project, plus a second domain with 2 projects, + # as well as a user. Also, create 2 roles. + 'entities': {'domains': [{'projects': 1}, + {'users': 1, 'projects': 2}], + 'roles': 2}, + 'assignments': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 1, 'domain': 1, + 'inherited_to_projects': True}], + 'tests': [ + # List all effective assignments for user[0] + # Should get one direct role plus one inherited role for each + # project in domain + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 1, 'project': 1, + 'indirect': {'domain': 1}}, + {'user': 0, 'role': 1, 'project': 2, + 'indirect': {'domain': 1}}]} + ] + } + self.execute_assignment_test_plan(test_plan) + def test_list_projects_for_user_with_inherited_user_project_grants(self): """Test inherited role assignments for users on nested projects. @@ -5798,6 +6328,50 @@ class InheritanceTests(object): self.assertEqual(1, len(user_projects)) self.assertIn(root_project, user_projects) + # TODO(henry-nash): The test above uses list_projects_for_user + # which may, in a subsequent patch, be re-implemeted to call + # list_role_assignments and then report only the distinct projects. + # + # The test plan below therefore mirrors this test, to ensure that + # list_role_assignments works the same. Once list_projects_for_user + # has been re-implemented then the manual tests above can be + # refactored. + test_plan = { + # A domain with a project and sub-project, plus a user. + # Also, create 2 roles. + 'entities': { + 'domains': {'id': DEFAULT_DOMAIN_ID, 'users': 1, + 'projects': {'project': 1}}, + 'roles': 2}, + # A direct role and an inherited role on the parent + 'assignments': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 1, 'project': 0, + 'inherited_to_projects': True}], + 'tests': [ + # List all effective assignments for user[0] - should get back + # one direct role plus one inherited role. + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 1, 'project': 1, + 'indirect': {'project': 0}}]} + ] + } + + test_plan_with_os_inherit_disabled = { + 'tests': [ + # List all effective assignments for user[0] - should only get + # back the one direct role. + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 0}]} + ] + } + self.config_fixture.config(group='os_inherit', enabled=True) + test_data = self.execute_assignment_test_plan(test_plan) + self.config_fixture.config(group='os_inherit', enabled=False) + # Pass the existing test data in to allow execution of 2nd test plan + self.execute_assignment_tests( + test_plan_with_os_inherit_disabled, test_data) + def test_list_projects_for_user_with_inherited_group_grants(self): """Test inherited group roles. @@ -5862,6 +6436,48 @@ class InheritanceTests(object): user_projects = self.assignment_api.list_projects_for_user(user1['id']) self.assertEqual(5, len(user_projects)) + # TODO(henry-nash): The test above uses list_projects_for_user + # which may, in a subsequent patch, be re-implemeted to call + # list_role_assignments and then report only the distinct projects. + # + # The test plan below therefore mirrors this test, to ensure that + # list_role_assignments works the same. Once list_projects_for_user + # has been re-implemented then the manual tests above can be + # refactored. + test_plan = { + # A domain with a 1 project, plus a second domain with 2 projects, + # as well as a user & group and a 3rd domain with 2 projects. + # Also, created 2 roles. + 'entities': {'domains': [{'projects': 1}, + {'users': 1, 'groups': 1, 'projects': 2}, + {'projects': 2}], + 'roles': 2}, + 'group_memberships': [{'group': 0, 'users': [0]}], + 'assignments': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 0, 'project': 3}, + {'user': 0, 'role': 1, 'domain': 1, + 'inherited_to_projects': True}, + {'user': 0, 'role': 1, 'domain': 2, + 'inherited_to_projects': True}], + 'tests': [ + # List all effective assignments for user[0] + # Should get back both direct roles plus roles on both projects + # from each domain. Duplicates should not be fitered out. + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 3}, + {'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 1, 'project': 1, + 'indirect': {'domain': 1}}, + {'user': 0, 'role': 1, 'project': 2, + 'indirect': {'domain': 1}}, + {'user': 0, 'role': 1, 'project': 3, + 'indirect': {'domain': 2}}, + {'user': 0, 'role': 1, 'project': 4, + 'indirect': {'domain': 2}}]} + ] + } + self.execute_assignment_test_plan(test_plan) + def test_list_projects_for_user_with_inherited_group_project_grants(self): """Test inherited role assignments for groups on nested projects. @@ -5925,6 +6541,53 @@ class InheritanceTests(object): self.assertEqual(1, len(user_projects)) self.assertIn(root_project, user_projects) + # TODO(henry-nash): The test above uses list_projects_for_user + # which may, in a subsequent patch, be re-implemeted to call + # list_role_assignments and then report only the distinct projects. + # + # The test plan below therefore mirrors this test, to ensure that + # list_role_assignments works the same. Once list_projects_for_user + # has been re-implemented then the manual tests above can be + # refactored. + test_plan = { + # A domain with a project ans sub-project, plus a user. + # Also, create 2 roles. + 'entities': { + 'domains': {'id': DEFAULT_DOMAIN_ID, 'users': 1, 'groups': 1, + 'projects': {'project': 1}}, + 'roles': 2}, + 'group_memberships': [{'group': 0, 'users': [0]}], + # A direct role and an inherited role on the parent + 'assignments': [{'group': 0, 'role': 0, 'project': 0}, + {'group': 0, 'role': 1, 'project': 0, + 'inherited_to_projects': True}], + 'tests': [ + # List all effective assignments for user[0] - should get back + # one direct role plus one inherited role. + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 0, + 'indirect': {'group': 0}}, + {'user': 0, 'role': 1, 'project': 1, + 'indirect': {'group': 0, 'project': 0}}]} + ] + } + + test_plan_with_os_inherit_disabled = { + 'tests': [ + # List all effective assignments for user[0] - should only get + # back the one direct role. + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 0, + 'indirect': {'group': 0}}]} + ] + } + self.config_fixture.config(group='os_inherit', enabled=True) + test_data = self.execute_assignment_test_plan(test_plan) + self.config_fixture.config(group='os_inherit', enabled=False) + # Pass the existing test data in to allow execution of 2nd test plan + self.execute_assignment_tests( + test_plan_with_os_inherit_disabled, test_data) + class FilterTests(filtering.FilterTests): def test_list_entities_filtered(self): diff --git a/keystone-moon/keystone/tests/unit/test_backend_kvs.py b/keystone-moon/keystone/tests/unit/test_backend_kvs.py index a22faa59..7406192a 100644 --- a/keystone-moon/keystone/tests/unit/test_backend_kvs.py +++ b/keystone-moon/keystone/tests/unit/test_backend_kvs.py @@ -20,14 +20,14 @@ import six from keystone.common import utils from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import test_backend CONF = cfg.CONF -class KvsToken(tests.TestCase, test_backend.TokenTests): +class KvsToken(unit.TestCase, test_backend.TokenTests): def setUp(self): super(KvsToken, self).setUp() self.load_backends() @@ -103,7 +103,7 @@ class KvsToken(tests.TestCase, test_backend.TokenTests): self.assertEqual(expected_user_token_list, user_token_list) -class KvsCatalog(tests.TestCase, test_backend.CatalogTests): +class KvsCatalog(unit.TestCase, test_backend.CatalogTests): def setUp(self): super(KvsCatalog, self).setUp() self.load_backends() @@ -157,7 +157,7 @@ class KvsCatalog(tests.TestCase, test_backend.CatalogTests): self.skipTest("kvs backend doesn't support filtering") -class KvsTokenCacheInvalidation(tests.TestCase, +class KvsTokenCacheInvalidation(unit.TestCase, test_backend.TokenCacheInvalidation): def setUp(self): super(KvsTokenCacheInvalidation, self).setUp() diff --git a/keystone-moon/keystone/tests/unit/test_backend_ldap.py b/keystone-moon/keystone/tests/unit/test_backend_ldap.py index 94fb82e7..808922a7 100644 --- a/keystone-moon/keystone/tests/unit/test_backend_ldap.py +++ b/keystone-moon/keystone/tests/unit/test_backend_ldap.py @@ -21,6 +21,7 @@ import ldap import mock from oslo_config import cfg import pkg_resources +from six.moves import http_client from six.moves import range from testtools import matchers @@ -31,7 +32,7 @@ from keystone import exception from keystone import identity from keystone.identity.mapping_backends import mapping as map from keystone import resource -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import default_fixtures from keystone.tests.unit import identity_mapping as mapping_sql from keystone.tests.unit.ksfixtures import database @@ -138,7 +139,7 @@ class BaseLDAPIdentity(test_backend.IdentityTests): def config_files(self): config_files = super(BaseLDAPIdentity, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_ldap.conf')) + config_files.append(unit.dirs.tests_conf('backend_ldap.conf')) return config_files def get_user_enabled_vals(self, user): @@ -712,7 +713,7 @@ class BaseLDAPIdentity(test_backend.IdentityTests): self.identity_api.get_group, group['id']) - @tests.skip_if_cache_disabled('identity') + @unit.skip_if_cache_disabled('identity') def test_cache_layer_group_crud(self): group = { 'domain_id': CONF.identity.default_domain_id, @@ -979,8 +980,21 @@ class BaseLDAPIdentity(test_backend.IdentityTests): # returned as part of the ref. self.assertIs(True, project_info['enabled']) + def test_list_role_assignment_by_domain(self): + """Multiple domain assignments are not supported.""" + self.assertRaises( + (exception.Forbidden, exception.DomainNotFound), + super(BaseLDAPIdentity, self).test_list_role_assignment_by_domain) + + def test_list_role_assignment_by_user_with_domain_group_roles(self): + """Multiple domain assignments are not supported.""" + self.assertRaises( + (exception.Forbidden, exception.DomainNotFound), + super(BaseLDAPIdentity, self). + test_list_role_assignment_by_user_with_domain_group_roles) + -class LDAPIdentity(BaseLDAPIdentity, tests.TestCase): +class LDAPIdentity(BaseLDAPIdentity, unit.TestCase): def setUp(self): # NOTE(dstanek): The database must be setup prior to calling the @@ -1578,7 +1592,7 @@ class LDAPIdentity(BaseLDAPIdentity, tests.TestCase): self.resource_api.get_domain, domain['id']) - @tests.skip_if_no_multiple_domains_support + @unit.skip_if_no_multiple_domains_support def test_create_domain_case_sensitivity(self): # domains are read-only, so case sensitivity isn't an issue ref = { @@ -1633,7 +1647,7 @@ class LDAPIdentity(BaseLDAPIdentity, tests.TestCase): self.resource_api.get_project, project['id']) - @tests.skip_if_cache_disabled('assignment') + @unit.skip_if_cache_disabled('assignment') def test_cache_layer_project_crud(self): # NOTE(morganfainberg): LDAP implementation does not currently support # updating project names. This method override provides a different @@ -2091,7 +2105,7 @@ class LDAPIdentityEnabledEmulation(LDAPIdentity): def config_files(self): config_files = super(LDAPIdentityEnabledEmulation, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_ldap.conf')) + config_files.append(unit.dirs.tests_conf('backend_ldap.conf')) return config_files def config_overrides(self): @@ -2250,12 +2264,12 @@ class LDAPIdentityEnabledEmulation(LDAPIdentity): self.assertIs(False, user_ref['enabled']) -class LdapIdentitySqlAssignment(BaseLDAPIdentity, tests.SQLDriverOverrides, - tests.TestCase): +class LdapIdentitySqlAssignment(BaseLDAPIdentity, unit.SQLDriverOverrides, + unit.TestCase): def config_files(self): config_files = super(LdapIdentitySqlAssignment, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_ldap_sql.conf')) + config_files.append(unit.dirs.tests_conf('backend_ldap_sql.conf')) return config_files def setUp(self): @@ -2473,7 +2487,7 @@ class BaseMultiLDAPandSQLIdentity(object): self.identity_api._get_domain_driver_and_entity_id( user['id'])) - if expected_status == 200: + if expected_status == http_client.OK: ref = driver.get_user(entity_id) ref = self.identity_api._set_domain_id_and_mapping( ref, domain_id, driver, map.EntityType.USER) @@ -2518,8 +2532,8 @@ class BaseMultiLDAPandSQLIdentity(object): password=self.users[user]['password']) -class MultiLDAPandSQLIdentity(BaseLDAPIdentity, tests.SQLDriverOverrides, - tests.TestCase, BaseMultiLDAPandSQLIdentity): +class MultiLDAPandSQLIdentity(BaseLDAPIdentity, unit.SQLDriverOverrides, + unit.TestCase, BaseMultiLDAPandSQLIdentity): """Class to test common SQL plus individual LDAP backends. We define a set of domains and domain-specific backends: @@ -2598,7 +2612,7 @@ class MultiLDAPandSQLIdentity(BaseLDAPIdentity, tests.SQLDriverOverrides, """ self.config_fixture.config( group='identity', domain_specific_drivers_enabled=True, - domain_config_dir=tests.TESTCONF + '/domain_configs_multi_ldap') + domain_config_dir=unit.TESTCONF + '/domain_configs_multi_ldap') self.config_fixture.config(group='identity_mapping', backward_compatible_ids=False) @@ -2647,21 +2661,23 @@ class MultiLDAPandSQLIdentity(BaseLDAPIdentity, tests.SQLDriverOverrides, check_user = self.check_user check_user(self.users['user0'], - self.domains['domain_default']['id'], 200) + self.domains['domain_default']['id'], http_client.OK) for domain in [self.domains['domain1']['id'], self.domains['domain2']['id'], self.domains['domain3']['id'], self.domains['domain4']['id']]: check_user(self.users['user0'], domain, exception.UserNotFound) - check_user(self.users['user1'], self.domains['domain1']['id'], 200) + check_user(self.users['user1'], self.domains['domain1']['id'], + http_client.OK) for domain in [self.domains['domain_default']['id'], self.domains['domain2']['id'], self.domains['domain3']['id'], self.domains['domain4']['id']]: check_user(self.users['user1'], domain, exception.UserNotFound) - check_user(self.users['user2'], self.domains['domain2']['id'], 200) + check_user(self.users['user2'], self.domains['domain2']['id'], + http_client.OK) for domain in [self.domains['domain_default']['id'], self.domains['domain1']['id'], self.domains['domain3']['id'], @@ -2671,10 +2687,14 @@ class MultiLDAPandSQLIdentity(BaseLDAPIdentity, tests.SQLDriverOverrides, # domain3 and domain4 share the same backend, so you should be # able to see user3 and user4 from either. - check_user(self.users['user3'], self.domains['domain3']['id'], 200) - check_user(self.users['user3'], self.domains['domain4']['id'], 200) - check_user(self.users['user4'], self.domains['domain3']['id'], 200) - check_user(self.users['user4'], self.domains['domain4']['id'], 200) + check_user(self.users['user3'], self.domains['domain3']['id'], + http_client.OK) + check_user(self.users['user3'], self.domains['domain4']['id'], + http_client.OK) + check_user(self.users['user4'], self.domains['domain3']['id'], + http_client.OK) + check_user(self.users['user4'], self.domains['domain4']['id'], + http_client.OK) for domain in [self.domains['domain_default']['id'], self.domains['domain1']['id'], @@ -2803,6 +2823,17 @@ class MultiLDAPandSQLIdentity(BaseLDAPIdentity, tests.SQLDriverOverrides, base = super(BaseLDAPIdentity, self) base.test_list_role_assignments_filtered_by_role() + def test_list_role_assignment_by_domain(self): + # With multi LDAP this method should work, so override the override + # from BaseLDAPIdentity + super(BaseLDAPIdentity, self).test_list_role_assignment_by_domain + + def test_list_role_assignment_by_user_with_domain_group_roles(self): + # With multi LDAP this method should work, so override the override + # from BaseLDAPIdentity + super(BaseLDAPIdentity, self).\ + test_list_role_assignment_by_user_with_domain_group_roles + class MultiLDAPandSQLIdentityDomainConfigsInSQL(MultiLDAPandSQLIdentity): """Class to test the use of domain configs stored in the database. @@ -2919,19 +2950,104 @@ class MultiLDAPandSQLIdentityDomainConfigsInSQL(MultiLDAPandSQLIdentity): domain_cfgs.get_domain_conf(CONF.identity.default_domain_id)) self.assertEqual(CONF.ldap.url, default_config.ldap.url) - def test_setting_sql_driver_raises_exception(self): - """Ensure setting of domain specific sql driver is prevented.""" + def test_setting_multiple_sql_driver_raises_exception(self): + """Ensure setting multiple domain specific sql drivers is prevented.""" new_config = {'identity': {'driver': 'sql'}} self.domain_config_api.create_config( CONF.identity.default_domain_id, new_config) - self.assertRaises(exception.InvalidDomainConfig, + self.identity_api.domain_configs.get_domain_conf( + CONF.identity.default_domain_id) + self.domain_config_api.create_config(self.domains['domain1']['id'], + new_config) + self.assertRaises(exception.MultipleSQLDriversInConfig, self.identity_api.domain_configs.get_domain_conf, - CONF.identity.default_domain_id) + self.domains['domain1']['id']) + + def test_same_domain_gets_sql_driver(self): + """Ensure we can set an SQL driver if we have had it before.""" + + new_config = {'identity': {'driver': 'sql'}} + self.domain_config_api.create_config( + CONF.identity.default_domain_id, new_config) + self.identity_api.domain_configs.get_domain_conf( + CONF.identity.default_domain_id) + + # By using a slightly different config, we cause the driver to be + # reloaded...and hence check if we can reuse the sql driver + new_config = {'identity': {'driver': 'sql'}, + 'ldap': {'url': 'fake://memory1'}} + self.domain_config_api.create_config( + CONF.identity.default_domain_id, new_config) + self.identity_api.domain_configs.get_domain_conf( + CONF.identity.default_domain_id) + + def test_delete_domain_clears_sql_registration(self): + """Ensure registration is deleted when a domain is deleted.""" + + domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex} + domain = self.resource_api.create_domain(domain['id'], domain) + new_config = {'identity': {'driver': 'sql'}} + self.domain_config_api.create_config(domain['id'], new_config) + self.identity_api.domain_configs.get_domain_conf(domain['id']) + + # First show that trying to set SQL for another driver fails + self.domain_config_api.create_config(self.domains['domain1']['id'], + new_config) + self.assertRaises(exception.MultipleSQLDriversInConfig, + self.identity_api.domain_configs.get_domain_conf, + self.domains['domain1']['id']) + self.domain_config_api.delete_config(self.domains['domain1']['id']) + + # Now we delete the domain + domain['enabled'] = False + self.resource_api.update_domain(domain['id'], domain) + self.resource_api.delete_domain(domain['id']) + + # The registration should now be available + self.domain_config_api.create_config(self.domains['domain1']['id'], + new_config) + self.identity_api.domain_configs.get_domain_conf( + self.domains['domain1']['id']) + + def test_orphaned_registration_does_not_prevent_getting_sql_driver(self): + """Ensure we self heal an orphaned sql registration.""" + + domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex} + domain = self.resource_api.create_domain(domain['id'], domain) + new_config = {'identity': {'driver': 'sql'}} + self.domain_config_api.create_config(domain['id'], new_config) + self.identity_api.domain_configs.get_domain_conf(domain['id']) + + # First show that trying to set SQL for another driver fails + self.domain_config_api.create_config(self.domains['domain1']['id'], + new_config) + self.assertRaises(exception.MultipleSQLDriversInConfig, + self.identity_api.domain_configs.get_domain_conf, + self.domains['domain1']['id']) + + # Now we delete the domain by using the backend driver directly, + # which causes the domain to be deleted without any of the cleanup + # that is in the manager (this is simulating a server process crashing + # in the middle of a delete domain operation, and somehow leaving the + # domain config settings in place, but the domain is deleted). We + # should still be able to set another domain to SQL, since we should + # self heal this issue. + + self.resource_api.driver.delete_domain(domain['id']) + # Invalidate cache (so we will see the domain has gone) + self.resource_api.get_domain.invalidate( + self.resource_api, domain['id']) + + # The registration should now be available + self.domain_config_api.create_config(self.domains['domain1']['id'], + new_config) + self.identity_api.domain_configs.get_domain_conf( + self.domains['domain1']['id']) class DomainSpecificLDAPandSQLIdentity( - BaseLDAPIdentity, tests.SQLDriverOverrides, tests.TestCase, + BaseLDAPIdentity, unit.SQLDriverOverrides, unit.TestCase, BaseMultiLDAPandSQLIdentity): """Class to test when all domains use specific configs, including SQL. @@ -2954,7 +3070,7 @@ class DomainSpecificLDAPandSQLIdentity( self.config_fixture.config( group='identity', domain_specific_drivers_enabled=True, domain_config_dir=( - tests.TESTCONF + '/domain_configs_one_sql_one_ldap')) + unit.TESTCONF + '/domain_configs_one_sql_one_ldap')) self.config_fixture.config(group='identity_mapping', backward_compatible_ids=False) @@ -3035,12 +3151,12 @@ class DomainSpecificLDAPandSQLIdentity( # driver, but won't find it via any other domain driver self.check_user(self.users['user0'], - self.domains['domain_default']['id'], 200) + self.domains['domain_default']['id'], http_client.OK) self.check_user(self.users['user0'], self.domains['domain1']['id'], exception.UserNotFound) self.check_user(self.users['user1'], - self.domains['domain1']['id'], 200) + self.domains['domain1']['id'], http_client.OK) self.check_user(self.users['user1'], self.domains['domain_default']['id'], exception.UserNotFound) @@ -3113,7 +3229,7 @@ class DomainSpecificSQLIdentity(DomainSpecificLDAPandSQLIdentity): self.config_fixture.config( group='identity', domain_specific_drivers_enabled=True, domain_config_dir=( - tests.TESTCONF + '/domain_configs_default_ldap_one_sql')) + unit.TESTCONF + '/domain_configs_default_ldap_one_sql')) # Part of the testing counts how many new mappings get created as # we create users, so ensure we are NOT using mapping for the default # LDAP domain so this doesn't confuse the calculation. @@ -3189,12 +3305,12 @@ class DomainSpecificSQLIdentity(DomainSpecificLDAPandSQLIdentity): exception.MultipleSQLDriversInConfig, self.identity_api.domain_configs._load_config_from_file, self.resource_api, - [tests.TESTCONF + '/domain_configs_one_extra_sql/' + + [unit.TESTCONF + '/domain_configs_one_extra_sql/' + 'keystone.domain2.conf'], 'domain2') -class LdapFilterTests(test_backend.FilterTests, tests.TestCase): +class LdapFilterTests(test_backend.FilterTests, unit.TestCase): def setUp(self): super(LdapFilterTests, self).setUp() @@ -3212,7 +3328,7 @@ class LdapFilterTests(test_backend.FilterTests, tests.TestCase): def config_files(self): config_files = super(LdapFilterTests, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_ldap.conf')) + config_files.append(unit.dirs.tests_conf('backend_ldap.conf')) return config_files def test_list_users_in_group_filtered(self): diff --git a/keystone-moon/keystone/tests/unit/test_backend_ldap_pool.py b/keystone-moon/keystone/tests/unit/test_backend_ldap_pool.py index 66827d7e..2b714b57 100644 --- a/keystone-moon/keystone/tests/unit/test_backend_ldap_pool.py +++ b/keystone-moon/keystone/tests/unit/test_backend_ldap_pool.py @@ -21,7 +21,7 @@ from oslotest import mockpatch from keystone.common.ldap import core as ldap_core from keystone.identity.backends import ldap -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import fakeldap from keystone.tests.unit import test_backend_ldap @@ -209,7 +209,7 @@ class LdapPoolCommonTestMixin(object): class LdapIdentitySqlAssignment(LdapPoolCommonTestMixin, test_backend_ldap.LdapIdentitySqlAssignment, - tests.TestCase): + unit.TestCase): """Executes tests in existing base class with pooled LDAP handler.""" def setUp(self): self.useFixture(mockpatch.PatchObject( @@ -226,7 +226,7 @@ class LdapIdentitySqlAssignment(LdapPoolCommonTestMixin, def config_files(self): config_files = super(LdapIdentitySqlAssignment, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_ldap_pool.conf')) + config_files.append(unit.dirs.tests_conf('backend_ldap_pool.conf')) return config_files @mock.patch.object(ldap_core, 'utf8_encode') diff --git a/keystone-moon/keystone/tests/unit/test_backend_rules.py b/keystone-moon/keystone/tests/unit/test_backend_rules.py index bc0dc13d..9a11fddc 100644 --- a/keystone-moon/keystone/tests/unit/test_backend_rules.py +++ b/keystone-moon/keystone/tests/unit/test_backend_rules.py @@ -14,11 +14,11 @@ from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import test_backend -class RulesPolicy(tests.TestCase, test_backend.PolicyTests): +class RulesPolicy(unit.TestCase, test_backend.PolicyTests): def setUp(self): super(RulesPolicy, self).setUp() self.load_backends() diff --git a/keystone-moon/keystone/tests/unit/test_backend_sql.py b/keystone-moon/keystone/tests/unit/test_backend_sql.py index bf50ac21..69fac63a 100644 --- a/keystone-moon/keystone/tests/unit/test_backend_sql.py +++ b/keystone-moon/keystone/tests/unit/test_backend_sql.py @@ -29,7 +29,7 @@ from keystone.common import driver_hints from keystone.common import sql from keystone import exception from keystone.identity.backends import sql as identity_sql -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import default_fixtures from keystone.tests.unit.ksfixtures import database from keystone.tests.unit import test_backend @@ -40,7 +40,7 @@ CONF = cfg.CONF DEFAULT_DOMAIN_ID = CONF.identity.default_domain_id -class SqlTests(tests.SQLDriverOverrides, tests.TestCase): +class SqlTests(unit.SQLDriverOverrides, unit.TestCase): def setUp(self): super(SqlTests, self).setUp() @@ -54,7 +54,7 @@ class SqlTests(tests.SQLDriverOverrides, tests.TestCase): def config_files(self): config_files = super(SqlTests, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_sql.conf')) + config_files.append(unit.dirs.tests_conf('backend_sql.conf')) return config_files @@ -840,7 +840,7 @@ class FakeTable(sql.ModelBase): raise KeyError -class SqlDecorators(tests.TestCase): +class SqlDecorators(unit.TestCase): def test_initialization_fail(self): self.assertRaises(exception.StringLengthExceeded, @@ -863,7 +863,7 @@ class SqlDecorators(tests.TestCase): self.assertRaises(KeyError, FakeTable().lookup) -class SqlModuleInitialization(tests.TestCase): +class SqlModuleInitialization(unit.TestCase): @mock.patch.object(sql.core, 'CONF') @mock.patch.object(options, 'set_defaults') diff --git a/keystone-moon/keystone/tests/unit/test_backend_templated.py b/keystone-moon/keystone/tests/unit/test_backend_templated.py index 82a8bed8..4a7bf9e5 100644 --- a/keystone-moon/keystone/tests/unit/test_backend_templated.py +++ b/keystone-moon/keystone/tests/unit/test_backend_templated.py @@ -18,7 +18,7 @@ import mock from six.moves import zip from keystone import catalog -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import default_fixtures from keystone.tests.unit.ksfixtures import database from keystone.tests.unit import test_backend @@ -28,7 +28,7 @@ BROKEN_WRITE_FUNCTIONALITY_MSG = ("Templated backend doesn't correctly " "implement write operations") -class TestTemplatedCatalog(tests.TestCase, test_backend.CatalogTests): +class TestTemplatedCatalog(unit.TestCase, test_backend.CatalogTests): DEFAULT_FIXTURE = { 'RegionOne': { @@ -60,7 +60,7 @@ class TestTemplatedCatalog(tests.TestCase, test_backend.CatalogTests): self.config_fixture.config( group='catalog', driver='templated', - template_file=tests.dirs.tests('default_catalog.templates')) + template_file=unit.dirs.tests('default_catalog.templates')) def test_get_catalog(self): catalog_ref = self.catalog_api.get_catalog('foo', 'bar') @@ -161,11 +161,11 @@ class TestTemplatedCatalog(tests.TestCase, test_backend.CatalogTests): def test_region_crud(self): self.skipTest(BROKEN_WRITE_FUNCTIONALITY_MSG) - @tests.skip_if_cache_disabled('catalog') + @unit.skip_if_cache_disabled('catalog') def test_cache_layer_region_crud(self): self.skipTest(BROKEN_WRITE_FUNCTIONALITY_MSG) - @tests.skip_if_cache_disabled('catalog') + @unit.skip_if_cache_disabled('catalog') def test_invalidate_cache_when_updating_region(self): self.skipTest(BROKEN_WRITE_FUNCTIONALITY_MSG) @@ -189,11 +189,11 @@ class TestTemplatedCatalog(tests.TestCase, test_backend.CatalogTests): def test_service_crud(self): self.skipTest(BROKEN_WRITE_FUNCTIONALITY_MSG) - @tests.skip_if_cache_disabled('catalog') + @unit.skip_if_cache_disabled('catalog') def test_cache_layer_service_crud(self): self.skipTest(BROKEN_WRITE_FUNCTIONALITY_MSG) - @tests.skip_if_cache_disabled('catalog') + @unit.skip_if_cache_disabled('catalog') def test_invalidate_cache_when_updating_service(self): self.skipTest(BROKEN_WRITE_FUNCTIONALITY_MSG) @@ -234,6 +234,6 @@ class TestTemplatedCatalog(tests.TestCase, test_backend.CatalogTests): endpoints = self.catalog_api.list_endpoints() self.assertEqual(expected_ids, set(e['id'] for e in endpoints)) - @tests.skip_if_cache_disabled('catalog') + @unit.skip_if_cache_disabled('catalog') def test_invalidate_cache_when_updating_endpoint(self): self.skipTest(BROKEN_WRITE_FUNCTIONALITY_MSG) diff --git a/keystone-moon/keystone/tests/unit/test_cache.py b/keystone-moon/keystone/tests/unit/test_cache.py index c60df877..3c2afe66 100644 --- a/keystone-moon/keystone/tests/unit/test_cache.py +++ b/keystone-moon/keystone/tests/unit/test_cache.py @@ -23,7 +23,7 @@ from oslo_config import cfg from keystone.common import cache from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit CONF = cfg.CONF @@ -76,7 +76,7 @@ class TestProxyValue(object): self.cached = False -class CacheRegionTest(tests.TestCase): +class CacheRegionTest(unit.TestCase): def setUp(self): super(CacheRegionTest, self).setUp() @@ -293,7 +293,7 @@ class CacheRegionTest(tests.TestCase): "bogus") -class CacheNoopBackendTest(tests.TestCase): +class CacheNoopBackendTest(unit.TestCase): def setUp(self): super(CacheNoopBackendTest, self).setUp() diff --git a/keystone-moon/keystone/tests/unit/test_cache_backend_mongo.py b/keystone-moon/keystone/tests/unit/test_cache_backend_mongo.py index 369570d6..66f80c21 100644 --- a/keystone-moon/keystone/tests/unit/test_cache_backend_mongo.py +++ b/keystone-moon/keystone/tests/unit/test_cache_backend_mongo.py @@ -24,7 +24,7 @@ from six.moves import range from keystone.common.cache.backends import mongo from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit # Mock database structure sample where 'ks_cache' is database and @@ -278,7 +278,7 @@ class MyTransformer(mongo.BaseTransform): return super(MyTransformer, self).transform_outgoing(son, collection) -class MongoCache(tests.BaseTestCase): +class MongoCache(unit.BaseTestCase): def setUp(self): super(MongoCache, self).setUp() global COLLECTIONS diff --git a/keystone-moon/keystone/tests/unit/test_catalog.py b/keystone-moon/keystone/tests/unit/test_catalog.py index 4e7f4037..85acfedf 100644 --- a/keystone-moon/keystone/tests/unit/test_catalog.py +++ b/keystone-moon/keystone/tests/unit/test_catalog.py @@ -14,8 +14,10 @@ import uuid +from six.moves import http_client + from keystone import catalog -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit.ksfixtures import database from keystone.tests.unit import rest @@ -30,7 +32,7 @@ class V2CatalogTestCase(rest.RestfulTestCase): self.useFixture(database.Database()) self.service_id = uuid.uuid4().hex - self.service = self.new_service_ref() + self.service = unit.new_service_ref() self.service['id'] = self.service_id self.catalog_api.create_service( self.service_id, @@ -47,24 +49,12 @@ class V2CatalogTestCase(rest.RestfulTestCase): super(V2CatalogTestCase, self).config_overrides() self.config_fixture.config(group='catalog', driver='sql') - def new_ref(self): - """Populates a ref with attributes common to all API entities.""" - return { - 'id': uuid.uuid4().hex, - 'name': uuid.uuid4().hex, - 'description': uuid.uuid4().hex, - 'enabled': True} - - def new_service_ref(self): - ref = self.new_ref() - ref['type'] = uuid.uuid4().hex - return ref - def _get_token_id(self, r): """Applicable only to JSON.""" return r.result['access']['token']['id'] - def _endpoint_create(self, expected_status=200, service_id=SERVICE_FIXTURE, + def _endpoint_create(self, expected_status=http_client.OK, + service_id=SERVICE_FIXTURE, publicurl='http://localhost:8080', internalurl='http://localhost:8080', adminurl='http://localhost:8080'): @@ -115,16 +105,20 @@ class V2CatalogTestCase(rest.RestfulTestCase): self.assertNotIn("internalurl", response.result['endpoint']) def test_endpoint_create_with_null_publicurl(self): - self._endpoint_create(expected_status=400, publicurl=None) + self._endpoint_create(expected_status=http_client.BAD_REQUEST, + publicurl=None) def test_endpoint_create_with_empty_publicurl(self): - self._endpoint_create(expected_status=400, publicurl='') + self._endpoint_create(expected_status=http_client.BAD_REQUEST, + publicurl='') def test_endpoint_create_with_null_service_id(self): - self._endpoint_create(expected_status=400, service_id=None) + self._endpoint_create(expected_status=http_client.BAD_REQUEST, + service_id=None) def test_endpoint_create_with_empty_service_id(self): - self._endpoint_create(expected_status=400, service_id='') + self._endpoint_create(expected_status=http_client.BAD_REQUEST, + service_id='') def test_endpoint_create_with_valid_url(self): """Create endpoint with valid URL should be tested, too.""" @@ -132,7 +126,7 @@ class V2CatalogTestCase(rest.RestfulTestCase): valid_url = 'http://127.0.0.1:8774/v1.1/$(tenant_id)s' # baseline tests that all valid URLs works - self._endpoint_create(expected_status=200, + self._endpoint_create(expected_status=http_client.OK, publicurl=valid_url, internalurl=valid_url, adminurl=valid_url) @@ -159,7 +153,7 @@ class V2CatalogTestCase(rest.RestfulTestCase): # Case one: publicurl, internalurl and adminurl are # all invalid for invalid_url in invalid_urls: - self._endpoint_create(expected_status=400, + self._endpoint_create(expected_status=http_client.BAD_REQUEST, publicurl=invalid_url, internalurl=invalid_url, adminurl=invalid_url) @@ -167,7 +161,7 @@ class V2CatalogTestCase(rest.RestfulTestCase): # Case two: publicurl, internalurl are invalid # and adminurl is valid for invalid_url in invalid_urls: - self._endpoint_create(expected_status=400, + self._endpoint_create(expected_status=http_client.BAD_REQUEST, publicurl=invalid_url, internalurl=invalid_url, adminurl=valid_url) @@ -175,7 +169,7 @@ class V2CatalogTestCase(rest.RestfulTestCase): # Case three: publicurl, adminurl are invalid # and internalurl is valid for invalid_url in invalid_urls: - self._endpoint_create(expected_status=400, + self._endpoint_create(expected_status=http_client.BAD_REQUEST, publicurl=invalid_url, internalurl=valid_url, adminurl=invalid_url) @@ -183,7 +177,7 @@ class V2CatalogTestCase(rest.RestfulTestCase): # Case four: internalurl, adminurl are invalid # and publicurl is valid for invalid_url in invalid_urls: - self._endpoint_create(expected_status=400, + self._endpoint_create(expected_status=http_client.BAD_REQUEST, publicurl=valid_url, internalurl=invalid_url, adminurl=invalid_url) @@ -191,7 +185,7 @@ class V2CatalogTestCase(rest.RestfulTestCase): # Case five: publicurl is invalid, internalurl # and adminurl are valid for invalid_url in invalid_urls: - self._endpoint_create(expected_status=400, + self._endpoint_create(expected_status=http_client.BAD_REQUEST, publicurl=invalid_url, internalurl=valid_url, adminurl=valid_url) @@ -199,7 +193,7 @@ class V2CatalogTestCase(rest.RestfulTestCase): # Case six: internalurl is invalid, publicurl # and adminurl are valid for invalid_url in invalid_urls: - self._endpoint_create(expected_status=400, + self._endpoint_create(expected_status=http_client.BAD_REQUEST, publicurl=valid_url, internalurl=invalid_url, adminurl=valid_url) @@ -207,13 +201,13 @@ class V2CatalogTestCase(rest.RestfulTestCase): # Case seven: adminurl is invalid, publicurl # and internalurl are valid for invalid_url in invalid_urls: - self._endpoint_create(expected_status=400, + self._endpoint_create(expected_status=http_client.BAD_REQUEST, publicurl=valid_url, internalurl=valid_url, adminurl=invalid_url) -class TestV2CatalogAPISQL(tests.TestCase): +class TestV2CatalogAPISQL(unit.TestCase): def setUp(self): super(TestV2CatalogAPISQL, self).setUp() diff --git a/keystone-moon/keystone/tests/unit/test_cert_setup.py b/keystone-moon/keystone/tests/unit/test_cert_setup.py index 3d300810..47a99810 100644 --- a/keystone-moon/keystone/tests/unit/test_cert_setup.py +++ b/keystone-moon/keystone/tests/unit/test_cert_setup.py @@ -17,18 +17,19 @@ import os import shutil import mock +from six.moves import http_client from testtools import matchers from keystone.common import environment from keystone.common import openssl from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import rest from keystone import token -SSLDIR = tests.dirs.tmp('ssl') -CONF = tests.CONF +SSLDIR = unit.dirs.tmp('ssl') +CONF = unit.CONF DEFAULT_DOMAIN_ID = CONF.identity.default_domain_id @@ -113,11 +114,13 @@ class CertSetupTestCase(rest.RestfulTestCase): # requests don't have some of the normal information signing_resp = self.request(self.public_app, '/v2.0/certificates/signing', - method='GET', expected_status=200) + method='GET', + expected_status=http_client.OK) cacert_resp = self.request(self.public_app, '/v2.0/certificates/ca', - method='GET', expected_status=200) + method='GET', + expected_status=http_client.OK) with open(CONF.signing.certfile) as f: self.assertEqual(f.read(), signing_resp.text) @@ -133,7 +136,7 @@ class CertSetupTestCase(rest.RestfulTestCase): for accept in [None, 'text/html', 'application/json', 'text/xml']: headers = {'Accept': accept} if accept else {} resp = self.request(self.public_app, path, method='GET', - expected_status=200, + expected_status=http_client.OK, headers=headers) self.assertEqual('text/html', resp.content_type) @@ -146,7 +149,7 @@ class CertSetupTestCase(rest.RestfulTestCase): def test_failure(self): for path in ['/v2.0/certificates/signing', '/v2.0/certificates/ca']: self.request(self.public_app, path, method='GET', - expected_status=500) + expected_status=http_client.INTERNAL_SERVER_ERROR) def test_pki_certs_rebuild(self): self.test_create_pki_certs() @@ -219,7 +222,7 @@ class CertSetupTestCase(rest.RestfulTestCase): self.assertEqual(cert_file1, cert_file2) -class TestExecCommand(tests.TestCase): +class TestExecCommand(unit.TestCase): @mock.patch.object(environment.subprocess.Popen, 'poll') def test_running_a_successful_command(self, mock_poll): diff --git a/keystone-moon/keystone/tests/unit/test_cli.py b/keystone-moon/keystone/tests/unit/test_cli.py index 3f37612e..d967eb53 100644 --- a/keystone-moon/keystone/tests/unit/test_cli.py +++ b/keystone-moon/keystone/tests/unit/test_cli.py @@ -23,17 +23,17 @@ from keystone.cmd import cli from keystone.common import dependency from keystone.i18n import _ from keystone import resource -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit.ksfixtures import database CONF = cfg.CONF -class CliTestCase(tests.SQLDriverOverrides, tests.TestCase): +class CliTestCase(unit.SQLDriverOverrides, unit.TestCase): def config_files(self): config_files = super(CliTestCase, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_sql.conf')) + config_files.append(unit.dirs.tests_conf('backend_sql.conf')) return config_files def test_token_flush(self): @@ -42,7 +42,7 @@ class CliTestCase(tests.SQLDriverOverrides, tests.TestCase): cli.TokenFlush.main() -class CliDomainConfigAllTestCase(tests.SQLDriverOverrides, tests.TestCase): +class CliDomainConfigAllTestCase(unit.SQLDriverOverrides, unit.TestCase): def setUp(self): self.useFixture(database.Database()) @@ -50,7 +50,7 @@ class CliDomainConfigAllTestCase(tests.SQLDriverOverrides, tests.TestCase): self.load_backends() self.config_fixture.config( group='identity', - domain_config_dir=tests.TESTCONF + '/domain_configs_multi_ldap') + domain_config_dir=unit.TESTCONF + '/domain_configs_multi_ldap') self.domain_count = 3 self.setup_initial_domains() @@ -58,7 +58,7 @@ class CliDomainConfigAllTestCase(tests.SQLDriverOverrides, tests.TestCase): self.config_fixture.register_cli_opt(cli.command_opt) self.addCleanup(self.cleanup) config_files = super(CliDomainConfigAllTestCase, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_sql.conf')) + config_files.append(unit.dirs.tests_conf('backend_sql.conf')) return config_files def cleanup(self): diff --git a/keystone-moon/keystone/tests/unit/test_config.py b/keystone-moon/keystone/tests/unit/test_config.py index 431f9965..7984646d 100644 --- a/keystone-moon/keystone/tests/unit/test_config.py +++ b/keystone-moon/keystone/tests/unit/test_config.py @@ -18,31 +18,31 @@ from oslo_config import cfg from keystone import config from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit CONF = cfg.CONF -class ConfigTestCase(tests.TestCase): +class ConfigTestCase(unit.TestCase): def config_files(self): config_files = super(ConfigTestCase, self).config_files() # Insert the keystone sample as the first config file to be loaded # since it is used in one of the code paths to determine the paste-ini # location. - config_files.insert(0, tests.dirs.etc('keystone.conf.sample')) + config_files.insert(0, unit.dirs.etc('keystone.conf.sample')) return config_files def test_paste_config(self): - self.assertEqual(tests.dirs.etc('keystone-paste.ini'), + self.assertEqual(unit.dirs.etc('keystone-paste.ini'), config.find_paste_config()) self.config_fixture.config(group='paste_deploy', config_file=uuid.uuid4().hex) self.assertRaises(exception.ConfigFileNotFound, config.find_paste_config) self.config_fixture.config(group='paste_deploy', config_file='') - self.assertEqual(tests.dirs.etc('keystone.conf.sample'), + self.assertEqual(unit.dirs.etc('keystone.conf.sample'), config.find_paste_config()) def test_config_default(self): @@ -50,12 +50,12 @@ class ConfigTestCase(tests.TestCase): self.assertIs(None, CONF.auth.token) -class DeprecatedTestCase(tests.TestCase): +class DeprecatedTestCase(unit.TestCase): """Test using the original (deprecated) name for renamed options.""" def config_files(self): config_files = super(DeprecatedTestCase, self).config_files() - config_files.append(tests.dirs.tests_conf('deprecated.conf')) + config_files.append(unit.dirs.tests_conf('deprecated.conf')) return config_files def test_sql(self): @@ -66,12 +66,12 @@ class DeprecatedTestCase(tests.TestCase): self.assertEqual(54321, CONF.database.idle_timeout) -class DeprecatedOverrideTestCase(tests.TestCase): +class DeprecatedOverrideTestCase(unit.TestCase): """Test using the deprecated AND new name for renamed options.""" def config_files(self): config_files = super(DeprecatedOverrideTestCase, self).config_files() - config_files.append(tests.dirs.tests_conf('deprecated_override.conf')) + config_files.append(unit.dirs.tests_conf('deprecated_override.conf')) return config_files def test_sql(self): diff --git a/keystone-moon/keystone/tests/unit/test_contrib_ec2.py b/keystone-moon/keystone/tests/unit/test_contrib_ec2.py index c6717dc5..2810a47a 100644 --- a/keystone-moon/keystone/tests/unit/test_contrib_ec2.py +++ b/keystone-moon/keystone/tests/unit/test_contrib_ec2.py @@ -18,12 +18,12 @@ from keystoneclient.contrib.ec2 import utils as ec2_utils from keystone.contrib.ec2 import controllers from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import default_fixtures from keystone.tests.unit.ksfixtures import database -class TestCredentialEc2(tests.TestCase): +class TestCredentialEc2(unit.TestCase): # TODO(davechen): more testcases for ec2 credential are expected here and # the file name would be renamed to "test_credential" to correspond with # "test_v3_credential.py". diff --git a/keystone-moon/keystone/tests/unit/test_contrib_s3_core.py b/keystone-moon/keystone/tests/unit/test_contrib_s3_core.py index 43ea1ac5..18c76dad 100644 --- a/keystone-moon/keystone/tests/unit/test_contrib_s3_core.py +++ b/keystone-moon/keystone/tests/unit/test_contrib_s3_core.py @@ -16,10 +16,10 @@ import uuid from keystone.contrib import s3 from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit -class S3ContribCore(tests.TestCase): +class S3ContribCore(unit.TestCase): def setUp(self): super(S3ContribCore, self).setUp() diff --git a/keystone-moon/keystone/tests/unit/test_contrib_simple_cert.py b/keystone-moon/keystone/tests/unit/test_contrib_simple_cert.py index 8664e2c3..b241b41b 100644 --- a/keystone-moon/keystone/tests/unit/test_contrib_simple_cert.py +++ b/keystone-moon/keystone/tests/unit/test_contrib_simple_cert.py @@ -12,6 +12,8 @@ import uuid +from six.moves import http_client + from keystone.tests.unit import test_v3 @@ -31,7 +33,7 @@ class TestSimpleCert(BaseTestCase): method='GET', path=path, headers={'Accept': content_type}, - expected_status=200) + expected_status=http_client.OK) self.assertEqual(content_type, response.content_type.lower()) self.assertIn('---BEGIN', response.body) @@ -54,4 +56,4 @@ class TestSimpleCert(BaseTestCase): self.request(app=self.public_app, method='GET', path=path, - expected_status=500) + expected_status=http_client.INTERNAL_SERVER_ERROR) diff --git a/keystone-moon/keystone/tests/unit/test_exception.py b/keystone-moon/keystone/tests/unit/test_exception.py index bf541dfd..4d602ccc 100644 --- a/keystone-moon/keystone/tests/unit/test_exception.py +++ b/keystone-moon/keystone/tests/unit/test_exception.py @@ -21,10 +21,10 @@ import six from keystone.common import wsgi from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit -class ExceptionTestCase(tests.BaseTestCase): +class ExceptionTestCase(unit.BaseTestCase): def assertValidJsonRendering(self, e): resp = wsgi.render_exception(e) self.assertEqual(e.code, resp.status_int) diff --git a/keystone-moon/keystone/tests/unit/test_ipv6.py b/keystone-moon/keystone/tests/unit/test_ipv6.py index e3d467fb..df59429e 100644 --- a/keystone-moon/keystone/tests/unit/test_ipv6.py +++ b/keystone-moon/keystone/tests/unit/test_ipv6.py @@ -16,14 +16,14 @@ from oslo_config import cfg from keystone.common import environment -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit.ksfixtures import appserver CONF = cfg.CONF -class IPv6TestCase(tests.TestCase): +class IPv6TestCase(unit.TestCase): def setUp(self): self.skip_if_no_ipv6() diff --git a/keystone-moon/keystone/tests/unit/test_kvs.py b/keystone-moon/keystone/tests/unit/test_kvs.py index 77e05e6d..18931f5d 100644 --- a/keystone-moon/keystone/tests/unit/test_kvs.py +++ b/keystone-moon/keystone/tests/unit/test_kvs.py @@ -26,7 +26,7 @@ from keystone.common.kvs.backends import inmemdb from keystone.common.kvs.backends import memcached from keystone.common.kvs import core from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit NO_VALUE = api.NO_VALUE @@ -147,7 +147,7 @@ class TestMemcacheDriver(api.CacheBackend): self.client.set_multi(mapping, **self.set_arguments) -class KVSTest(tests.TestCase): +class KVSTest(unit.TestCase): def setUp(self): super(KVSTest, self).setUp() self.key_foo = 'foo_' + uuid.uuid4().hex @@ -569,7 +569,7 @@ class KVSTest(tests.TestCase): key=test_key) -class TestMemcachedBackend(tests.TestCase): +class TestMemcachedBackend(unit.TestCase): @mock.patch('keystone.common.kvs.backends.memcached._', six.text_type) def test_invalid_backend_fails_initialization(self): diff --git a/keystone-moon/keystone/tests/unit/test_ldap_livetest.py b/keystone-moon/keystone/tests/unit/test_ldap_livetest.py index b9f56e8d..e2abd56d 100644 --- a/keystone-moon/keystone/tests/unit/test_ldap_livetest.py +++ b/keystone-moon/keystone/tests/unit/test_ldap_livetest.py @@ -21,7 +21,7 @@ from six.moves import range from keystone import exception from keystone.identity.backends import ldap as identity_ldap -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import test_backend_ldap @@ -78,7 +78,7 @@ class LiveLDAPIdentity(test_backend_ldap.LDAPIdentity): def config_files(self): config_files = super(LiveLDAPIdentity, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_liveldap.conf')) + config_files.append(unit.dirs.tests_conf('backend_liveldap.conf')) return config_files def test_build_tree(self): diff --git a/keystone-moon/keystone/tests/unit/test_ldap_pool_livetest.py b/keystone-moon/keystone/tests/unit/test_ldap_pool_livetest.py index a8776e5b..81e91ce5 100644 --- a/keystone-moon/keystone/tests/unit/test_ldap_pool_livetest.py +++ b/keystone-moon/keystone/tests/unit/test_ldap_pool_livetest.py @@ -19,7 +19,7 @@ from oslo_config import cfg from keystone.common.ldap import core as ldap_core from keystone.identity.backends import ldap -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import fakeldap from keystone.tests.unit import test_backend_ldap_pool from keystone.tests.unit import test_ldap_livetest @@ -44,8 +44,7 @@ class LiveLDAPPoolIdentity(test_backend_ldap_pool.LdapPoolCommonTestMixin, def config_files(self): config_files = super(LiveLDAPPoolIdentity, self).config_files() - config_files.append(tests.dirs. - tests_conf('backend_pool_liveldap.conf')) + config_files.append(unit.dirs.tests_conf('backend_pool_liveldap.conf')) return config_files def test_assert_connector_used_not_fake_ldap_pool(self): diff --git a/keystone-moon/keystone/tests/unit/test_ldap_tls_livetest.py b/keystone-moon/keystone/tests/unit/test_ldap_tls_livetest.py index e77bbc98..6b47bfd9 100644 --- a/keystone-moon/keystone/tests/unit/test_ldap_tls_livetest.py +++ b/keystone-moon/keystone/tests/unit/test_ldap_tls_livetest.py @@ -18,7 +18,7 @@ from oslo_config import cfg from keystone import exception from keystone import identity -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import test_ldap_livetest @@ -40,7 +40,7 @@ class LiveTLSLDAPIdentity(test_ldap_livetest.LiveLDAPIdentity): def config_files(self): config_files = super(LiveTLSLDAPIdentity, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_tls_liveldap.conf')) + config_files.append(unit.dirs.tests_conf('backend_tls_liveldap.conf')) return config_files def test_tls_certfile_demand_option(self): diff --git a/keystone-moon/keystone/tests/unit/test_middleware.py b/keystone-moon/keystone/tests/unit/test_middleware.py index 3a26dd24..0eedb9c6 100644 --- a/keystone-moon/keystone/tests/unit/test_middleware.py +++ b/keystone-moon/keystone/tests/unit/test_middleware.py @@ -12,11 +12,21 @@ # License for the specific language governing permissions and limitations # under the License. +import hashlib +import uuid + from oslo_config import cfg +from six.moves import http_client import webob +from keystone.common import authorization +from keystone.common import tokenless_auth +from keystone.contrib.federation import constants as federation_constants +from keystone import exception from keystone import middleware -from keystone.tests import unit as tests +from keystone.tests import unit +from keystone.tests.unit import mapping_fixtures +from keystone.tests.unit import test_backend_sql CONF = cfg.CONF @@ -40,7 +50,7 @@ def make_response(**kwargs): return webob.Response(body) -class TokenAuthMiddlewareTest(tests.TestCase): +class TokenAuthMiddlewareTest(unit.TestCase): def test_request(self): req = make_request() req.headers[middleware.AUTH_TOKEN_HEADER] = 'MAGIC' @@ -49,7 +59,7 @@ class TokenAuthMiddlewareTest(tests.TestCase): self.assertEqual('MAGIC', context['token_id']) -class AdminTokenAuthMiddlewareTest(tests.TestCase): +class AdminTokenAuthMiddlewareTest(unit.TestCase): def test_request_admin(self): req = make_request() req.headers[middleware.AUTH_TOKEN_HEADER] = CONF.admin_token @@ -65,7 +75,7 @@ class AdminTokenAuthMiddlewareTest(tests.TestCase): self.assertFalse(context['is_admin']) -class PostParamsMiddlewareTest(tests.TestCase): +class PostParamsMiddlewareTest(unit.TestCase): def test_request_with_params(self): req = make_request(body="arg1=one", method='POST') middleware.PostParamsMiddleware(None).process_request(req) @@ -73,7 +83,7 @@ class PostParamsMiddlewareTest(tests.TestCase): self.assertEqual({"arg1": "one"}, params) -class JsonBodyMiddlewareTest(tests.TestCase): +class JsonBodyMiddlewareTest(unit.TestCase): def test_request_with_params(self): req = make_request(body='{"arg1": "one", "arg2": ["a"]}', content_type='application/json', @@ -87,14 +97,14 @@ class JsonBodyMiddlewareTest(tests.TestCase): content_type='application/json', method='POST') resp = middleware.JsonBodyMiddleware(None).process_request(req) - self.assertEqual(400, resp.status_int) + self.assertEqual(http_client.BAD_REQUEST, resp.status_int) def test_not_dict_body(self): req = make_request(body='42', content_type='application/json', method='POST') resp = middleware.JsonBodyMiddleware(None).process_request(req) - self.assertEqual(400, resp.status_int) + self.assertEqual(http_client.BAD_REQUEST, resp.status_int) self.assertTrue('valid JSON object' in resp.json['error']['message']) def test_no_content_type(self): @@ -109,7 +119,7 @@ class JsonBodyMiddlewareTest(tests.TestCase): content_type='text/plain', method='POST') resp = middleware.JsonBodyMiddleware(None).process_request(req) - self.assertEqual(400, resp.status_int) + self.assertEqual(http_client.BAD_REQUEST, resp.status_int) def test_unrecognized_content_type_without_body(self): req = make_request(content_type='text/plain', @@ -117,3 +127,624 @@ class JsonBodyMiddlewareTest(tests.TestCase): middleware.JsonBodyMiddleware(None).process_request(req) params = req.environ.get(middleware.PARAMS_ENV, {}) self.assertEqual({}, params) + + +class AuthContextMiddlewareTest(test_backend_sql.SqlTests): + + def setUp(self): + super(AuthContextMiddlewareTest, self).setUp() + self.client_issuer = uuid.uuid4().hex + self.untrusted_client_issuer = uuid.uuid4().hex + self.trusted_issuer = self.client_issuer + self.config_fixture.config(group='tokenless_auth', + trusted_issuer=[self.trusted_issuer]) + + # This idp_id is calculated based on + # sha256(self.client_issuer) + hashed_idp = hashlib.sha256(self.client_issuer) + self.idp_id = hashed_idp.hexdigest() + self._load_sample_data() + + def _load_sample_data(self): + self.domain_id = uuid.uuid4().hex + self.domain_name = uuid.uuid4().hex + self.project_id = uuid.uuid4().hex + self.project_name = uuid.uuid4().hex + self.user_name = uuid.uuid4().hex + self.user_password = uuid.uuid4().hex + self.user_email = uuid.uuid4().hex + self.protocol_id = 'x509' + self.role_id = uuid.uuid4().hex + self.role_name = uuid.uuid4().hex + # for ephemeral user + self.group_name = uuid.uuid4().hex + + # 1) Create a domain for the user. + self.domain = { + 'description': uuid.uuid4().hex, + 'enabled': True, + 'id': self.domain_id, + 'name': self.domain_name, + } + + self.resource_api.create_domain(self.domain_id, self.domain) + + # 2) Create a project for the user. + self.project = { + 'description': uuid.uuid4().hex, + 'domain_id': self.domain_id, + 'enabled': True, + 'id': self.project_id, + 'name': self.project_name, + } + + self.resource_api.create_project(self.project_id, self.project) + + # 3) Create a user in new domain. + self.user = { + 'name': self.user_name, + 'domain_id': self.domain_id, + 'project_id': self.project_id, + 'password': self.user_password, + 'email': self.user_email, + } + + self.user = self.identity_api.create_user(self.user) + + # Add IDP + self.idp = self._idp_ref(id=self.idp_id) + self.federation_api.create_idp(self.idp['id'], + self.idp) + + # Add a role + self.role = { + 'id': self.role_id, + 'name': self.role_name, + } + self.role_api.create_role(self.role_id, self.role) + + # Add a group + self.group = { + 'name': self.group_name, + 'domain_id': self.domain_id, + } + self.group = self.identity_api.create_group(self.group) + + # Assign a role to the user on a project + self.assignment_api.add_role_to_user_and_project( + user_id=self.user['id'], + tenant_id=self.project_id, + role_id=self.role_id) + + # Assign a role to the group on a project + self.assignment_api.create_grant( + role_id=self.role_id, + group_id=self.group['id'], + project_id=self.project_id) + + def _load_mapping_rules(self, rules): + # Add a mapping + self.mapping = self._mapping_ref(rules=rules) + self.federation_api.create_mapping(self.mapping['id'], + self.mapping) + # Add protocols + self.proto_x509 = self._proto_ref(mapping_id=self.mapping['id']) + self.proto_x509['id'] = self.protocol_id + self.federation_api.create_protocol(self.idp['id'], + self.proto_x509['id'], + self.proto_x509) + + def _idp_ref(self, id=None): + idp = { + 'id': id or uuid.uuid4().hex, + 'enabled': True, + 'description': uuid.uuid4().hex + } + return idp + + def _proto_ref(self, mapping_id=None): + proto = { + 'id': uuid.uuid4().hex, + 'mapping_id': mapping_id or uuid.uuid4().hex + } + return proto + + def _mapping_ref(self, rules=None): + if rules is None: + mapped_rules = {} + else: + mapped_rules = rules.get('rules', {}) + return { + 'id': uuid.uuid4().hex, + 'rules': mapped_rules + } + + def _assert_tokenless_auth_context(self, context, ephemeral_user=False): + self.assertIsNotNone(context) + self.assertEqual(self.project_id, context['project_id']) + self.assertIn(self.role_name, context['roles']) + if ephemeral_user: + self.assertEqual(self.group['id'], context['group_ids'][0]) + self.assertEqual('ephemeral', + context[federation_constants.PROTOCOL]) + self.assertEqual(self.idp_id, + context[federation_constants.IDENTITY_PROVIDER]) + else: + self.assertEqual(self.user['id'], context['user_id']) + + def _create_context(self, request, mapping_ref=None, + exception_expected=False): + """Builds the auth context from the given arguments. + + auth context will be returned from the AuthContextMiddleware based on + what is being passed in the given request and what mapping is being + setup in the backend DB. + + :param request: HTTP request + :param mapping_ref: A mapping in JSON structure will be setup in the + backend DB for mapping an user or a group. + :param exception_expected: Sets to True when an exception is expected + to raised based on the given arguments. + :returns: context an auth context contains user and role information + :rtype: dict + """ + if mapping_ref: + self._load_mapping_rules(mapping_ref) + + if not exception_expected: + (middleware.AuthContextMiddleware('Tokenless_auth_test'). + process_request(request)) + context = request.environ.get(authorization.AUTH_CONTEXT_ENV) + else: + context = middleware.AuthContextMiddleware('Tokenless_auth_test') + return context + + def test_context_already_exists(self): + req = make_request() + token_id = uuid.uuid4().hex + req.environ[authorization.AUTH_CONTEXT_ENV] = {'token_id': token_id} + context = self._create_context(request=req) + self.assertEqual(token_id, context['token_id']) + + def test_not_applicable_to_token_request(self): + env = {} + env['PATH_INFO'] = '/auth/tokens' + env['REQUEST_METHOD'] = 'POST' + req = make_request(environ=env) + context = self._create_context(request=req) + self.assertIsNone(context) + + def test_no_tokenless_attributes_request(self): + req = make_request() + context = self._create_context(request=req) + self.assertIsNone(context) + + def test_no_issuer_attribute_request(self): + env = {} + env['HTTP_X_PROJECT_ID'] = uuid.uuid4().hex + req = make_request(environ=env) + context = self._create_context(request=req) + self.assertIsNone(context) + + def test_has_only_issuer_and_project_name_request(self): + env = {} + # SSL_CLIENT_I_DN is the attribute name that wsgi env + # references to issuer of the client certificate. + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_NAME'] = uuid.uuid4().hex + req = make_request(environ=env) + context = self._create_context(request=req, + exception_expected=True) + self.assertRaises(exception.ValidationError, + context.process_request, + req) + + def test_has_only_issuer_and_project_domain_name_request(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_DOMAIN_NAME'] = uuid.uuid4().hex + req = make_request(environ=env) + context = self._create_context(request=req, + exception_expected=True) + self.assertRaises(exception.ValidationError, + context.process_request, + req) + + def test_has_only_issuer_and_project_domain_id_request(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_DOMAIN_ID'] = uuid.uuid4().hex + req = make_request(environ=env) + context = self._create_context(request=req, + exception_expected=True) + self.assertRaises(exception.ValidationError, + context.process_request, + req) + + def test_missing_both_domain_and_project_request(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + req = make_request(environ=env) + context = self._create_context(request=req, + exception_expected=True) + self.assertRaises(exception.ValidationError, + context.process_request, + req) + + def test_empty_trusted_issuer_list(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_ID'] = uuid.uuid4().hex + req = make_request(environ=env) + self.config_fixture.config(group='tokenless_auth', + trusted_issuer=[]) + context = self._create_context(request=req) + self.assertIsNone(context) + + def test_client_issuer_not_trusted(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.untrusted_client_issuer + env['HTTP_X_PROJECT_ID'] = uuid.uuid4().hex + req = make_request(environ=env) + context = self._create_context(request=req) + self.assertIsNone(context) + + def test_proj_scope_with_proj_id_and_proj_dom_id_success(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_ID'] = self.project_id + env['HTTP_X_PROJECT_DOMAIN_ID'] = self.domain_id + # SSL_CLIENT_USER_NAME and SSL_CLIENT_DOMAIN_NAME are the types + # defined in the mapping that will map to the user name and + # domain name + env['SSL_CLIENT_USER_NAME'] = self.user_name + env['SSL_CLIENT_DOMAIN_NAME'] = self.domain_name + req = make_request(environ=env) + context = self._create_context( + request=req, + mapping_ref=mapping_fixtures.MAPPING_WITH_USERNAME_AND_DOMAINNAME) + self._assert_tokenless_auth_context(context) + + def test_proj_scope_with_proj_id_only_success(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_ID'] = self.project_id + env['SSL_CLIENT_USER_NAME'] = self.user_name + env['SSL_CLIENT_DOMAIN_NAME'] = self.domain_name + req = make_request(environ=env) + context = self._create_context( + request=req, + mapping_ref=mapping_fixtures.MAPPING_WITH_USERNAME_AND_DOMAINNAME) + self._assert_tokenless_auth_context(context) + + def test_proj_scope_with_proj_name_and_proj_dom_id_success(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_NAME'] = self.project_name + env['HTTP_X_PROJECT_DOMAIN_ID'] = self.domain_id + env['SSL_CLIENT_USER_NAME'] = self.user_name + env['SSL_CLIENT_DOMAIN_NAME'] = self.domain_name + req = make_request(environ=env) + context = self._create_context( + request=req, + mapping_ref=mapping_fixtures.MAPPING_WITH_USERNAME_AND_DOMAINNAME) + self._assert_tokenless_auth_context(context) + + def test_proj_scope_with_proj_name_and_proj_dom_name_success(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_NAME'] = self.project_name + env['HTTP_X_PROJECT_DOMAIN_NAME'] = self.domain_name + env['SSL_CLIENT_USER_NAME'] = self.user_name + env['SSL_CLIENT_DOMAIN_NAME'] = self.domain_name + req = make_request(environ=env) + context = self._create_context( + request=req, + mapping_ref=mapping_fixtures.MAPPING_WITH_USERNAME_AND_DOMAINNAME) + self._assert_tokenless_auth_context(context) + + def test_proj_scope_with_proj_name_only_fail(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_NAME'] = self.project_id + env['SSL_CLIENT_USER_NAME'] = self.user_name + env['SSL_CLIENT_DOMAIN_NAME'] = self.domain_name + req = make_request(environ=env) + context = self._create_context( + request=req, + mapping_ref=mapping_fixtures.MAPPING_WITH_USERNAME_AND_DOMAINNAME, + exception_expected=True) + self.assertRaises(exception.ValidationError, + context.process_request, + req) + + def test_mapping_with_userid_and_domainid_success(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_NAME'] = self.project_name + env['HTTP_X_PROJECT_DOMAIN_NAME'] = self.domain_name + env['SSL_CLIENT_USER_ID'] = self.user['id'] + env['SSL_CLIENT_DOMAIN_ID'] = self.domain_id + req = make_request(environ=env) + context = self._create_context( + request=req, + mapping_ref=mapping_fixtures.MAPPING_WITH_USERID_AND_DOMAINID) + self._assert_tokenless_auth_context(context) + + def test_mapping_with_userid_and_domainname_success(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_NAME'] = self.project_name + env['HTTP_X_PROJECT_DOMAIN_NAME'] = self.domain_name + env['SSL_CLIENT_USER_ID'] = self.user['id'] + env['SSL_CLIENT_DOMAIN_NAME'] = self.domain_name + req = make_request(environ=env) + context = self._create_context( + request=req, + mapping_ref=mapping_fixtures.MAPPING_WITH_USERID_AND_DOMAINNAME) + self._assert_tokenless_auth_context(context) + + def test_mapping_with_username_and_domainid_success(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_NAME'] = self.project_name + env['HTTP_X_PROJECT_DOMAIN_NAME'] = self.domain_name + env['SSL_CLIENT_USER_NAME'] = self.user_name + env['SSL_CLIENT_DOMAIN_ID'] = self.domain_id + req = make_request(environ=env) + context = self._create_context( + request=req, + mapping_ref=mapping_fixtures.MAPPING_WITH_USERNAME_AND_DOMAINID) + self._assert_tokenless_auth_context(context) + + def test_only_domain_name_fail(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_ID'] = self.project_id + env['HTTP_X_PROJECT_DOMAIN_ID'] = self.domain_id + env['SSL_CLIENT_DOMAIN_NAME'] = self.domain_name + req = make_request(environ=env) + context = self._create_context( + request=req, + mapping_ref=mapping_fixtures.MAPPING_WITH_DOMAINNAME_ONLY, + exception_expected=True) + self.assertRaises(exception.ValidationError, + context.process_request, + req) + + def test_only_domain_id_fail(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_ID'] = self.project_id + env['HTTP_X_PROJECT_DOMAIN_ID'] = self.domain_id + env['SSL_CLIENT_DOMAIN_ID'] = self.domain_id + req = make_request(environ=env) + context = self._create_context( + request=req, + mapping_ref=mapping_fixtures.MAPPING_WITH_DOMAINID_ONLY, + exception_expected=True) + self.assertRaises(exception.ValidationError, + context.process_request, + req) + + def test_missing_domain_data_fail(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_ID'] = self.project_id + env['HTTP_X_PROJECT_DOMAIN_ID'] = self.domain_id + env['SSL_CLIENT_USER_NAME'] = self.user_name + req = make_request(environ=env) + context = self._create_context( + request=req, + mapping_ref=mapping_fixtures.MAPPING_WITH_USERNAME_ONLY, + exception_expected=True) + self.assertRaises(exception.ValidationError, + context.process_request, + req) + + def test_userid_success(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_ID'] = self.project_id + env['HTTP_X_PROJECT_DOMAIN_ID'] = self.domain_id + env['SSL_CLIENT_USER_ID'] = self.user['id'] + req = make_request(environ=env) + context = self._create_context( + request=req, + mapping_ref=mapping_fixtures.MAPPING_WITH_USERID_ONLY) + self._assert_tokenless_auth_context(context) + + def test_domain_disable_fail(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_NAME'] = self.project_name + env['HTTP_X_PROJECT_DOMAIN_NAME'] = self.domain_name + env['SSL_CLIENT_USER_NAME'] = self.user_name + env['SSL_CLIENT_DOMAIN_ID'] = self.domain_id + req = make_request(environ=env) + self.domain['enabled'] = False + self.domain = self.resource_api.update_domain( + self.domain['id'], self.domain) + context = self._create_context( + request=req, + mapping_ref=mapping_fixtures.MAPPING_WITH_USERNAME_AND_DOMAINID, + exception_expected=True) + self.assertRaises(exception.Unauthorized, + context.process_request, + req) + + def test_user_disable_fail(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_NAME'] = self.project_name + env['HTTP_X_PROJECT_DOMAIN_NAME'] = self.domain_name + env['SSL_CLIENT_USER_NAME'] = self.user_name + env['SSL_CLIENT_DOMAIN_ID'] = self.domain_id + req = make_request(environ=env) + self.user['enabled'] = False + self.user = self.identity_api.update_user(self.user['id'], self.user) + context = self._create_context( + request=req, + mapping_ref=mapping_fixtures.MAPPING_WITH_USERNAME_AND_DOMAINID, + exception_expected=True) + self.assertRaises(AssertionError, + context.process_request, + req) + + def test_invalid_user_fail(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_ID'] = self.project_id + env['HTTP_X_PROJECT_DOMAIN_ID'] = self.domain_id + env['SSL_CLIENT_USER_NAME'] = uuid.uuid4().hex + env['SSL_CLIENT_DOMAIN_NAME'] = self.domain_name + req = make_request(environ=env) + context = self._create_context( + request=req, + mapping_ref=mapping_fixtures.MAPPING_WITH_USERNAME_AND_DOMAINNAME, + exception_expected=True) + self.assertRaises(exception.UserNotFound, + context.process_request, + req) + + def test_ephemeral_success(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_NAME'] = self.project_name + env['HTTP_X_PROJECT_DOMAIN_NAME'] = self.domain_name + env['SSL_CLIENT_USER_NAME'] = self.user_name + req = make_request(environ=env) + self.config_fixture.config(group='tokenless_auth', + protocol='ephemeral') + self.protocol_id = 'ephemeral' + mapping = mapping_fixtures.MAPPING_FOR_EPHEMERAL_USER.copy() + mapping['rules'][0]['local'][0]['group']['id'] = self.group['id'] + context = self._create_context( + request=req, + mapping_ref=mapping) + self._assert_tokenless_auth_context(context, ephemeral_user=True) + + def test_ephemeral_with_default_user_type_success(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_NAME'] = self.project_name + env['HTTP_X_PROJECT_DOMAIN_NAME'] = self.domain_name + env['SSL_CLIENT_USER_NAME'] = self.user_name + req = make_request(environ=env) + self.config_fixture.config(group='tokenless_auth', + protocol='ephemeral') + self.protocol_id = 'ephemeral' + # this mapping does not have the user type defined + # and it should defaults to 'ephemeral' which is + # the expected type for the test case. + mapping = mapping_fixtures.MAPPING_FOR_DEFAULT_EPHEMERAL_USER.copy() + mapping['rules'][0]['local'][0]['group']['id'] = self.group['id'] + context = self._create_context( + request=req, + mapping_ref=mapping) + self._assert_tokenless_auth_context(context, ephemeral_user=True) + + def test_ephemeral_any_user_success(self): + """Ephemeral user does not need a specified user + Keystone is not looking to match the user, but a corresponding group. + """ + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_NAME'] = self.project_name + env['HTTP_X_PROJECT_DOMAIN_NAME'] = self.domain_name + env['SSL_CLIENT_USER_NAME'] = uuid.uuid4().hex + req = make_request(environ=env) + self.config_fixture.config(group='tokenless_auth', + protocol='ephemeral') + self.protocol_id = 'ephemeral' + mapping = mapping_fixtures.MAPPING_FOR_EPHEMERAL_USER.copy() + mapping['rules'][0]['local'][0]['group']['id'] = self.group['id'] + context = self._create_context( + request=req, + mapping_ref=mapping) + self._assert_tokenless_auth_context(context, ephemeral_user=True) + + def test_ephemeral_invalid_scope_fail(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_NAME'] = uuid.uuid4().hex + env['HTTP_X_PROJECT_DOMAIN_NAME'] = uuid.uuid4().hex + env['SSL_CLIENT_USER_NAME'] = self.user_name + req = make_request(environ=env) + self.config_fixture.config(group='tokenless_auth', + protocol='ephemeral') + self.protocol_id = 'ephemeral' + mapping = mapping_fixtures.MAPPING_FOR_EPHEMERAL_USER.copy() + mapping['rules'][0]['local'][0]['group']['id'] = self.group['id'] + context = self._create_context( + request=req, + mapping_ref=mapping, + exception_expected=True) + self.assertRaises(exception.Unauthorized, + context.process_request, + req) + + def test_ephemeral_no_group_found_fail(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_NAME'] = self.project_name + env['HTTP_X_PROJECT_DOMAIN_NAME'] = self.domain_name + env['SSL_CLIENT_USER_NAME'] = self.user_name + req = make_request(environ=env) + self.config_fixture.config(group='tokenless_auth', + protocol='ephemeral') + self.protocol_id = 'ephemeral' + mapping = mapping_fixtures.MAPPING_FOR_EPHEMERAL_USER.copy() + mapping['rules'][0]['local'][0]['group']['id'] = uuid.uuid4().hex + context = self._create_context( + request=req, + mapping_ref=mapping, + exception_expected=True) + self.assertRaises(exception.MappedGroupNotFound, + context.process_request, + req) + + def test_ephemeral_incorrect_mapping_fail(self): + """Ephemeral user picks up the non-ephemeral user mapping. + Looking up the mapping with protocol Id 'x509' will load up + the non-ephemeral user mapping, results unauthenticated. + """ + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_NAME'] = self.project_name + env['HTTP_X_PROJECT_DOMAIN_NAME'] = self.domain_name + env['SSL_CLIENT_USER_NAME'] = self.user_name + req = make_request(environ=env) + # This will pick up the incorrect mapping + self.config_fixture.config(group='tokenless_auth', + protocol='x509') + self.protocol_id = 'x509' + mapping = mapping_fixtures.MAPPING_FOR_EPHEMERAL_USER.copy() + mapping['rules'][0]['local'][0]['group']['id'] = uuid.uuid4().hex + context = self._create_context( + request=req, + mapping_ref=mapping, + exception_expected=True) + self.assertRaises(exception.MappedGroupNotFound, + context.process_request, + req) + + def test_create_idp_id_success(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + auth = tokenless_auth.TokenlessAuthHelper(env) + idp_id = auth._build_idp_id() + self.assertEqual(self.idp_id, idp_id) + + def test_create_idp_id_attri_not_found_fail(self): + env = {} + env[uuid.uuid4().hex] = self.client_issuer + auth = tokenless_auth.TokenlessAuthHelper(env) + expected_msg = ('Could not determine Identity Provider ID. The ' + 'configuration option %s was not found in the ' + 'request environment.' % + CONF.tokenless_auth.issuer_attribute) + # Check the content of the exception message as well + self.assertRaisesRegexp(exception.TokenlessAuthConfigError, + expected_msg, + auth._build_idp_id) diff --git a/keystone-moon/keystone/tests/unit/test_no_admin_token_auth.py b/keystone-moon/keystone/tests/unit/test_no_admin_token_auth.py index 9f67fbd7..bf60cff0 100644 --- a/keystone-moon/keystone/tests/unit/test_no_admin_token_auth.py +++ b/keystone-moon/keystone/tests/unit/test_no_admin_token_auth.py @@ -14,12 +14,13 @@ import os +from six.moves import http_client import webtest -from keystone.tests import unit as tests +from keystone.tests import unit -class TestNoAdminTokenAuth(tests.TestCase): +class TestNoAdminTokenAuth(unit.TestCase): def setUp(self): super(TestNoAdminTokenAuth, self).setUp() self.load_backends() @@ -27,7 +28,7 @@ class TestNoAdminTokenAuth(tests.TestCase): self._generate_paste_config() self.admin_app = webtest.TestApp( - self.loadapp(tests.dirs.tmp('no_admin_token_auth'), name='admin'), + self.loadapp(unit.dirs.tmp('no_admin_token_auth'), name='admin'), extra_environ=dict(REMOTE_ADDR='127.0.0.1')) self.addCleanup(setattr, self, 'admin_app', None) @@ -35,12 +36,12 @@ class TestNoAdminTokenAuth(tests.TestCase): # Generate a file, based on keystone-paste.ini, that doesn't include # admin_token_auth in the pipeline - with open(tests.dirs.etc('keystone-paste.ini'), 'r') as f: + with open(unit.dirs.etc('keystone-paste.ini'), 'r') as f: contents = f.read() new_contents = contents.replace(' admin_token_auth ', ' ') - filename = tests.dirs.tmp('no_admin_token_auth-paste.ini') + filename = unit.dirs.tmp('no_admin_token_auth-paste.ini') with open(filename, 'w') as f: f.write(new_contents) self.addCleanup(os.remove, filename) @@ -56,4 +57,4 @@ class TestNoAdminTokenAuth(tests.TestCase): # If the following does not raise, then the test is successful. self.admin_app.get(REQ_PATH, headers={'X-Auth-Token': 'NotAdminToken'}, - status=401) + status=http_client.UNAUTHORIZED) diff --git a/keystone-moon/keystone/tests/unit/test_policy.py b/keystone-moon/keystone/tests/unit/test_policy.py index 30df0b2b..b2f0e525 100644 --- a/keystone-moon/keystone/tests/unit/test_policy.py +++ b/keystone-moon/keystone/tests/unit/test_policy.py @@ -24,11 +24,11 @@ from testtools import matchers from keystone import exception from keystone.policy.backends import rules -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit.ksfixtures import temporaryfile -class BasePolicyTestCase(tests.TestCase): +class BasePolicyTestCase(unit.TestCase): def setUp(self): super(BasePolicyTestCase, self).setUp() rules.reset() @@ -214,15 +214,15 @@ class DefaultPolicyTestCase(BasePolicyTestCase): self.credentials, "example:noexist", {}) -class PolicyJsonTestCase(tests.TestCase): +class PolicyJsonTestCase(unit.TestCase): def _load_entries(self, filename): return set(json.load(open(filename))) def test_json_examples_have_matching_entries(self): - policy_keys = self._load_entries(tests.dirs.etc('policy.json')) + policy_keys = self._load_entries(unit.dirs.etc('policy.json')) cloud_policy_keys = self._load_entries( - tests.dirs.etc('policy.v3cloudsample.json')) + unit.dirs.etc('policy.v3cloudsample.json')) policy_extra_keys = ['admin_or_token_subject', 'service_admin_or_token_subject', @@ -236,7 +236,7 @@ class PolicyJsonTestCase(tests.TestCase): # All the targets in the sample policy file must be documented in # doc/source/policy_mapping.rst. - policy_keys = self._load_entries(tests.dirs.etc('policy.json')) + policy_keys = self._load_entries(unit.dirs.etc('policy.json')) # These keys are in the policy.json but aren't targets. policy_rule_keys = [ @@ -249,7 +249,7 @@ class PolicyJsonTestCase(tests.TestCase): # targets. doc_path = os.path.join( - tests.ROOTDIR, 'doc', 'source', 'policy_mapping.rst') + unit.ROOTDIR, 'doc', 'source', 'policy_mapping.rst') with open(doc_path) as doc_file: for line in doc_file: if line.startswith('Target'): diff --git a/keystone-moon/keystone/tests/unit/test_revoke.py b/keystone-moon/keystone/tests/unit/test_revoke.py index 5394688c..9062981f 100644 --- a/keystone-moon/keystone/tests/unit/test_revoke.py +++ b/keystone-moon/keystone/tests/unit/test_revoke.py @@ -22,7 +22,7 @@ from testtools import matchers from keystone.common import utils from keystone.contrib.revoke import model from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import test_backend_sql from keystone.token import provider @@ -191,7 +191,7 @@ class SqlRevokeTests(test_backend_sql.SqlTests, RevokeTests): revoke_by_id=False) -class KvsRevokeTests(tests.TestCase, RevokeTests): +class KvsRevokeTests(unit.TestCase, RevokeTests): def config_overrides(self): super(KvsRevokeTests, self).config_overrides() self.config_fixture.config(group='revoke', driver='kvs') @@ -205,7 +205,7 @@ class KvsRevokeTests(tests.TestCase, RevokeTests): self.load_backends() -class RevokeTreeTests(tests.TestCase): +class RevokeTreeTests(unit.TestCase): def setUp(self): super(RevokeTreeTests, self).setUp() self.events = [] diff --git a/keystone-moon/keystone/tests/unit/test_sql_livetest.py b/keystone-moon/keystone/tests/unit/test_sql_livetest.py index 96ee6c70..e2186907 100644 --- a/keystone-moon/keystone/tests/unit/test_sql_livetest.py +++ b/keystone-moon/keystone/tests/unit/test_sql_livetest.py @@ -12,7 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import test_sql_migrate_extensions from keystone.tests.unit import test_sql_upgrade @@ -24,7 +24,7 @@ class PostgresqlMigrateTests(test_sql_upgrade.SqlUpgradeTests): def config_files(self): files = super(PostgresqlMigrateTests, self).config_files() - files.append(tests.dirs.tests_conf("backend_postgresql.conf")) + files.append(unit.dirs.tests_conf("backend_postgresql.conf")) return files @@ -35,7 +35,7 @@ class MysqlMigrateTests(test_sql_upgrade.SqlUpgradeTests): def config_files(self): files = super(MysqlMigrateTests, self).config_files() - files.append(tests.dirs.tests_conf("backend_mysql.conf")) + files.append(unit.dirs.tests_conf("backend_mysql.conf")) return files @@ -47,7 +47,7 @@ class PostgresqlRevokeExtensionsTests( def config_files(self): files = super(PostgresqlRevokeExtensionsTests, self).config_files() - files.append(tests.dirs.tests_conf("backend_postgresql.conf")) + files.append(unit.dirs.tests_conf("backend_postgresql.conf")) return files @@ -58,7 +58,7 @@ class MysqlRevokeExtensionsTests(test_sql_migrate_extensions.RevokeExtension): def config_files(self): files = super(MysqlRevokeExtensionsTests, self).config_files() - files.append(tests.dirs.tests_conf("backend_mysql.conf")) + files.append(unit.dirs.tests_conf("backend_mysql.conf")) return files @@ -69,5 +69,5 @@ class Db2MigrateTests(test_sql_upgrade.SqlUpgradeTests): def config_files(self): files = super(Db2MigrateTests, self).config_files() - files.append(tests.dirs.tests_conf("backend_db2.conf")) + files.append(unit.dirs.tests_conf("backend_db2.conf")) return files diff --git a/keystone-moon/keystone/tests/unit/test_sql_upgrade.py b/keystone-moon/keystone/tests/unit/test_sql_upgrade.py index 96dfa9e8..d617d445 100644 --- a/keystone-moon/keystone/tests/unit/test_sql_upgrade.py +++ b/keystone-moon/keystone/tests/unit/test_sql_upgrade.py @@ -48,7 +48,7 @@ from keystone.common.sql import migration_helpers from keystone.contrib import federation from keystone.contrib import revoke from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import default_fixtures from keystone.tests.unit.ksfixtures import database @@ -124,14 +124,14 @@ EXTENSIONS = {'federation': federation, 'revoke': revoke} -class SqlMigrateBase(tests.SQLDriverOverrides, tests.TestCase): +class SqlMigrateBase(unit.SQLDriverOverrides, unit.TestCase): def initialize_sql(self): self.metadata = sqlalchemy.MetaData() self.metadata.bind = self.engine def config_files(self): config_files = super(SqlMigrateBase, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_sql.conf')) + config_files.append(unit.dirs.tests_conf('backend_sql.conf')) return config_files def repo_package(self): @@ -141,15 +141,15 @@ class SqlMigrateBase(tests.SQLDriverOverrides, tests.TestCase): super(SqlMigrateBase, self).setUp() database.initialize_sql_session() conn_str = CONF.database.connection - if (conn_str != tests.IN_MEM_DB_CONN_STRING and + if (conn_str != unit.IN_MEM_DB_CONN_STRING and conn_str.startswith('sqlite') and - conn_str[10:] == tests.DEFAULT_TEST_DB_FILE): + conn_str[10:] == unit.DEFAULT_TEST_DB_FILE): # Override the default with a DB that is specific to the migration # tests only if the DB Connection string is the same as the global # default. This is required so that no conflicts occur due to the # global default DB already being under migrate control. This is # only needed if the DB is not-in-memory - db_file = tests.dirs.tmp('keystone_migrate_test.db') + db_file = unit.dirs.tmp('keystone_migrate_test.db') self.config_fixture.config( group='database', connection='sqlite:///%s' % db_file) @@ -636,6 +636,13 @@ class SqlUpgradeTests(SqlMigrateBase): 'enabled', 'domain_id', 'parent_id', 'is_domain']) + def test_add_config_registration(self): + config_registration = 'config_register' + self.upgrade(74) + self.assertTableDoesNotExist(config_registration) + self.upgrade(75) + self.assertTableColumns(config_registration, ['type', 'domain_id']) + def populate_user_table(self, with_pass_enab=False, with_pass_enab_domain=False): # Populate the appropriate fields in the user diff --git a/keystone-moon/keystone/tests/unit/test_ssl.py b/keystone-moon/keystone/tests/unit/test_ssl.py index 3b86bb2d..6a6d9ffb 100644 --- a/keystone-moon/keystone/tests/unit/test_ssl.py +++ b/keystone-moon/keystone/tests/unit/test_ssl.py @@ -19,21 +19,21 @@ import ssl from oslo_config import cfg from keystone.common import environment -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit.ksfixtures import appserver CONF = cfg.CONF -CERTDIR = tests.dirs.root('examples', 'pki', 'certs') -KEYDIR = tests.dirs.root('examples', 'pki', 'private') +CERTDIR = unit.dirs.root('examples', 'pki', 'certs') +KEYDIR = unit.dirs.root('examples', 'pki', 'private') CERT = os.path.join(CERTDIR, 'ssl_cert.pem') KEY = os.path.join(KEYDIR, 'ssl_key.pem') CA = os.path.join(CERTDIR, 'cacert.pem') CLIENT = os.path.join(CERTDIR, 'middleware.pem') -class SSLTestCase(tests.TestCase): +class SSLTestCase(unit.TestCase): def setUp(self): super(SSLTestCase, self).setUp() raise self.skipTest('SSL Version and Ciphers cannot be configured ' diff --git a/keystone-moon/keystone/tests/unit/test_token_bind.py b/keystone-moon/keystone/tests/unit/test_token_bind.py index 7dc7ccca..ee4d011a 100644 --- a/keystone-moon/keystone/tests/unit/test_token_bind.py +++ b/keystone-moon/keystone/tests/unit/test_token_bind.py @@ -18,7 +18,7 @@ import uuid from keystone.common import wsgi from keystone import exception from keystone.models import token_model -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import test_token_provider @@ -26,7 +26,7 @@ KERBEROS_BIND = 'USER@REALM' ANY = 'any' -class BindTest(tests.TestCase): +class BindTest(unit.TestCase): """Test binding tokens to a Principal. Even though everything in this file references kerberos the same concepts diff --git a/keystone-moon/keystone/tests/unit/test_token_provider.py b/keystone-moon/keystone/tests/unit/test_token_provider.py index 3ebb0187..f60f7d53 100644 --- a/keystone-moon/keystone/tests/unit/test_token_provider.py +++ b/keystone-moon/keystone/tests/unit/test_token_provider.py @@ -20,7 +20,7 @@ from oslo_utils import timeutils from keystone.common import dependency from keystone.common import utils from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit.ksfixtures import database from keystone import token from keystone.token.providers import fernet @@ -712,7 +712,7 @@ SAMPLE_MALFORMED_TOKEN = { } -class TestTokenProvider(tests.TestCase): +class TestTokenProvider(unit.TestCase): def setUp(self): super(TestTokenProvider, self).setUp() self.useFixture(database.Database()) @@ -817,7 +817,7 @@ class PKIProviderTests(object): token_data) -class TestPKIProviderWithEventlet(PKIProviderTests, tests.TestCase): +class TestPKIProviderWithEventlet(PKIProviderTests, unit.TestCase): def setUp(self): # force keystoneclient.common.cms to use eventlet's subprocess @@ -827,7 +827,7 @@ class TestPKIProviderWithEventlet(PKIProviderTests, tests.TestCase): super(TestPKIProviderWithEventlet, self).setUp() -class TestPKIProviderWithStdlib(PKIProviderTests, tests.TestCase): +class TestPKIProviderWithStdlib(PKIProviderTests, unit.TestCase): def setUp(self): # force keystoneclient.common.cms to use the stdlib subprocess diff --git a/keystone-moon/keystone/tests/unit/test_url_middleware.py b/keystone-moon/keystone/tests/unit/test_url_middleware.py index 1b3872b5..217b302d 100644 --- a/keystone-moon/keystone/tests/unit/test_url_middleware.py +++ b/keystone-moon/keystone/tests/unit/test_url_middleware.py @@ -15,7 +15,7 @@ import webob from keystone import middleware -from keystone.tests import unit as tests +from keystone.tests import unit class FakeApp(object): @@ -26,7 +26,7 @@ class FakeApp(object): return resp(env, start_response) -class UrlMiddlewareTest(tests.TestCase): +class UrlMiddlewareTest(unit.TestCase): def setUp(self): self.middleware = middleware.NormalizingFilter(FakeApp()) self.response_status = None diff --git a/keystone-moon/keystone/tests/unit/test_v2.py b/keystone-moon/keystone/tests/unit/test_v2.py index 415150cf..99b5a897 100644 --- a/keystone-moon/keystone/tests/unit/test_v2.py +++ b/keystone-moon/keystone/tests/unit/test_v2.py @@ -19,6 +19,7 @@ import uuid from keystoneclient.common import cms from oslo_config import cfg import six +from six.moves import http_client from testtools import matchers from keystone.common import extension as keystone_extension @@ -70,13 +71,13 @@ class CoreApiTests(object): def test_public_not_found(self): r = self.public_request( path='/%s' % uuid.uuid4().hex, - expected_status=404) + expected_status=http_client.NOT_FOUND) self.assertValidErrorResponse(r) def test_admin_not_found(self): r = self.admin_request( path='/%s' % uuid.uuid4().hex, - expected_status=404) + expected_status=http_client.NOT_FOUND) self.assertValidErrorResponse(r) def test_public_multiple_choice(self): @@ -107,11 +108,11 @@ class CoreApiTests(object): def test_admin_extensions_404(self): self.admin_request(path='/v2.0/extensions/invalid-extension', - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_public_osksadm_extension_404(self): self.public_request(path='/v2.0/extensions/OS-KSADM', - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_admin_osksadm_extension(self): r = self.admin_request(path='/v2.0/extensions/OS-KSADM') @@ -131,7 +132,7 @@ class CoreApiTests(object): 'tenantId': self.tenant_bar['id'], }, }, - expected_status=200) + expected_status=http_client.OK) self.assertValidAuthenticationResponse(r, require_service_catalog=True) def test_authenticate_unscoped(self): @@ -146,7 +147,7 @@ class CoreApiTests(object): }, }, }, - expected_status=200) + expected_status=http_client.OK) self.assertValidAuthenticationResponse(r) def test_get_tenants_for_token(self): @@ -170,7 +171,7 @@ class CoreApiTests(object): 'token_id': 'invalid', }, token=token, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_validate_token_service_role(self): self.md_foobar = self.assignment_api.add_role_to_user_and_project( @@ -204,7 +205,7 @@ class CoreApiTests(object): r = self.admin_request( path='/v2.0/tokens/%s' % token, token=token, - expected_status=401) + expected_status=http_client.UNAUTHORIZED) def test_validate_token_belongs_to(self): token = self.get_scoped_token() @@ -233,7 +234,7 @@ class CoreApiTests(object): 'token_id': token, }, token=token, - expected_status=200) + expected_status=http_client.OK) def test_endpoints(self): token = self.get_scoped_token() @@ -306,7 +307,7 @@ class CoreApiTests(object): }, }, token=token, - expected_status=400) + expected_status=http_client.BAD_REQUEST) self.assertValidErrorResponse(r) r = self.admin_request( @@ -321,7 +322,7 @@ class CoreApiTests(object): }, }, token=token, - expected_status=400) + expected_status=http_client.BAD_REQUEST) self.assertValidErrorResponse(r) # Test UPDATE request @@ -338,7 +339,7 @@ class CoreApiTests(object): }, }, token=token, - expected_status=400) + expected_status=http_client.BAD_REQUEST) self.assertValidErrorResponse(r) r = self.admin_request( @@ -351,7 +352,7 @@ class CoreApiTests(object): }, }, token=token, - expected_status=400) + expected_status=http_client.BAD_REQUEST) self.assertValidErrorResponse(r) def test_create_update_user_valid_enabled_type(self): @@ -369,11 +370,12 @@ class CoreApiTests(object): }, }, token=token, - expected_status=200) + expected_status=http_client.OK) def test_error_response(self): """This triggers assertValidErrorResponse by convention.""" - self.public_request(path='/v2.0/tenants', expected_status=401) + self.public_request(path='/v2.0/tenants', + expected_status=http_client.UNAUTHORIZED) def test_invalid_parameter_error_response(self): token = self.get_scoped_token() @@ -387,13 +389,13 @@ class CoreApiTests(object): path='/v2.0/OS-KSADM/services', body=bad_body, token=token, - expected_status=400) + expected_status=http_client.BAD_REQUEST) self.assertValidErrorResponse(res) res = self.admin_request(method='POST', path='/v2.0/users', body=bad_body, token=token, - expected_status=400) + expected_status=http_client.BAD_REQUEST) self.assertValidErrorResponse(res) def _get_user_id(self, r): @@ -457,7 +459,7 @@ class CoreApiTests(object): }, }, token=token, - expected_status=200) + expected_status=http_client.OK) user_id = self._get_user_id(r.result) @@ -468,7 +470,7 @@ class CoreApiTests(object): 'user_id': user_id }, token=token, - expected_status=200) + expected_status=http_client.OK) self.assertEqual(CONF.member_role_name, self._get_role_name(r.result)) # Create a new tenant @@ -483,7 +485,7 @@ class CoreApiTests(object): }, }, token=token, - expected_status=200) + expected_status=http_client.OK) project_id = self._get_project_id(r.result) @@ -499,7 +501,7 @@ class CoreApiTests(object): }, }, token=token, - expected_status=200) + expected_status=http_client.OK) # 'member_role' should be in new_tenant r = self.admin_request( @@ -508,7 +510,7 @@ class CoreApiTests(object): 'user_id': user_id }, token=token, - expected_status=200) + expected_status=http_client.OK) self.assertEqual('_member_', self._get_role_name(r.result)) # 'member_role' should not be in tenant_bar any more @@ -518,7 +520,7 @@ class CoreApiTests(object): 'user_id': user_id }, token=token, - expected_status=200) + expected_status=http_client.OK) self.assertNoRoles(r.result) def test_update_user_with_invalid_tenant(self): @@ -537,7 +539,7 @@ class CoreApiTests(object): }, }, token=token, - expected_status=200) + expected_status=http_client.OK) user_id = self._get_user_id(r.result) # Update user with an invalid tenant @@ -552,7 +554,7 @@ class CoreApiTests(object): }, }, token=token, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_update_user_with_invalid_tenant_no_prev_tenant(self): token = self.get_scoped_token() @@ -569,7 +571,7 @@ class CoreApiTests(object): }, }, token=token, - expected_status=200) + expected_status=http_client.OK) user_id = self._get_user_id(r.result) # Update user with an invalid tenant @@ -584,7 +586,7 @@ class CoreApiTests(object): }, }, token=token, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_update_user_with_old_tenant(self): token = self.get_scoped_token() @@ -602,7 +604,7 @@ class CoreApiTests(object): }, }, token=token, - expected_status=200) + expected_status=http_client.OK) user_id = self._get_user_id(r.result) @@ -613,7 +615,7 @@ class CoreApiTests(object): 'user_id': user_id }, token=token, - expected_status=200) + expected_status=http_client.OK) self.assertEqual(CONF.member_role_name, self._get_role_name(r.result)) # Update user's tenant with old tenant id @@ -628,7 +630,7 @@ class CoreApiTests(object): }, }, token=token, - expected_status=200) + expected_status=http_client.OK) # 'member_role' should still be in tenant_bar r = self.admin_request( @@ -637,7 +639,7 @@ class CoreApiTests(object): 'user_id': user_id }, token=token, - expected_status=200) + expected_status=http_client.OK) self.assertEqual('_member_', self._get_role_name(r.result)) def test_authenticating_a_user_with_no_password(self): @@ -669,13 +671,13 @@ class CoreApiTests(object): }, }, }, - expected_status=401) + expected_status=http_client.UNAUTHORIZED) self.assertValidErrorResponse(r) def test_www_authenticate_header(self): r = self.public_request( path='/v2.0/tenants', - expected_status=401) + expected_status=http_client.UNAUTHORIZED) self.assertEqual('Keystone uri="http://localhost"', r.headers.get('WWW-Authenticate')) @@ -684,7 +686,7 @@ class CoreApiTests(object): self.config_fixture.config(public_endpoint=test_url) r = self.public_request( path='/v2.0/tenants', - expected_status=401) + expected_status=http_client.UNAUTHORIZED) self.assertEqual('Keystone uri="%s"' % test_url, r.headers.get('WWW-Authenticate')) @@ -719,7 +721,7 @@ class LegacyV2UsernameTests(object): path='/v2.0/users', token=token, body=body, - expected_status=200) + expected_status=http_client.OK) def test_create_with_extra_username(self): """The response for creating a user will contain the extra fields.""" @@ -770,7 +772,7 @@ class LegacyV2UsernameTests(object): 'enabled': enabled, }, }, - expected_status=200) + expected_status=http_client.OK) self.assertValidUserResponse(r) @@ -800,7 +802,7 @@ class LegacyV2UsernameTests(object): 'enabled': enabled, }, }, - expected_status=200) + expected_status=http_client.OK) self.assertValidUserResponse(r) @@ -879,7 +881,7 @@ class LegacyV2UsernameTests(object): 'enabled': enabled, }, }, - expected_status=200) + expected_status=http_client.OK) self.assertValidUserResponse(r) @@ -909,7 +911,7 @@ class LegacyV2UsernameTests(object): 'enabled': enabled, }, }, - expected_status=200) + expected_status=http_client.OK) self.assertValidUserResponse(r) @@ -929,7 +931,7 @@ class LegacyV2UsernameTests(object): 'enabled': True, }, }, - expected_status=200) + expected_status=http_client.OK) self.assertValidUserResponse(r) @@ -954,7 +956,7 @@ class LegacyV2UsernameTests(object): 'enabled': enabled, }, }, - expected_status=200) + expected_status=http_client.OK) self.assertValidUserResponse(r) @@ -1141,8 +1143,9 @@ class V2TestCase(RestfulTestCase, CoreApiTests, LegacyV2UsernameTests): return r.result['user'][attribute_name] def test_service_crud_requires_auth(self): - """Service CRUD should 401 without an X-Auth-Token (bug 1006822).""" - # values here don't matter because we should 401 before they're checked + """Service CRUD should return unauthorized without an X-Auth-Token.""" + # values here don't matter because it will be unauthorized before + # they're checked (bug 1006822). service_path = '/v2.0/OS-KSADM/services/%s' % uuid.uuid4().hex service_body = { 'OS-KSADM:service': { @@ -1153,41 +1156,43 @@ class V2TestCase(RestfulTestCase, CoreApiTests, LegacyV2UsernameTests): r = self.admin_request(method='GET', path='/v2.0/OS-KSADM/services', - expected_status=401) + expected_status=http_client.UNAUTHORIZED) self.assertValidErrorResponse(r) r = self.admin_request(method='POST', path='/v2.0/OS-KSADM/services', body=service_body, - expected_status=401) + expected_status=http_client.UNAUTHORIZED) self.assertValidErrorResponse(r) r = self.admin_request(method='GET', path=service_path, - expected_status=401) + expected_status=http_client.UNAUTHORIZED) self.assertValidErrorResponse(r) r = self.admin_request(method='DELETE', path=service_path, - expected_status=401) + expected_status=http_client.UNAUTHORIZED) self.assertValidErrorResponse(r) def test_user_role_list_requires_auth(self): - """User role list should 401 without an X-Auth-Token (bug 1006815).""" - # values here don't matter because we should 401 before they're checked + """User role list return unauthorized without an X-Auth-Token.""" + # values here don't matter because it will be unauthorized before + # they're checked (bug 1006815). path = '/v2.0/tenants/%(tenant_id)s/users/%(user_id)s/roles' % { 'tenant_id': uuid.uuid4().hex, 'user_id': uuid.uuid4().hex, } - r = self.admin_request(path=path, expected_status=401) + r = self.admin_request(path=path, + expected_status=http_client.UNAUTHORIZED) self.assertValidErrorResponse(r) def test_fetch_revocation_list_nonadmin_fails(self): self.admin_request( method='GET', path='/v2.0/tokens/revoked', - expected_status=401) + expected_status=http_client.UNAUTHORIZED) def test_fetch_revocation_list_admin_200(self): token = self.get_scoped_token() @@ -1195,7 +1200,7 @@ class V2TestCase(RestfulTestCase, CoreApiTests, LegacyV2UsernameTests): method='GET', path='/v2.0/tokens/revoked', token=token, - expected_status=200) + expected_status=http_client.OK) self.assertValidRevocationListResponse(r) def assertValidRevocationListResponse(self, response): @@ -1226,7 +1231,7 @@ class V2TestCase(RestfulTestCase, CoreApiTests, LegacyV2UsernameTests): method='GET', path='/v2.0/tokens/revoked', token=token1, - expected_status=200) + expected_status=http_client.OK) signed_text = r.result['signed'] data_json = cms.cms_verify(signed_text, CONF.signing.certfile, @@ -1278,7 +1283,7 @@ class V2TestCase(RestfulTestCase, CoreApiTests, LegacyV2UsernameTests): }, }, token=token, - expected_status=400) + expected_status=http_client.BAD_REQUEST) self.assertValidErrorResponse(r) # Test UPDATE request @@ -1294,7 +1299,7 @@ class V2TestCase(RestfulTestCase, CoreApiTests, LegacyV2UsernameTests): }, }, token=token, - expected_status=400) + expected_status=http_client.BAD_REQUEST) self.assertValidErrorResponse(r) def test_authenticating_a_user_with_an_OSKSADM_password(self): @@ -1328,7 +1333,7 @@ class V2TestCase(RestfulTestCase, CoreApiTests, LegacyV2UsernameTests): }, }, }, - expected_status=200) + expected_status=http_client.OK) # ensure password doesn't leak user_id = r.result['user']['id'] @@ -1336,7 +1341,7 @@ class V2TestCase(RestfulTestCase, CoreApiTests, LegacyV2UsernameTests): method='GET', path='/v2.0/users/%s' % user_id, token=token, - expected_status=200) + expected_status=http_client.OK) self.assertNotIn('OS-KSADM:password', r.result['user']) def test_updating_a_user_with_an_OSKSADM_password(self): @@ -1355,7 +1360,7 @@ class V2TestCase(RestfulTestCase, CoreApiTests, LegacyV2UsernameTests): }, }, token=token, - expected_status=200) + expected_status=http_client.OK) # successfully authenticate self.public_request( @@ -1369,7 +1374,7 @@ class V2TestCase(RestfulTestCase, CoreApiTests, LegacyV2UsernameTests): }, }, }, - expected_status=200) + expected_status=http_client.OK) class RevokeApiTestCase(V2TestCase): @@ -1431,7 +1436,7 @@ class TestFernetTokenProviderV2(RestfulTestCase): method='GET', path=path, token=admin_token, - expected_status=200) + expected_status=http_client.OK) def test_authenticate_scoped_token(self): project_ref = self.new_project_ref() @@ -1461,7 +1466,7 @@ class TestFernetTokenProviderV2(RestfulTestCase): method='GET', path=path, token=admin_token, - expected_status=200) + expected_status=http_client.OK) def test_token_authentication_and_validation(self): """Test token authentication for Fernet token provider. @@ -1486,7 +1491,7 @@ class TestFernetTokenProviderV2(RestfulTestCase): } } }, - expected_status=200) + expected_status=http_client.OK) token_id = self._get_token_id(r) path = ('/v2.0/tokens/%s?belongsTo=%s' % (token_id, project_ref['id'])) @@ -1495,7 +1500,7 @@ class TestFernetTokenProviderV2(RestfulTestCase): method='GET', path=path, token=CONF.admin_token, - expected_status=200) + expected_status=http_client.OK) def test_rescoped_tokens_maintain_original_expiration(self): project_ref = self.new_project_ref() @@ -1517,7 +1522,7 @@ class TestFernetTokenProviderV2(RestfulTestCase): }, # NOTE(lbragstad): This test may need to be refactored if Keystone # decides to disallow rescoping using a scoped token. - expected_status=200) + expected_status=http_client.OK) original_token = resp.result['access']['token']['id'] original_expiration = resp.result['access']['token']['expires'] @@ -1532,7 +1537,7 @@ class TestFernetTokenProviderV2(RestfulTestCase): } } }, - expected_status=200) + expected_status=http_client.OK) rescoped_token = resp.result['access']['token']['id'] rescoped_expiration = resp.result['access']['token']['expires'] self.assertNotEqual(original_token, rescoped_token) diff --git a/keystone-moon/keystone/tests/unit/test_v2_controller.py b/keystone-moon/keystone/tests/unit/test_v2_controller.py index 0d4b3cdc..581e6b9c 100644 --- a/keystone-moon/keystone/tests/unit/test_v2_controller.py +++ b/keystone-moon/keystone/tests/unit/test_v2_controller.py @@ -18,7 +18,7 @@ import uuid from keystone.assignment import controllers as assignment_controllers from keystone import exception from keystone.resource import controllers as resource_controllers -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import default_fixtures from keystone.tests.unit.ksfixtures import database @@ -26,7 +26,7 @@ from keystone.tests.unit.ksfixtures import database _ADMIN_CONTEXT = {'is_admin': True, 'query_string': {}} -class TenantTestCase(tests.TestCase): +class TenantTestCase(unit.TestCase): """Tests for the V2 Tenant controller. These tests exercise :class:`keystone.assignment.controllers.Tenant`. diff --git a/keystone-moon/keystone/tests/unit/test_v2_keystoneclient.py b/keystone-moon/keystone/tests/unit/test_v2_keystoneclient.py index e0843605..8d6d9eb7 100644 --- a/keystone-moon/keystone/tests/unit/test_v2_keystoneclient.py +++ b/keystone-moon/keystone/tests/unit/test_v2_keystoneclient.py @@ -22,10 +22,11 @@ import mock from oslo_config import cfg from oslo_serialization import jsonutils from oslo_utils import timeutils +from six.moves import http_client from six.moves import range import webob -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import default_fixtures from keystone.tests.unit.ksfixtures import appserver from keystone.tests.unit.ksfixtures import database @@ -35,11 +36,11 @@ CONF = cfg.CONF DEFAULT_DOMAIN_ID = CONF.identity.default_domain_id -class ClientDrivenTestCase(tests.TestCase): +class ClientDrivenTestCase(unit.TestCase): def config_files(self): config_files = super(ClientDrivenTestCase, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_sql.conf')) + config_files.append(unit.dirs.tests_conf('backend_sql.conf')) return config_files def setUp(self): @@ -1032,7 +1033,8 @@ class ClientDrivenTestCase(tests.TestCase): (new_password, self.user_two['password'])) self.public_server.application(req.environ, responseobject.start_fake_response) - self.assertEqual(403, responseobject.response_status) + self.assertEqual(http_client.FORBIDDEN, + responseobject.response_status) self.user_two['password'] = new_password self.assertRaises(client_exceptions.Unauthorized, @@ -1110,10 +1112,10 @@ class ClientDrivenTestCase(tests.TestCase): if not client: client = self.default_client url = '%s/ec2tokens' % self.default_client.auth_url - (resp, token) = client.request( + resp = client.session.request( url=url, method='POST', - body={'credentials': credentials}) - return resp, token + json={'credentials': credentials}) + return resp, resp.json() def _generate_default_user_ec2_credentials(self): cred = self. default_client.ec2.create( @@ -1135,7 +1137,7 @@ class ClientDrivenTestCase(tests.TestCase): credentials, signature = self._generate_default_user_ec2_credentials() credentials['signature'] = signature resp, token = self._send_ec2_auth_request(credentials) - self.assertEqual(200, resp.status_code) + self.assertEqual(http_client.OK, resp.status_code) self.assertIn('access', token) def test_ec2_auth_success_trust(self): @@ -1167,7 +1169,7 @@ class ClientDrivenTestCase(tests.TestCase): cred.access, cred.secret) credentials['signature'] = signature resp, token = self._send_ec2_auth_request(credentials) - self.assertEqual(200, resp.status_code) + self.assertEqual(http_client.OK, resp.status_code) self.assertEqual(trust_id, token['access']['trust']['id']) # TODO(shardy) we really want to check the roles and trustee # but because of where the stubbing happens we don't seem to diff --git a/keystone-moon/keystone/tests/unit/test_v3.py b/keystone-moon/keystone/tests/unit/test_v3.py index 9bbfa103..7afe6ad8 100644 --- a/keystone-moon/keystone/tests/unit/test_v3.py +++ b/keystone-moon/keystone/tests/unit/test_v3.py @@ -18,7 +18,7 @@ import uuid from oslo_config import cfg from oslo_serialization import jsonutils from oslo_utils import timeutils -import six +from six.moves import http_client from testtools import matchers from keystone import auth @@ -27,14 +27,14 @@ from keystone.common import cache from keystone import exception from keystone import middleware from keystone.policy.backends import rules -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import rest CONF = cfg.CONF DEFAULT_DOMAIN_ID = 'default' -TIME_FORMAT = '%Y-%m-%dT%H:%M:%S.%fZ' +TIME_FORMAT = unit.TIME_FORMAT class AuthTestMixin(object): @@ -116,11 +116,11 @@ class AuthTestMixin(object): return {'auth': auth_data} -class RestfulTestCase(tests.SQLDriverOverrides, rest.RestfulTestCase, +class RestfulTestCase(unit.SQLDriverOverrides, rest.RestfulTestCase, AuthTestMixin): def config_files(self): config_files = super(RestfulTestCase, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_sql.conf')) + config_files.append(unit.dirs.tests_conf('backend_sql.conf')) return config_files def get_extensions(self): @@ -132,7 +132,7 @@ class RestfulTestCase(tests.SQLDriverOverrides, rest.RestfulTestCase, def generate_paste_config(self): new_paste_file = None try: - new_paste_file = tests.generate_paste_config(self.EXTENSION_TO_ADD) + new_paste_file = unit.generate_paste_config(self.EXTENSION_TO_ADD) except AttributeError: # no need to report this error here, as most tests will not have # EXTENSION_TO_ADD defined. @@ -142,7 +142,7 @@ class RestfulTestCase(tests.SQLDriverOverrides, rest.RestfulTestCase, def remove_generated_paste_config(self): try: - tests.remove_generated_paste_config(self.EXTENSION_TO_ADD) + unit.remove_generated_paste_config(self.EXTENSION_TO_ADD) except AttributeError: pass @@ -176,7 +176,7 @@ class RestfulTestCase(tests.SQLDriverOverrides, rest.RestfulTestCase, self.load_sample_data() def _populate_default_domain(self): - if CONF.database.connection == tests.IN_MEM_DB_CONN_STRING: + if CONF.database.connection == unit.IN_MEM_DB_CONN_STRING: # NOTE(morganfainberg): If an in-memory db is being used, be sure # to populate the default domain, this is typically done by # a migration, but the in-mem db uses model definitions to create @@ -265,122 +265,52 @@ class RestfulTestCase(tests.SQLDriverOverrides, rest.RestfulTestCase, self.endpoint['enabled'] = True def new_ref(self): - """Populates a ref with attributes common to all API entities.""" - return { - 'id': uuid.uuid4().hex, - 'name': uuid.uuid4().hex, - 'description': uuid.uuid4().hex, - 'enabled': True} + """Populates a ref with attributes common to some API entities.""" + return unit.new_ref() def new_region_ref(self): - ref = self.new_ref() - # Region doesn't have name or enabled. - del ref['name'] - del ref['enabled'] - ref['parent_region_id'] = None - return ref + return unit.new_region_ref() def new_service_ref(self): - ref = self.new_ref() - ref['type'] = uuid.uuid4().hex - return ref + return unit.new_service_ref() def new_endpoint_ref(self, service_id, interface='public', **kwargs): - ref = self.new_ref() - del ref['enabled'] # enabled is optional - ref['interface'] = interface - ref['service_id'] = service_id - ref['url'] = 'https://' + uuid.uuid4().hex + '.com' - ref['region_id'] = self.region_id - ref.update(kwargs) - return ref + return unit.new_endpoint_ref( + service_id, interface=interface, default_region_id=self.region_id, + **kwargs) def new_domain_ref(self): - ref = self.new_ref() - return ref + return unit.new_domain_ref() def new_project_ref(self, domain_id=None, parent_id=None, is_domain=False): - ref = self.new_ref() - ref['domain_id'] = domain_id - ref['parent_id'] = parent_id - ref['is_domain'] = is_domain - return ref + return unit.new_project_ref(domain_id=domain_id, parent_id=parent_id, + is_domain=is_domain) def new_user_ref(self, domain_id, project_id=None): - ref = self.new_ref() - ref['domain_id'] = domain_id - ref['email'] = uuid.uuid4().hex - ref['password'] = uuid.uuid4().hex - if project_id: - ref['default_project_id'] = project_id - return ref + return unit.new_user_ref(domain_id, project_id=project_id) def new_group_ref(self, domain_id): - ref = self.new_ref() - ref['domain_id'] = domain_id - return ref + return unit.new_group_ref(domain_id) def new_credential_ref(self, user_id, project_id=None, cred_type=None): - ref = dict() - ref['id'] = uuid.uuid4().hex - ref['user_id'] = user_id - if cred_type == 'ec2': - ref['type'] = 'ec2' - ref['blob'] = {'blah': 'test'} - else: - ref['type'] = 'cert' - ref['blob'] = uuid.uuid4().hex - if project_id: - ref['project_id'] = project_id - return ref + return unit.new_credential_ref(user_id, project_id=project_id, + cred_type=cred_type) def new_role_ref(self): - ref = self.new_ref() - # Roles don't have a description or the enabled flag - del ref['description'] - del ref['enabled'] - return ref + return unit.new_role_ref() def new_policy_ref(self): - ref = self.new_ref() - ref['blob'] = uuid.uuid4().hex - ref['type'] = uuid.uuid4().hex - return ref + return unit.new_policy_ref() def new_trust_ref(self, trustor_user_id, trustee_user_id, project_id=None, impersonation=None, expires=None, role_ids=None, role_names=None, remaining_uses=None, allow_redelegation=False): - ref = dict() - ref['id'] = uuid.uuid4().hex - ref['trustor_user_id'] = trustor_user_id - ref['trustee_user_id'] = trustee_user_id - ref['impersonation'] = impersonation or False - ref['project_id'] = project_id - ref['remaining_uses'] = remaining_uses - ref['allow_redelegation'] = allow_redelegation - - if isinstance(expires, six.string_types): - ref['expires_at'] = expires - elif isinstance(expires, dict): - ref['expires_at'] = ( - timeutils.utcnow() + datetime.timedelta(**expires) - ).strftime(TIME_FORMAT) - elif expires is None: - pass - else: - raise NotImplementedError('Unexpected value for "expires"') - - role_ids = role_ids or [] - role_names = role_names or [] - if role_ids or role_names: - ref['roles'] = [] - for role_id in role_ids: - ref['roles'].append({'id': role_id}) - for role_name in role_names: - ref['roles'].append({'name': role_name}) - - return ref + return unit.new_trust_ref( + trustor_user_id, trustee_user_id, project_id=project_id, + impersonation=impersonation, expires=expires, role_ids=role_ids, + role_names=role_names, remaining_uses=remaining_uses, + allow_redelegation=allow_redelegation) def create_new_default_project_for_user(self, user_id, domain_id, enable_project=True): @@ -482,7 +412,7 @@ class RestfulTestCase(tests.SQLDriverOverrides, rest.RestfulTestCase, r = self.v3_authenticate_token(auth) return r.headers.get('X-Subject-Token') - def v3_authenticate_token(self, auth, expected_status=201): + def v3_authenticate_token(self, auth, expected_status=http_client.CREATED): return self.admin_request(method='POST', path='/v3/auth/tokens', body=auth, @@ -511,42 +441,31 @@ class RestfulTestCase(tests.SQLDriverOverrides, rest.RestfulTestCase, return self.admin_request(path=path, token=token, **kwargs) - def get(self, path, **kwargs): - r = self.v3_request(method='GET', path=path, **kwargs) - if 'expected_status' not in kwargs: - self.assertResponseStatus(r, 200) - return r + def get(self, path, expected_status=http_client.OK, **kwargs): + return self.v3_request(path, method='GET', + expected_status=expected_status, **kwargs) - def head(self, path, **kwargs): - r = self.v3_request(method='HEAD', path=path, **kwargs) - if 'expected_status' not in kwargs: - self.assertResponseStatus(r, 204) + def head(self, path, expected_status=http_client.NO_CONTENT, **kwargs): + r = self.v3_request(path, method='HEAD', + expected_status=expected_status, **kwargs) self.assertEqual('', r.body) return r - def post(self, path, **kwargs): - r = self.v3_request(method='POST', path=path, **kwargs) - if 'expected_status' not in kwargs: - self.assertResponseStatus(r, 201) - return r + def post(self, path, expected_status=http_client.CREATED, **kwargs): + return self.v3_request(path, method='POST', + expected_status=expected_status, **kwargs) - def put(self, path, **kwargs): - r = self.v3_request(method='PUT', path=path, **kwargs) - if 'expected_status' not in kwargs: - self.assertResponseStatus(r, 204) - return r + def put(self, path, expected_status=http_client.NO_CONTENT, **kwargs): + return self.v3_request(path, method='PUT', + expected_status=expected_status, **kwargs) - def patch(self, path, **kwargs): - r = self.v3_request(method='PATCH', path=path, **kwargs) - if 'expected_status' not in kwargs: - self.assertResponseStatus(r, 200) - return r + def patch(self, path, expected_status=http_client.OK, **kwargs): + return self.v3_request(path, method='PATCH', + expected_status=expected_status, **kwargs) - def delete(self, path, **kwargs): - r = self.v3_request(method='DELETE', path=path, **kwargs) - if 'expected_status' not in kwargs: - self.assertResponseStatus(r, 204) - return r + def delete(self, path, expected_status=http_client.NO_CONTENT, **kwargs): + return self.v3_request(path, method='DELETE', + expected_status=expected_status, **kwargs) def assertValidErrorResponse(self, r): resp = r.result diff --git a/keystone-moon/keystone/tests/unit/test_v3_assignment.py b/keystone-moon/keystone/tests/unit/test_v3_assignment.py index 03e5d30b..f22e9f2b 100644 --- a/keystone-moon/keystone/tests/unit/test_v3_assignment.py +++ b/keystone-moon/keystone/tests/unit/test_v3_assignment.py @@ -14,11 +14,12 @@ import random import uuid from oslo_config import cfg +from six.moves import http_client from six.moves import range from keystone.common import controller from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import test_v3 from keystone.tests.unit import utils @@ -75,9 +76,10 @@ class AssignmentTestCase(test_v3.RestfulTestCase, body={'domain': ref}) self.assertValidDomainResponse(r, ref) - def test_create_domain_400(self): + def test_create_domain_bad_request(self): """Call ``POST /domains``.""" - self.post('/domains', body={'domain': {}}, expected_status=400) + self.post('/domains', body={'domain': {}}, + expected_status=http_client.BAD_REQUEST) def test_list_domains(self): """Call ``GET /domains``.""" @@ -133,7 +135,8 @@ class AssignmentTestCase(test_v3.RestfulTestCase, } } self.admin_request( - path='/v2.0/tokens', method='POST', body=body, expected_status=401) + path='/v2.0/tokens', method='POST', body=body, + expected_status=http_client.UNAUTHORIZED) auth_data = self.build_authentication_request( user_id=self.user2['id'], @@ -160,21 +163,24 @@ class AssignmentTestCase(test_v3.RestfulTestCase, } } self.admin_request( - path='/v2.0/tokens', method='POST', body=body, expected_status=401) + path='/v2.0/tokens', method='POST', body=body, + expected_status=http_client.UNAUTHORIZED) # Try looking up in v3 by name and id auth_data = self.build_authentication_request( user_id=self.user2['id'], password=self.user2['password'], project_id=self.project2['id']) - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) auth_data = self.build_authentication_request( username=self.user2['name'], user_domain_id=self.domain2['id'], password=self.user2['password'], project_id=self.project2['id']) - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) def test_delete_enabled_domain_fails(self): """Call ``DELETE /domains/{domain_id}`` (when domain enabled).""" @@ -357,20 +363,19 @@ class AssignmentTestCase(test_v3.RestfulTestCase, # validates the returned token and it should be valid. self.head('/auth/tokens', headers={'x-subject-token': subject_token}, - expected_status=200) + expected_status=http_client.OK) # now disable the domain self.domain['enabled'] = False url = "/domains/%(domain_id)s" % {'domain_id': self.domain['id']} self.patch(url, - body={'domain': {'enabled': False}}, - expected_status=200) + body={'domain': {'enabled': False}}) # validates the same token again and it should be 'not found' # as the domain has already been disabled. self.head('/auth/tokens', headers={'x-subject-token': subject_token}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_delete_domain_hierarchy(self): """Call ``DELETE /domains/{domain_id}``.""" @@ -485,14 +490,16 @@ class AssignmentTestCase(test_v3.RestfulTestCase, body={'project': ref}) self.assertValidProjectResponse(r, ref) - def test_create_project_400(self): + def test_create_project_bad_request(self): """Call ``POST /projects``.""" - self.post('/projects', body={'project': {}}, expected_status=400) + self.post('/projects', body={'project': {}}, + expected_status=http_client.BAD_REQUEST) def test_create_project_invalid_domain_id(self): """Call ``POST /projects``.""" ref = self.new_project_ref(domain_id=uuid.uuid4().hex) - self.post('/projects', body={'project': ref}, expected_status=400) + self.post('/projects', body={'project': ref}, + expected_status=http_client.BAD_REQUEST) def test_create_project_is_domain_not_allowed(self): """Call ``POST /projects``. @@ -504,7 +511,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, ref = self.new_project_ref(domain_id=self.domain_id, is_domain=True) self.post('/projects', body={'project': ref}, - expected_status=501) + expected_status=http_client.NOT_IMPLEMENTED) @utils.wip('waiting for projects acting as domains implementation') def test_create_project_without_parent_id_and_without_domain_id(self): @@ -644,18 +651,20 @@ class AssignmentTestCase(test_v3.RestfulTestCase, def test_get_project_with_parents_as_list_with_invalid_id(self): """Call ``GET /projects/{project_id}?parents_as_list``.""" self.get('/projects/%(project_id)s?parents_as_list' % { - 'project_id': None}, expected_status=404) + 'project_id': None}, expected_status=http_client.NOT_FOUND) self.get('/projects/%(project_id)s?parents_as_list' % { - 'project_id': uuid.uuid4().hex}, expected_status=404) + 'project_id': uuid.uuid4().hex}, + expected_status=http_client.NOT_FOUND) def test_get_project_with_subtree_as_list_with_invalid_id(self): """Call ``GET /projects/{project_id}?subtree_as_list``.""" self.get('/projects/%(project_id)s?subtree_as_list' % { - 'project_id': None}, expected_status=404) + 'project_id': None}, expected_status=http_client.NOT_FOUND) self.get('/projects/%(project_id)s?subtree_as_list' % { - 'project_id': uuid.uuid4().hex}, expected_status=404) + 'project_id': uuid.uuid4().hex}, + expected_status=http_client.NOT_FOUND) def test_get_project_with_parents_as_ids(self): """Call ``GET /projects/{project_id}?parents_as_ids``.""" @@ -766,7 +775,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, self.get( '/projects/%(project_id)s?parents_as_list&parents_as_ids' % { 'project_id': projects[1]['project']['id']}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_get_project_with_subtree_as_ids(self): """Call ``GET /projects/{project_id}?subtree_as_ids``. @@ -928,7 +937,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, self.get( '/projects/%(project_id)s?subtree_as_list&subtree_as_ids' % { 'project_id': projects[1]['project']['id']}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_update_project(self): """Call ``PATCH /projects/{project_id}``.""" @@ -965,7 +974,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, '/projects/%(project_id)s' % { 'project_id': leaf_project['id']}, body={'project': leaf_project}, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_update_project_is_domain_not_allowed(self): """Call ``PATCH /projects/{project_id}`` with is_domain. @@ -981,7 +990,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, self.patch('/projects/%(project_id)s' % { 'project_id': resp.result['project']['id']}, body={'project': project}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_disable_leaf_project(self): """Call ``PATCH /projects/{project_id}``.""" @@ -1004,7 +1013,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, '/projects/%(project_id)s' % { 'project_id': root_project['id']}, body={'project': root_project}, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_delete_project(self): """Call ``DELETE /projects/{project_id}`` @@ -1048,7 +1057,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, self.delete( '/projects/%(project_id)s' % { 'project_id': projects[0]['project']['id']}, - expected_status=403) + expected_status=http_client.FORBIDDEN) # Role CRUD tests @@ -1060,9 +1069,10 @@ class AssignmentTestCase(test_v3.RestfulTestCase, body={'role': ref}) return self.assertValidRoleResponse(r, ref) - def test_create_role_400(self): + def test_create_role_bad_request(self): """Call ``POST /roles``.""" - self.post('/roles', body={'role': {}}, expected_status=400) + self.post('/roles', body={'role': {}}, + expected_status=http_client.BAD_REQUEST) def test_list_roles(self): """Call ``GET /roles``.""" @@ -1132,7 +1142,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, """Grant role on a project to a user that doesn't exist, 404 result. When grant a role on a project to a user that doesn't exist, the server - returns 404 Not Found for the user. + returns Not Found for the user. """ @@ -1145,7 +1155,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, 'collection_url': collection_url, 'role_id': self.role_id} - self.put(member_url, expected_status=404) + self.put(member_url, expected_status=http_client.NOT_FOUND) def test_crud_user_domain_role_grants(self): collection_url = ( @@ -1184,7 +1194,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, 'collection_url': collection_url, 'role_id': self.role_id} - self.put(member_url, expected_status=404) + self.put(member_url, expected_status=http_client.NOT_FOUND) def test_crud_group_project_role_grants(self): collection_url = ( @@ -1224,7 +1234,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, 'collection_url': collection_url, 'role_id': self.role_id} - self.put(member_url, expected_status=404) + self.put(member_url, expected_status=http_client.NOT_FOUND) def test_crud_group_domain_role_grants(self): collection_url = ( @@ -1264,7 +1274,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, 'collection_url': collection_url, 'role_id': self.role_id} - self.put(member_url, expected_status=404) + self.put(member_url, expected_status=http_client.NOT_FOUND) def _create_new_user_and_assign_role_on_project(self): """Create a new user and assign user a role on a project.""" @@ -1279,9 +1289,9 @@ class AssignmentTestCase(test_v3.RestfulTestCase, member_url = ('%(collection_url)s/%(role_id)s' % { 'collection_url': collection_url, 'role_id': self.role_id}) - self.put(member_url, expected_status=204) + self.put(member_url) # Check the user has the role assigned - self.head(member_url, expected_status=204) + self.head(member_url) return member_url, user_ref def test_delete_user_before_removing_role_assignment_succeeds(self): @@ -1290,9 +1300,9 @@ class AssignmentTestCase(test_v3.RestfulTestCase, # Delete the user from identity backend self.identity_api.driver.delete_user(user['id']) # Clean up the role assignment - self.delete(member_url, expected_status=204) + self.delete(member_url) # Make sure the role is gone - self.head(member_url, expected_status=404) + self.head(member_url, expected_status=http_client.NOT_FOUND) def test_delete_user_and_check_role_assignment_fails(self): """Call ``DELETE`` on the user and check the role assignment.""" @@ -1301,7 +1311,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, self.identity_api.delete_user(user['id']) # We should get a 404 when looking for the user in the identity # backend because we're not performing a delete operation on the role. - self.head(member_url, expected_status=404) + self.head(member_url, expected_status=http_client.NOT_FOUND) def test_token_revoked_once_group_role_grant_revoked(self): """Test token is revoked when group role grant is revoked @@ -1333,7 +1343,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, # validates the returned token; it should be valid. self.head('/auth/tokens', headers={'x-subject-token': token}, - expected_status=200) + expected_status=http_client.OK) # revokes the grant from group on project. self.assignment_api.delete_grant(role_id=self.role['id'], @@ -1343,7 +1353,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, # validates the same token again; it should not longer be valid. self.head('/auth/tokens', headers={'x-subject-token': token}, - expected_status=404) + expected_status=http_client.NOT_FOUND) # Role Assignments tests @@ -1858,7 +1868,7 @@ class RoleAssignmentBaseTestCase(test_v3.RestfulTestCase, self.default_user_id = self.user_ids[0] self.default_group_id = self.group_ids[0] - def get_role_assignments(self, expected_status=200, **filters): + def get_role_assignments(self, expected_status=http_client.OK, **filters): """Returns the result from querying role assignment API + queried URL. Calls GET /v3/role_assignments?<params> and returns its result, where @@ -1903,24 +1913,24 @@ class RoleAssignmentFailureTestCase(RoleAssignmentBaseTestCase): def test_get_role_assignments_by_domain_and_project(self): self.get_role_assignments(domain_id=self.domain_id, project_id=self.project_id, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_get_role_assignments_by_user_and_group(self): self.get_role_assignments(user_id=self.default_user_id, group_id=self.default_group_id, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_get_role_assignments_by_effective_and_inherited(self): self.config_fixture.config(group='os_inherit', enabled=True) self.get_role_assignments(domain_id=self.domain_id, effective=True, inherited_to_projects=True, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_get_role_assignments_by_effective_and_group(self): self.get_role_assignments(effective=True, group_id=self.default_group_id, - expected_status=400) + expected_status=http_client.BAD_REQUEST) class RoleAssignmentDirectTestCase(RoleAssignmentBaseTestCase): @@ -2193,8 +2203,10 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase, project_id=self.project_id) # Check the user cannot get a domain nor a project token - self.v3_authenticate_token(domain_auth_data, expected_status=401) - self.v3_authenticate_token(project_auth_data, expected_status=401) + self.v3_authenticate_token(domain_auth_data, + expected_status=http_client.UNAUTHORIZED) + self.v3_authenticate_token(project_auth_data, + expected_status=http_client.UNAUTHORIZED) # Grant non-inherited role for user on domain non_inher_ud_link = self.build_role_assignment_link( @@ -2203,7 +2215,8 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase, # Check the user can get only a domain token self.v3_authenticate_token(domain_auth_data) - self.v3_authenticate_token(project_auth_data, expected_status=401) + self.v3_authenticate_token(project_auth_data, + expected_status=http_client.UNAUTHORIZED) # Create inherited role inherited_role = {'id': uuid.uuid4().hex, 'name': 'inherited'} @@ -2224,13 +2237,15 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase, # Check the user can only get a domain token self.v3_authenticate_token(domain_auth_data) - self.v3_authenticate_token(project_auth_data, expected_status=401) + self.v3_authenticate_token(project_auth_data, + expected_status=http_client.UNAUTHORIZED) # Delete non-inherited grant self.delete(non_inher_ud_link) # Check the user cannot get a domain token anymore - self.v3_authenticate_token(domain_auth_data, expected_status=401) + self.v3_authenticate_token(domain_auth_data, + expected_status=http_client.UNAUTHORIZED) def test_get_token_from_inherited_group_domain_role_grants(self): # Create a new group and put a new user in it to @@ -2255,8 +2270,10 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase, project_id=self.project_id) # Check the user cannot get a domain nor a project token - self.v3_authenticate_token(domain_auth_data, expected_status=401) - self.v3_authenticate_token(project_auth_data, expected_status=401) + self.v3_authenticate_token(domain_auth_data, + expected_status=http_client.UNAUTHORIZED) + self.v3_authenticate_token(project_auth_data, + expected_status=http_client.UNAUTHORIZED) # Grant non-inherited role for user on domain non_inher_gd_link = self.build_role_assignment_link( @@ -2265,7 +2282,8 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase, # Check the user can get only a domain token self.v3_authenticate_token(domain_auth_data) - self.v3_authenticate_token(project_auth_data, expected_status=401) + self.v3_authenticate_token(project_auth_data, + expected_status=http_client.UNAUTHORIZED) # Create inherited role inherited_role = {'id': uuid.uuid4().hex, 'name': 'inherited'} @@ -2286,13 +2304,15 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase, # Check the user can only get a domain token self.v3_authenticate_token(domain_auth_data) - self.v3_authenticate_token(project_auth_data, expected_status=401) + self.v3_authenticate_token(project_auth_data, + expected_status=http_client.UNAUTHORIZED) # Delete non-inherited grant self.delete(non_inher_gd_link) # Check the user cannot get a domain token anymore - self.v3_authenticate_token(domain_auth_data, expected_status=401) + self.v3_authenticate_token(domain_auth_data, + expected_status=http_client.UNAUTHORIZED) def _test_crud_inherited_and_direct_assignment_on_target(self, target_url): # Create a new role to avoid assignments loaded from sample data @@ -2308,7 +2328,7 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase, self.put(direct_url) # Check the direct assignment exists, but the inherited one does not self.head(direct_url) - self.head(inherited_url, expected_status=404) + self.head(inherited_url, expected_status=http_client.NOT_FOUND) # Now add the inherited assignment self.put(inherited_url) @@ -2320,13 +2340,13 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase, self.delete(inherited_url) # Check the direct assignment exists, but the inherited one does not self.head(direct_url) - self.head(inherited_url, expected_status=404) + self.head(inherited_url, expected_status=http_client.NOT_FOUND) # Now delete the inherited assignment self.delete(direct_url) # Check that none of them exist - self.head(direct_url, expected_status=404) - self.head(inherited_url, expected_status=404) + self.head(direct_url, expected_status=http_client.NOT_FOUND) + self.head(inherited_url, expected_status=http_client.NOT_FOUND) def test_crud_inherited_and_direct_assignment_on_domains(self): self._test_crud_inherited_and_direct_assignment_on_target( @@ -2801,8 +2821,10 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase, project_id=leaf_id) # Check the user cannot get a token on root nor leaf project - self.v3_authenticate_token(root_project_auth_data, expected_status=401) - self.v3_authenticate_token(leaf_project_auth_data, expected_status=401) + self.v3_authenticate_token(root_project_auth_data, + expected_status=http_client.UNAUTHORIZED) + self.v3_authenticate_token(leaf_project_auth_data, + expected_status=http_client.UNAUTHORIZED) # Grant non-inherited role for user on leaf project non_inher_up_link = self.build_role_assignment_link( @@ -2811,7 +2833,8 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase, self.put(non_inher_up_link) # Check the user can only get a token on leaf project - self.v3_authenticate_token(root_project_auth_data, expected_status=401) + self.v3_authenticate_token(root_project_auth_data, + expected_status=http_client.UNAUTHORIZED) self.v3_authenticate_token(leaf_project_auth_data) # Grant inherited role for user on root project @@ -2821,21 +2844,24 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase, self.put(inher_up_link) # Check the user still can get a token only on leaf project - self.v3_authenticate_token(root_project_auth_data, expected_status=401) + self.v3_authenticate_token(root_project_auth_data, + expected_status=http_client.UNAUTHORIZED) self.v3_authenticate_token(leaf_project_auth_data) # Delete non-inherited grant self.delete(non_inher_up_link) # Check the inherited role still applies for leaf project - self.v3_authenticate_token(root_project_auth_data, expected_status=401) + self.v3_authenticate_token(root_project_auth_data, + expected_status=http_client.UNAUTHORIZED) self.v3_authenticate_token(leaf_project_auth_data) # Delete inherited grant self.delete(inher_up_link) # Check the user cannot get a token on leaf project anymore - self.v3_authenticate_token(leaf_project_auth_data, expected_status=401) + self.v3_authenticate_token(leaf_project_auth_data, + expected_status=http_client.UNAUTHORIZED) def test_get_token_from_inherited_group_project_role_grants(self): # Create default scenario @@ -2858,8 +2884,10 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase, project_id=leaf_id) # Check the user cannot get a token on root nor leaf project - self.v3_authenticate_token(root_project_auth_data, expected_status=401) - self.v3_authenticate_token(leaf_project_auth_data, expected_status=401) + self.v3_authenticate_token(root_project_auth_data, + expected_status=http_client.UNAUTHORIZED) + self.v3_authenticate_token(leaf_project_auth_data, + expected_status=http_client.UNAUTHORIZED) # Grant non-inherited role for group on leaf project non_inher_gp_link = self.build_role_assignment_link( @@ -2868,7 +2896,8 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase, self.put(non_inher_gp_link) # Check the user can only get a token on leaf project - self.v3_authenticate_token(root_project_auth_data, expected_status=401) + self.v3_authenticate_token(root_project_auth_data, + expected_status=http_client.UNAUTHORIZED) self.v3_authenticate_token(leaf_project_auth_data) # Grant inherited role for group on root project @@ -2878,7 +2907,8 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase, self.put(inher_gp_link) # Check the user still can get a token only on leaf project - self.v3_authenticate_token(root_project_auth_data, expected_status=401) + self.v3_authenticate_token(root_project_auth_data, + expected_status=http_client.UNAUTHORIZED) self.v3_authenticate_token(leaf_project_auth_data) # Delete no-inherited grant @@ -2891,7 +2921,8 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase, self.delete(inher_gp_link) # Check the user cannot get a token on leaf project anymore - self.v3_authenticate_token(leaf_project_auth_data, expected_status=401) + self.v3_authenticate_token(leaf_project_auth_data, + expected_status=http_client.UNAUTHORIZED) def test_get_role_assignments_for_project_hierarchy(self): """Call ``GET /role_assignments``. @@ -3069,13 +3100,13 @@ class AssignmentInheritanceDisabledTestCase(test_v3.RestfulTestCase): 'role_id': role['id']} collection_url = base_collection_url + '/inherited_to_projects' - self.put(member_url, expected_status=404) - self.head(member_url, expected_status=404) - self.get(collection_url, expected_status=404) - self.delete(member_url, expected_status=404) + self.put(member_url, expected_status=http_client.NOT_FOUND) + self.head(member_url, expected_status=http_client.NOT_FOUND) + self.get(collection_url, expected_status=http_client.NOT_FOUND) + self.delete(member_url, expected_status=http_client.NOT_FOUND) -class AssignmentV3toV2MethodsTestCase(tests.TestCase): +class AssignmentV3toV2MethodsTestCase(unit.TestCase): """Test domain V3 to V2 conversion methods.""" def _setup_initial_projects(self): self.project_id = uuid.uuid4().hex diff --git a/keystone-moon/keystone/tests/unit/test_v3_auth.py b/keystone-moon/keystone/tests/unit/test_v3_auth.py index 96f0ff1f..496a75c0 100644 --- a/keystone-moon/keystone/tests/unit/test_v3_auth.py +++ b/keystone-moon/keystone/tests/unit/test_v3_auth.py @@ -22,6 +22,7 @@ from keystoneclient.common import cms import mock from oslo_config import cfg from oslo_utils import timeutils +from six.moves import http_client from six.moves import range from testtools import matchers from testtools import testcase @@ -30,7 +31,7 @@ from keystone import auth from keystone.common import utils from keystone import exception from keystone.policy.backends import rules -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import ksfixtures from keystone.tests.unit import test_v3 @@ -141,7 +142,7 @@ class TokenAPITests(object): path='/v2.0/tokens/%s' % v3_token, token=CONF.admin_token, method='GET', - expected_status=401) + expected_status=http_client.UNAUTHORIZED) def test_v3_v2_intermix_new_default_domain(self): # If the default_domain_id config option is changed, then should be @@ -199,7 +200,7 @@ class TokenAPITests(object): method='GET', path='/v2.0/tokens/%s' % v3_token, token=CONF.admin_token, - expected_status=401) + expected_status=http_client.UNAUTHORIZED) def test_v3_v2_intermix_non_default_project_failed(self): # self.project is in a non-default domain @@ -213,7 +214,7 @@ class TokenAPITests(object): method='GET', path='/v2.0/tokens/%s' % v3_token, token=CONF.admin_token, - expected_status=401) + expected_status=http_client.UNAUTHORIZED) def test_v3_v2_intermix_non_default_user_failed(self): self.assignment_api.create_grant( @@ -232,7 +233,7 @@ class TokenAPITests(object): method='GET', path='/v2.0/tokens/%s' % v3_token, token=CONF.admin_token, - expected_status=401) + expected_status=http_client.UNAUTHORIZED) def test_v3_v2_intermix_domain_scope_failed(self): self.assignment_api.create_grant( @@ -250,7 +251,7 @@ class TokenAPITests(object): path='/v2.0/tokens/%s' % v3_token, token=CONF.admin_token, method='GET', - expected_status=401) + expected_status=http_client.UNAUTHORIZED) def test_v3_v2_unscoped_token_intermix(self): r = self.v3_authenticate_token(self.build_authentication_request( @@ -383,14 +384,13 @@ class TokenAPITests(object): v2_token = r.result['access']['token']['id'] # Delete the v2 token using v3. - resp = self.delete( + self.delete( '/auth/tokens', headers={'X-Subject-Token': v2_token}) - self.assertEqual(resp.status_code, 204) # Attempting to use the deleted token on v2 should fail. self.admin_request( path='/v2.0/tenants', method='GET', token=v2_token, - expected_status=401) + expected_status=http_client.UNAUTHORIZED) def test_rescoping_token(self): expires = self.v3_token_data['token']['expires_at'] @@ -405,7 +405,8 @@ class TokenAPITests(object): self.assertEqual(expires, r.result['token']['expires_at']) def test_check_token(self): - self.head('/auth/tokens', headers=self.headers, expected_status=200) + self.head('/auth/tokens', headers=self.headers, + expected_status=http_client.OK) def test_validate_token(self): r = self.get('/auth/tokens', headers=self.headers) @@ -434,7 +435,7 @@ class AllowRescopeScopedTokenDisabledTests(test_v3.RestfulTestCase): self.build_authentication_request( token=self.get_scoped_token(), project_id=self.project_id), - expected_status=403) + expected_status=http_client.FORBIDDEN) def _v2_token(self): body = { @@ -460,7 +461,7 @@ class AllowRescopeScopedTokenDisabledTests(test_v3.RestfulTestCase): self.admin_request(path='/v2.0/tokens', method='POST', body=body, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_rescoping_v2_to_v3_disabled(self): token = self._v2_token() @@ -468,7 +469,7 @@ class AllowRescopeScopedTokenDisabledTests(test_v3.RestfulTestCase): self.build_authentication_request( token=token['access']['token']['id'], project_id=self.project_id), - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_rescoping_v3_to_v2_disabled(self): token = {'id': self.get_scoped_token()} @@ -498,7 +499,7 @@ class AllowRescopeScopedTokenDisabledTests(test_v3.RestfulTestCase): self.build_authentication_request( token=domain_scoped_token, project_id=self.project_id), - expected_status=403) + expected_status=http_client.FORBIDDEN) class TestPKITokenAPIs(test_v3.RestfulTestCase, TokenAPITests): @@ -637,7 +638,7 @@ class TestTokenRevokeSelfAndAdmin(test_v3.RestfulTestCase): super(TestTokenRevokeSelfAndAdmin, self).config_overrides() self.config_fixture.config( group='oslo_policy', - policy_file=tests.dirs.etc('policy.v3cloudsample.json')) + policy_file=unit.dirs.etc('policy.v3cloudsample.json')) def test_user_revokes_own_token(self): user_token = self.get_requested_token( @@ -654,23 +655,29 @@ class TestTokenRevokeSelfAndAdmin(test_v3.RestfulTestCase): password=self.userAdminA['password'], domain_name=self.domainA['name'])) - self.head('/auth/tokens', headers=headers, expected_status=200, + self.head('/auth/tokens', headers=headers, + expected_status=http_client.OK, token=adminA_token) - self.head('/auth/tokens', headers=headers, expected_status=200, + self.head('/auth/tokens', headers=headers, + expected_status=http_client.OK, token=user_token) - self.delete('/auth/tokens', headers=headers, expected_status=204, + self.delete('/auth/tokens', headers=headers, token=user_token) - # invalid X-Auth-Token and invalid X-Subject-Token (401) - self.head('/auth/tokens', headers=headers, expected_status=401, + # invalid X-Auth-Token and invalid X-Subject-Token + self.head('/auth/tokens', headers=headers, + expected_status=http_client.UNAUTHORIZED, token=user_token) - # invalid X-Auth-Token and invalid X-Subject-Token (401) - self.delete('/auth/tokens', headers=headers, expected_status=401, + # invalid X-Auth-Token and invalid X-Subject-Token + self.delete('/auth/tokens', headers=headers, + expected_status=http_client.UNAUTHORIZED, token=user_token) - # valid X-Auth-Token and invalid X-Subject-Token (404) - self.delete('/auth/tokens', headers=headers, expected_status=404, + # valid X-Auth-Token and invalid X-Subject-Token + self.delete('/auth/tokens', headers=headers, + expected_status=http_client.NOT_FOUND, token=adminA_token) - # valid X-Auth-Token and invalid X-Subject-Token (404) - self.head('/auth/tokens', headers=headers, expected_status=404, + # valid X-Auth-Token and invalid X-Subject-Token + self.head('/auth/tokens', headers=headers, + expected_status=http_client.NOT_FOUND, token=adminA_token) def test_adminA_revokes_userA_token(self): @@ -688,20 +695,25 @@ class TestTokenRevokeSelfAndAdmin(test_v3.RestfulTestCase): password=self.userAdminA['password'], domain_name=self.domainA['name'])) - self.head('/auth/tokens', headers=headers, expected_status=200, + self.head('/auth/tokens', headers=headers, + expected_status=http_client.OK, token=adminA_token) - self.head('/auth/tokens', headers=headers, expected_status=200, + self.head('/auth/tokens', headers=headers, + expected_status=http_client.OK, token=user_token) - self.delete('/auth/tokens', headers=headers, expected_status=204, + self.delete('/auth/tokens', headers=headers, token=adminA_token) - # invalid X-Auth-Token and invalid X-Subject-Token (401) - self.head('/auth/tokens', headers=headers, expected_status=401, + # invalid X-Auth-Token and invalid X-Subject-Token + self.head('/auth/tokens', headers=headers, + expected_status=http_client.UNAUTHORIZED, token=user_token) - # valid X-Auth-Token and invalid X-Subject-Token (404) - self.delete('/auth/tokens', headers=headers, expected_status=404, + # valid X-Auth-Token and invalid X-Subject-Token + self.delete('/auth/tokens', headers=headers, + expected_status=http_client.NOT_FOUND, token=adminA_token) - # valid X-Auth-Token and invalid X-Subject-Token (404) - self.head('/auth/tokens', headers=headers, expected_status=404, + # valid X-Auth-Token and invalid X-Subject-Token + self.head('/auth/tokens', headers=headers, + expected_status=http_client.NOT_FOUND, token=adminA_token) def test_adminB_fails_revoking_userA_token(self): @@ -729,9 +741,11 @@ class TestTokenRevokeSelfAndAdmin(test_v3.RestfulTestCase): password=self.userAdminB['password'], domain_name=self.domainB['name'])) - self.head('/auth/tokens', headers=headers, expected_status=403, + self.head('/auth/tokens', headers=headers, + expected_status=http_client.FORBIDDEN, token=adminB_token) - self.delete('/auth/tokens', headers=headers, expected_status=403, + self.delete('/auth/tokens', headers=headers, + expected_status=http_client.FORBIDDEN, token=adminB_token) @@ -854,10 +868,10 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # confirm both tokens are valid self.head('/auth/tokens', headers={'X-Subject-Token': unscoped_token}, - expected_status=200) + expected_status=http_client.OK) self.head('/auth/tokens', headers={'X-Subject-Token': scoped_token}, - expected_status=200) + expected_status=http_client.OK) # create a new role role = self.new_role_ref() @@ -873,10 +887,10 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # both tokens should remain valid self.head('/auth/tokens', headers={'X-Subject-Token': unscoped_token}, - expected_status=200) + expected_status=http_client.OK) self.head('/auth/tokens', headers={'X-Subject-Token': scoped_token}, - expected_status=200) + expected_status=http_client.OK) def test_deleting_user_grant_revokes_token(self): """Test deleting a user grant revokes token. @@ -896,7 +910,7 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # Confirm token is valid self.head('/auth/tokens', headers={'X-Subject-Token': token}, - expected_status=200) + expected_status=http_client.OK) # Delete the grant, which should invalidate the token grant_url = ( '/projects/%(project_id)s/users/%(user_id)s/' @@ -907,7 +921,7 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): self.delete(grant_url) self.head('/auth/tokens', headers={'X-Subject-Token': token}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def role_data_fixtures(self): self.projectC = self.new_project_ref(domain_id=self.domainA['id']) @@ -998,19 +1012,19 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # Confirm tokens are valid self.head('/auth/tokens', headers={'X-Subject-Token': tokenA}, - expected_status=200) + expected_status=http_client.OK) self.head('/auth/tokens', headers={'X-Subject-Token': tokenB}, - expected_status=200) + expected_status=http_client.OK) self.head('/auth/tokens', headers={'X-Subject-Token': tokenC}, - expected_status=200) + expected_status=http_client.OK) self.head('/auth/tokens', headers={'X-Subject-Token': tokenD}, - expected_status=200) + expected_status=http_client.OK) self.head('/auth/tokens', headers={'X-Subject-Token': tokenE}, - expected_status=200) + expected_status=http_client.OK) # Delete the role, which should invalidate the tokens role_url = '/roles/%s' % self.role1['id'] @@ -1019,21 +1033,21 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # Check the tokens that used role1 is invalid self.head('/auth/tokens', headers={'X-Subject-Token': tokenA}, - expected_status=404) + expected_status=http_client.NOT_FOUND) self.head('/auth/tokens', headers={'X-Subject-Token': tokenB}, - expected_status=404) + expected_status=http_client.NOT_FOUND) self.head('/auth/tokens', headers={'X-Subject-Token': tokenD}, - expected_status=404) + expected_status=http_client.NOT_FOUND) self.head('/auth/tokens', headers={'X-Subject-Token': tokenE}, - expected_status=404) + expected_status=http_client.NOT_FOUND) # ...but the one using role2 is still valid self.head('/auth/tokens', headers={'X-Subject-Token': tokenC}, - expected_status=200) + expected_status=http_client.OK) def test_domain_user_role_assignment_maintains_token(self): """Test user-domain role assignment maintains existing token. @@ -1053,7 +1067,7 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # Confirm token is valid self.head('/auth/tokens', headers={'X-Subject-Token': token}, - expected_status=200) + expected_status=http_client.OK) # Assign a role, which should not affect the token grant_url = ( '/domains/%(domain_id)s/users/%(user_id)s/' @@ -1064,7 +1078,7 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): self.put(grant_url) self.head('/auth/tokens', headers={'X-Subject-Token': token}, - expected_status=200) + expected_status=http_client.OK) def test_disabling_project_revokes_token(self): token = self.get_requested_token( @@ -1076,7 +1090,7 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # confirm token is valid self.head('/auth/tokens', headers={'X-Subject-Token': token}, - expected_status=200) + expected_status=http_client.OK) # disable the project, which should invalidate the token self.patch( @@ -1086,13 +1100,13 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # user should no longer have access to the project self.head('/auth/tokens', headers={'X-Subject-Token': token}, - expected_status=404) + expected_status=http_client.NOT_FOUND) self.v3_authenticate_token( self.build_authentication_request( user_id=self.user3['id'], password=self.user3['password'], project_id=self.projectA['id']), - expected_status=401) + expected_status=http_client.UNAUTHORIZED) def test_deleting_project_revokes_token(self): token = self.get_requested_token( @@ -1104,7 +1118,7 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # confirm token is valid self.head('/auth/tokens', headers={'X-Subject-Token': token}, - expected_status=200) + expected_status=http_client.OK) # delete the project, which should invalidate the token self.delete( @@ -1113,13 +1127,13 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # user should no longer have access to the project self.head('/auth/tokens', headers={'X-Subject-Token': token}, - expected_status=404) + expected_status=http_client.NOT_FOUND) self.v3_authenticate_token( self.build_authentication_request( user_id=self.user3['id'], password=self.user3['password'], project_id=self.projectA['id']), - expected_status=401) + expected_status=http_client.UNAUTHORIZED) def test_deleting_group_grant_revokes_tokens(self): """Test deleting a group grant revokes tokens. @@ -1153,13 +1167,13 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # Confirm tokens are valid self.head('/auth/tokens', headers={'X-Subject-Token': token1}, - expected_status=200) + expected_status=http_client.OK) self.head('/auth/tokens', headers={'X-Subject-Token': token2}, - expected_status=200) + expected_status=http_client.OK) self.head('/auth/tokens', headers={'X-Subject-Token': token3}, - expected_status=200) + expected_status=http_client.OK) # Delete the group grant, which should invalidate the # tokens for user1 and user2 grant_url = ( @@ -1171,15 +1185,15 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): self.delete(grant_url) self.head('/auth/tokens', headers={'X-Subject-Token': token1}, - expected_status=404) + expected_status=http_client.NOT_FOUND) self.head('/auth/tokens', headers={'X-Subject-Token': token2}, - expected_status=404) + expected_status=http_client.NOT_FOUND) # But user3's token should be invalid too as revocation is done for # scope role & project self.head('/auth/tokens', headers={'X-Subject-Token': token3}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_domain_group_role_assignment_maintains_token(self): """Test domain-group role assignment maintains existing token. @@ -1199,7 +1213,7 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # Confirm token is valid self.head('/auth/tokens', headers={'X-Subject-Token': token}, - expected_status=200) + expected_status=http_client.OK) # Delete the grant, which should invalidate the token grant_url = ( '/domains/%(domain_id)s/groups/%(group_id)s/' @@ -1210,7 +1224,7 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): self.put(grant_url) self.head('/auth/tokens', headers={'X-Subject-Token': token}, - expected_status=200) + expected_status=http_client.OK) def test_group_membership_changes_revokes_token(self): """Test add/removal to/from group revokes token. @@ -1240,10 +1254,10 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # Confirm tokens are valid self.head('/auth/tokens', headers={'X-Subject-Token': token1}, - expected_status=200) + expected_status=http_client.OK) self.head('/auth/tokens', headers={'X-Subject-Token': token2}, - expected_status=200) + expected_status=http_client.OK) # Remove user1 from group1, which should invalidate # the token self.delete('/groups/%(group_id)s/users/%(user_id)s' % { @@ -1251,18 +1265,18 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): 'user_id': self.user1['id']}) self.head('/auth/tokens', headers={'X-Subject-Token': token1}, - expected_status=404) + expected_status=http_client.NOT_FOUND) # But user2's token should still be valid self.head('/auth/tokens', headers={'X-Subject-Token': token2}, - expected_status=200) + expected_status=http_client.OK) # Adding user2 to a group should not invalidate token self.put('/groups/%(group_id)s/users/%(user_id)s' % { 'group_id': self.group2['id'], 'user_id': self.user2['id']}) self.head('/auth/tokens', headers={'X-Subject-Token': token2}, - expected_status=200) + expected_status=http_client.OK) def test_removing_role_assignment_does_not_affect_other_users(self): """Revoking a role from one user should not affect other users.""" @@ -1295,18 +1309,18 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # authorization for the first user should now fail self.head('/auth/tokens', headers={'X-Subject-Token': user1_token}, - expected_status=404) + expected_status=http_client.NOT_FOUND) self.v3_authenticate_token( self.build_authentication_request( user_id=self.user1['id'], password=self.user1['password'], project_id=self.projectA['id']), - expected_status=401) + expected_status=http_client.UNAUTHORIZED) # authorization for the second user should still succeed self.head('/auth/tokens', headers={'X-Subject-Token': user3_token}, - expected_status=200) + expected_status=http_client.OK) self.v3_authenticate_token( self.build_authentication_request( user_id=self.user3['id'], @@ -1329,7 +1343,7 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): '/projects/%(project_id)s' % {'project_id': self.projectA['id']}) # Make sure that we get a NotFound(404) when heading that role. - self.head(role_path, expected_status=404) + self.head(role_path, expected_status=http_client.NOT_FOUND) def get_v2_token(self, token=None, project_id=None): body = {'auth': {}, } @@ -1356,12 +1370,11 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): token = self.get_v2_token() self.delete('/auth/tokens', - headers={'X-Subject-Token': token}, - expected_status=204) + headers={'X-Subject-Token': token}) self.head('/auth/tokens', headers={'X-Subject-Token': token}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_revoke_token_from_token(self): # Test that a scoped token can be requested from an unscoped token, @@ -1387,38 +1400,36 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # revoke the project-scoped token. self.delete('/auth/tokens', - headers={'X-Subject-Token': project_scoped_token}, - expected_status=204) + headers={'X-Subject-Token': project_scoped_token}) # The project-scoped token is invalidated. self.head('/auth/tokens', headers={'X-Subject-Token': project_scoped_token}, - expected_status=404) + expected_status=http_client.NOT_FOUND) # The unscoped token should still be valid. self.head('/auth/tokens', headers={'X-Subject-Token': unscoped_token}, - expected_status=200) + expected_status=http_client.OK) # The domain-scoped token should still be valid. self.head('/auth/tokens', headers={'X-Subject-Token': domain_scoped_token}, - expected_status=200) + expected_status=http_client.OK) # revoke the domain-scoped token. self.delete('/auth/tokens', - headers={'X-Subject-Token': domain_scoped_token}, - expected_status=204) + headers={'X-Subject-Token': domain_scoped_token}) # The domain-scoped token is invalid. self.head('/auth/tokens', headers={'X-Subject-Token': domain_scoped_token}, - expected_status=404) + expected_status=http_client.NOT_FOUND) # The unscoped token should still be valid. self.head('/auth/tokens', headers={'X-Subject-Token': unscoped_token}, - expected_status=200) + expected_status=http_client.OK) def test_revoke_token_from_token_v2(self): # Test that a scoped token can be requested from an unscoped token, @@ -1436,18 +1447,17 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # revoke the project-scoped token. self.delete('/auth/tokens', - headers={'X-Subject-Token': project_scoped_token}, - expected_status=204) + headers={'X-Subject-Token': project_scoped_token}) # The project-scoped token is invalidated. self.head('/auth/tokens', headers={'X-Subject-Token': project_scoped_token}, - expected_status=404) + expected_status=http_client.NOT_FOUND) # The unscoped token should still be valid. self.head('/auth/tokens', headers={'X-Subject-Token': unscoped_token}, - expected_status=200) + expected_status=http_client.OK) class TestTokenRevokeByAssignment(TestTokenRevokeById): @@ -1491,11 +1501,11 @@ class TestTokenRevokeByAssignment(TestTokenRevokeById): # authorization for the projectA should still succeed self.head('/auth/tokens', headers={'X-Subject-Token': other_project_token}, - expected_status=200) + expected_status=http_client.OK) # while token for the projectB should not self.head('/auth/tokens', headers={'X-Subject-Token': project_token}, - expected_status=404) + expected_status=http_client.NOT_FOUND) revoked_tokens = [ t['id'] for t in self.token_provider_api.list_revoked_tokens()] # token is in token revocation list @@ -1553,57 +1563,53 @@ class TestTokenRevokeApi(TestTokenRevokeById): def test_revoke_token(self): scoped_token = self.get_scoped_token() headers = {'X-Subject-Token': scoped_token} - response = self.get('/auth/tokens', headers=headers, - expected_status=200).json_body['token'] + response = self.get('/auth/tokens', headers=headers).json_body['token'] - self.delete('/auth/tokens', headers=headers, expected_status=204) - self.head('/auth/tokens', headers=headers, expected_status=404) - events_response = self.get('/OS-REVOKE/events', - expected_status=200).json_body + self.delete('/auth/tokens', headers=headers) + self.head('/auth/tokens', headers=headers, + expected_status=http_client.NOT_FOUND) + events_response = self.get('/OS-REVOKE/events').json_body self.assertValidRevokedTokenResponse(events_response, audit_id=response['audit_ids'][0]) def test_revoke_v2_token(self): token = self.get_v2_token() headers = {'X-Subject-Token': token} - response = self.get('/auth/tokens', headers=headers, - expected_status=200).json_body['token'] - self.delete('/auth/tokens', headers=headers, expected_status=204) - self.head('/auth/tokens', headers=headers, expected_status=404) - events_response = self.get('/OS-REVOKE/events', - expected_status=200).json_body + response = self.get('/auth/tokens', + headers=headers).json_body['token'] + self.delete('/auth/tokens', headers=headers) + self.head('/auth/tokens', headers=headers, + expected_status=http_client.NOT_FOUND) + events_response = self.get('/OS-REVOKE/events').json_body self.assertValidRevokedTokenResponse( events_response, audit_id=response['audit_ids'][0]) def test_revoke_by_id_false_410(self): - self.get('/auth/tokens/OS-PKI/revoked', expected_status=410) + self.get('/auth/tokens/OS-PKI/revoked', + expected_status=http_client.GONE) def test_list_delete_project_shows_in_event_list(self): self.role_data_fixtures() - events = self.get('/OS-REVOKE/events', - expected_status=200).json_body['events'] + events = self.get('/OS-REVOKE/events').json_body['events'] self.assertEqual([], events) self.delete( '/projects/%(project_id)s' % {'project_id': self.projectA['id']}) - events_response = self.get('/OS-REVOKE/events', - expected_status=200).json_body + events_response = self.get('/OS-REVOKE/events').json_body self.assertValidDeletedProjectResponse(events_response, self.projectA['id']) def test_disable_domain_shows_in_event_list(self): - events = self.get('/OS-REVOKE/events', - expected_status=200).json_body['events'] + events = self.get('/OS-REVOKE/events').json_body['events'] self.assertEqual([], events) disable_body = {'domain': {'enabled': False}} self.patch( '/domains/%(project_id)s' % {'project_id': self.domainA['id']}, body=disable_body) - events = self.get('/OS-REVOKE/events', - expected_status=200).json_body + events = self.get('/OS-REVOKE/events').json_body self.assertDomainInList(events, self.domainA['id']) @@ -1633,8 +1639,7 @@ class TestTokenRevokeApi(TestTokenRevokeById): def test_list_delete_token_shows_in_event_list(self): self.role_data_fixtures() - events = self.get('/OS-REVOKE/events', - expected_status=200).json_body['events'] + events = self.get('/OS-REVOKE/events').json_body['events'] self.assertEqual([], events) scoped_token = self.get_scoped_token() @@ -1648,47 +1653,50 @@ class TestTokenRevokeApi(TestTokenRevokeById): response.json_body['token'] headers3 = {'X-Subject-Token': response.headers['X-Subject-Token']} - self.head('/auth/tokens', headers=headers, expected_status=200) - self.head('/auth/tokens', headers=headers2, expected_status=200) - self.head('/auth/tokens', headers=headers3, expected_status=200) + self.head('/auth/tokens', headers=headers, + expected_status=http_client.OK) + self.head('/auth/tokens', headers=headers2, + expected_status=http_client.OK) + self.head('/auth/tokens', headers=headers3, + expected_status=http_client.OK) - self.delete('/auth/tokens', headers=headers, expected_status=204) + self.delete('/auth/tokens', headers=headers) # NOTE(ayoung): not deleting token3, as it should be deleted # by previous - events_response = self.get('/OS-REVOKE/events', - expected_status=200).json_body + events_response = self.get('/OS-REVOKE/events').json_body events = events_response['events'] self.assertEqual(1, len(events)) self.assertEventDataInList( events, audit_id=token2['audit_ids'][1]) - self.head('/auth/tokens', headers=headers, expected_status=404) - self.head('/auth/tokens', headers=headers2, expected_status=200) - self.head('/auth/tokens', headers=headers3, expected_status=200) + self.head('/auth/tokens', headers=headers, + expected_status=http_client.NOT_FOUND) + self.head('/auth/tokens', headers=headers2, + expected_status=http_client.OK) + self.head('/auth/tokens', headers=headers3, + expected_status=http_client.OK) def test_list_with_filter(self): self.role_data_fixtures() - events = self.get('/OS-REVOKE/events', - expected_status=200).json_body['events'] + events = self.get('/OS-REVOKE/events').json_body['events'] self.assertEqual(0, len(events)) scoped_token = self.get_scoped_token() headers = {'X-Subject-Token': scoped_token} auth = self.build_authentication_request(token=scoped_token) headers2 = {'X-Subject-Token': self.get_requested_token(auth)} - self.delete('/auth/tokens', headers=headers, expected_status=204) - self.delete('/auth/tokens', headers=headers2, expected_status=204) + self.delete('/auth/tokens', headers=headers) + self.delete('/auth/tokens', headers=headers2) - events = self.get('/OS-REVOKE/events', - expected_status=200).json_body['events'] + events = self.get('/OS-REVOKE/events').json_body['events'] self.assertEqual(2, len(events)) future = utils.isotime(timeutils.utcnow() + datetime.timedelta(seconds=1000)) - events = self.get('/OS-REVOKE/events?since=%s' % (future), - expected_status=200).json_body['events'] + events = self.get('/OS-REVOKE/events?since=%s' % (future) + ).json_body['events'] self.assertEqual(0, len(events)) @@ -2002,7 +2010,7 @@ class TestAuth(test_v3.RestfulTestCase): self._check_disabled_endpoint_result(r.result['token']['catalog'], disabled_endpoint_id) - def test_project_id_scoped_token_with_user_id_401(self): + def test_project_id_scoped_token_with_user_id_unauthorized(self): project = self.new_project_ref(domain_id=self.domain_id) self.resource_api.create_project(project['id'], project) @@ -2010,7 +2018,8 @@ class TestAuth(test_v3.RestfulTestCase): user_id=self.user['id'], password=self.user['password'], project_id=project['id']) - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) def test_user_and_group_roles_scoped_token(self): """Test correct roles are returned in scoped token. @@ -2346,7 +2355,8 @@ class TestAuth(test_v3.RestfulTestCase): user_id=self.user['id'], password=self.user['password'], domain_id=self.domain['id']) - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) def test_auth_with_id(self): auth_data = self.build_authentication_request( @@ -2395,34 +2405,39 @@ class TestAuth(test_v3.RestfulTestCase): auth_data = self.build_authentication_request( user_id=uuid.uuid4().hex, password=self.user['password']) - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) def test_invalid_user_name(self): auth_data = self.build_authentication_request( username=uuid.uuid4().hex, user_domain_id=self.domain['id'], password=self.user['password']) - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) def test_invalid_domain_id(self): auth_data = self.build_authentication_request( username=self.user['name'], user_domain_id=uuid.uuid4().hex, password=self.user['password']) - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) def test_invalid_domain_name(self): auth_data = self.build_authentication_request( username=self.user['name'], user_domain_name=uuid.uuid4().hex, password=self.user['password']) - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) def test_invalid_password(self): auth_data = self.build_authentication_request( user_id=self.user['id'], password=uuid.uuid4().hex) - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) def test_remote_user_no_realm(self): api = auth.controllers.Auth() @@ -2588,7 +2603,8 @@ class TestAuth(test_v3.RestfulTestCase): user_id=user['id'], password='password') - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) def test_disabled_default_project_result_in_unscoped_token(self): # create a disabled project to work with @@ -2666,7 +2682,8 @@ class TestAuth(test_v3.RestfulTestCase): user_id=self.user['id'], password=self.user['password'], project_id=project['id']) - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) # user should not be able to auth with project_name & domain auth_data = self.build_authentication_request( @@ -2674,7 +2691,8 @@ class TestAuth(test_v3.RestfulTestCase): password=self.user['password'], project_name=project['name'], project_domain_id=domain['id']) - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) def test_auth_methods_with_different_identities_fails(self): # get the token for a user. This is self.user which is different from @@ -2686,7 +2704,8 @@ class TestAuth(test_v3.RestfulTestCase): token=token, user_id=self.default_domain_user['id'], password=self.default_domain_user['password']) - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) class TestAuthJSONExternal(test_v3.RestfulTestCase): @@ -2712,15 +2731,18 @@ class TestTrustOptional(test_v3.RestfulTestCase): self.config_fixture.config(group='trust', enabled=False) def test_trusts_404(self): - self.get('/OS-TRUST/trusts', body={'trust': {}}, expected_status=404) - self.post('/OS-TRUST/trusts', body={'trust': {}}, expected_status=404) + self.get('/OS-TRUST/trusts', body={'trust': {}}, + expected_status=http_client.NOT_FOUND) + self.post('/OS-TRUST/trusts', body={'trust': {}}, + expected_status=http_client.NOT_FOUND) - def test_auth_with_scope_in_trust_403(self): + def test_auth_with_scope_in_trust_forbidden(self): auth_data = self.build_authentication_request( user_id=self.user['id'], password=self.user['password'], trust_id=uuid.uuid4().hex) - self.v3_authenticate_token(auth_data, expected_status=403) + self.v3_authenticate_token(auth_data, + expected_status=http_client.FORBIDDEN) class TestTrustRedelegation(test_v3.RestfulTestCase): @@ -2804,7 +2826,7 @@ class TestTrustRedelegation(test_v3.RestfulTestCase): self.post('/OS-TRUST/trusts', body={'trust': self.chained_trust_ref}, token=trust_token, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_modified_redelegation_count_error(self): r = self.post('/OS-TRUST/trusts', @@ -2820,14 +2842,14 @@ class TestTrustRedelegation(test_v3.RestfulTestCase): self.post('/OS-TRUST/trusts', body={'trust': self.chained_trust_ref}, token=trust_token, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_max_redelegation_count_constraint(self): incorrect = CONF.trust.max_redelegation_count + 1 self.redelegated_trust_ref['redelegation_count'] = incorrect self.post('/OS-TRUST/trusts', body={'trust': self.redelegated_trust_ref}, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_redelegation_expiry(self): r = self.post('/OS-TRUST/trusts', @@ -2847,7 +2869,7 @@ class TestTrustRedelegation(test_v3.RestfulTestCase): self.post('/OS-TRUST/trusts', body={'trust': too_long_live_chained_trust_ref}, token=trust_token, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_redelegation_remaining_uses(self): r = self.post('/OS-TRUST/trusts', @@ -2862,7 +2884,7 @@ class TestTrustRedelegation(test_v3.RestfulTestCase): self.post('/OS-TRUST/trusts', body={'trust': self.chained_trust_ref}, token=trust_token, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_roles_subset(self): # Build second role @@ -2949,7 +2971,7 @@ class TestTrustRedelegation(test_v3.RestfulTestCase): self.post('/OS-TRUST/trusts', body={'trust': self.chained_trust_ref}, token=trust_token, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_redelegation_terminator(self): r = self.post('/OS-TRUST/trusts', @@ -2977,7 +2999,7 @@ class TestTrustRedelegation(test_v3.RestfulTestCase): self.post('/OS-TRUST/trusts', body={'trust': ref}, token=trust_token, - expected_status=403) + expected_status=http_client.FORBIDDEN) class TestTrustChain(test_v3.RestfulTestCase): @@ -3084,22 +3106,20 @@ class TestTrustChain(test_v3.RestfulTestCase): def test_delete_trust_cascade(self): self.assert_user_authenticate(self.user_chain[0]) self.delete('/OS-TRUST/trusts/%(trust_id)s' % { - 'trust_id': self.trust_chain[0]['id']}, - expected_status=204) + 'trust_id': self.trust_chain[0]['id']}) headers = {'X-Subject-Token': self.last_token} - self.head('/auth/tokens', headers=headers, expected_status=404) + self.head('/auth/tokens', headers=headers, + expected_status=http_client.NOT_FOUND) self.assert_trust_tokens_revoked(self.trust_chain[0]['id']) def test_delete_broken_chain(self): self.assert_user_authenticate(self.user_chain[0]) self.delete('/OS-TRUST/trusts/%(trust_id)s' % { - 'trust_id': self.trust_chain[1]['id']}, - expected_status=204) + 'trust_id': self.trust_chain[1]['id']}) self.delete('/OS-TRUST/trusts/%(trust_id)s' % { - 'trust_id': self.trust_chain[0]['id']}, - expected_status=204) + 'trust_id': self.trust_chain[0]['id']}) def test_trustor_roles_revoked(self): self.assert_user_authenticate(self.user_chain[0]) @@ -3111,7 +3131,8 @@ class TestTrustChain(test_v3.RestfulTestCase): auth_data = self.build_authentication_request( token=self.last_token, trust_id=self.trust_chain[-1]['id']) - self.v3_authenticate_token(auth_data, expected_status=404) + self.v3_authenticate_token(auth_data, + expected_status=http_client.NOT_FOUND) def test_intermediate_user_disabled(self): self.assert_user_authenticate(self.user_chain[0]) @@ -3123,7 +3144,8 @@ class TestTrustChain(test_v3.RestfulTestCase): # Bypass policy enforcement with mock.patch.object(rules, 'enforce', return_value=True): headers = {'X-Subject-Token': self.last_token} - self.head('/auth/tokens', headers=headers, expected_status=403) + self.head('/auth/tokens', headers=headers, + expected_status=http_client.FORBIDDEN) def test_intermediate_user_deleted(self): self.assert_user_authenticate(self.user_chain[0]) @@ -3133,7 +3155,8 @@ class TestTrustChain(test_v3.RestfulTestCase): # Bypass policy enforcement with mock.patch.object(rules, 'enforce', return_value=True): headers = {'X-Subject-Token': self.last_token} - self.head('/auth/tokens', headers=headers, expected_status=403) + self.head('/auth/tokens', headers=headers, + expected_status=http_client.FORBIDDEN) class TestTrustAuth(test_v3.RestfulTestCase): @@ -3159,9 +3182,10 @@ class TestTrustAuth(test_v3.RestfulTestCase): self.trustee_user['password'] = password self.trustee_user_id = self.trustee_user['id'] - def test_create_trust_400(self): + def test_create_trust_bad_request(self): # The server returns a 403 Forbidden rather than a 400, see bug 1133435 - self.post('/OS-TRUST/trusts', body={'trust': {}}, expected_status=403) + self.post('/OS-TRUST/trusts', body={'trust': {}}, + expected_status=http_client.FORBIDDEN) def test_create_unscoped_trust(self): ref = self.new_trust_ref( @@ -3175,7 +3199,8 @@ class TestTrustAuth(test_v3.RestfulTestCase): trustor_user_id=self.user_id, trustee_user_id=self.trustee_user_id, project_id=self.project_id) - self.post('/OS-TRUST/trusts', body={'trust': ref}, expected_status=403) + self.post('/OS-TRUST/trusts', body={'trust': ref}, + expected_status=http_client.FORBIDDEN) def _initialize_test_consume_trust(self, count): # Make sure remaining_uses is decremented as we consume the trust @@ -3189,8 +3214,7 @@ class TestTrustAuth(test_v3.RestfulTestCase): # make sure the trust exists trust = self.assertValidTrustResponse(r, ref) r = self.get( - '/OS-TRUST/trusts/%(trust_id)s' % {'trust_id': trust['id']}, - expected_status=200) + '/OS-TRUST/trusts/%(trust_id)s' % {'trust_id': trust['id']}) # get a token for the trustee auth_data = self.build_authentication_request( user_id=self.trustee_user['id'], @@ -3208,8 +3232,7 @@ class TestTrustAuth(test_v3.RestfulTestCase): trust = self._initialize_test_consume_trust(2) # check decremented value r = self.get( - '/OS-TRUST/trusts/%(trust_id)s' % {'trust_id': trust['id']}, - expected_status=200) + '/OS-TRUST/trusts/%(trust_id)s' % {'trust_id': trust['id']}) trust = r.result.get('trust') self.assertIsNotNone(trust) self.assertEqual(1, trust['remaining_uses']) @@ -3219,13 +3242,14 @@ class TestTrustAuth(test_v3.RestfulTestCase): # No more uses, the trust is made unavailable self.get( '/OS-TRUST/trusts/%(trust_id)s' % {'trust_id': trust['id']}, - expected_status=404) + expected_status=http_client.NOT_FOUND) # this time we can't get a trust token auth_data = self.build_authentication_request( user_id=self.trustee_user['id'], password=self.trustee_user['password'], trust_id=trust['id']) - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) def test_create_trust_with_bad_values_for_remaining_uses(self): # negative values for the remaining_uses parameter are forbidden @@ -3245,7 +3269,7 @@ class TestTrustAuth(test_v3.RestfulTestCase): role_ids=[self.role_id]) self.post('/OS-TRUST/trusts', body={'trust': ref}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_invalid_trust_request_without_impersonation(self): ref = self.new_trust_ref( @@ -3258,7 +3282,7 @@ class TestTrustAuth(test_v3.RestfulTestCase): self.post('/OS-TRUST/trusts', body={'trust': ref}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_invalid_trust_request_without_trustee(self): ref = self.new_trust_ref( @@ -3271,7 +3295,7 @@ class TestTrustAuth(test_v3.RestfulTestCase): self.post('/OS-TRUST/trusts', body={'trust': ref}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_create_unlimited_use_trust(self): # by default trusts are unlimited in terms of tokens that can be @@ -3286,8 +3310,7 @@ class TestTrustAuth(test_v3.RestfulTestCase): trust = self.assertValidTrustResponse(r, ref) r = self.get( - '/OS-TRUST/trusts/%(trust_id)s' % {'trust_id': trust['id']}, - expected_status=200) + '/OS-TRUST/trusts/%(trust_id)s' % {'trust_id': trust['id']}) auth_data = self.build_authentication_request( user_id=self.trustee_user['id'], password=self.trustee_user['password']) @@ -3298,8 +3321,7 @@ class TestTrustAuth(test_v3.RestfulTestCase): trust_id=trust['id']) r = self.v3_authenticate_token(auth_data) r = self.get( - '/OS-TRUST/trusts/%(trust_id)s' % {'trust_id': trust['id']}, - expected_status=200) + '/OS-TRUST/trusts/%(trust_id)s' % {'trust_id': trust['id']}) trust = r.result.get('trust') self.assertIsNone(trust['remaining_uses']) @@ -3313,45 +3335,41 @@ class TestTrustAuth(test_v3.RestfulTestCase): trust = self.assertValidTrustResponse(r, ref) r = self.get( - '/OS-TRUST/trusts/%(trust_id)s' % {'trust_id': trust['id']}, - expected_status=200) + '/OS-TRUST/trusts/%(trust_id)s' % {'trust_id': trust['id']}) self.assertValidTrustResponse(r, ref) # validate roles on the trust r = self.get( '/OS-TRUST/trusts/%(trust_id)s/roles' % { - 'trust_id': trust['id']}, - expected_status=200) + 'trust_id': trust['id']}) roles = self.assertValidRoleListResponse(r, self.role) self.assertIn(self.role['id'], [x['id'] for x in roles]) self.head( '/OS-TRUST/trusts/%(trust_id)s/roles/%(role_id)s' % { 'trust_id': trust['id'], 'role_id': self.role['id']}, - expected_status=200) + expected_status=http_client.OK) r = self.get( '/OS-TRUST/trusts/%(trust_id)s/roles/%(role_id)s' % { 'trust_id': trust['id'], - 'role_id': self.role['id']}, - expected_status=200) + 'role_id': self.role['id']}) self.assertValidRoleResponse(r, self.role) - r = self.get('/OS-TRUST/trusts', expected_status=200) + r = self.get('/OS-TRUST/trusts') self.assertValidTrustListResponse(r, trust) # trusts are immutable self.patch( '/OS-TRUST/trusts/%(trust_id)s' % {'trust_id': trust['id']}, body={'trust': ref}, - expected_status=404) + expected_status=http_client.NOT_FOUND) self.delete( - '/OS-TRUST/trusts/%(trust_id)s' % {'trust_id': trust['id']}, - expected_status=204) + '/OS-TRUST/trusts/%(trust_id)s' % {'trust_id': trust['id']}) self.get( '/OS-TRUST/trusts/%(trust_id)s' % {'trust_id': trust['id']}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_create_trust_trustee_404(self): ref = self.new_trust_ref( @@ -3359,7 +3377,8 @@ class TestTrustAuth(test_v3.RestfulTestCase): trustee_user_id=uuid.uuid4().hex, project_id=self.project_id, role_ids=[self.role_id]) - self.post('/OS-TRUST/trusts', body={'trust': ref}, expected_status=404) + self.post('/OS-TRUST/trusts', body={'trust': ref}, + expected_status=http_client.NOT_FOUND) def test_create_trust_trustor_trustee_backwards(self): ref = self.new_trust_ref( @@ -3367,7 +3386,8 @@ class TestTrustAuth(test_v3.RestfulTestCase): trustee_user_id=self.user_id, project_id=self.project_id, role_ids=[self.role_id]) - self.post('/OS-TRUST/trusts', body={'trust': ref}, expected_status=403) + self.post('/OS-TRUST/trusts', body={'trust': ref}, + expected_status=http_client.FORBIDDEN) def test_create_trust_project_404(self): ref = self.new_trust_ref( @@ -3375,7 +3395,8 @@ class TestTrustAuth(test_v3.RestfulTestCase): trustee_user_id=self.trustee_user_id, project_id=uuid.uuid4().hex, role_ids=[self.role_id]) - self.post('/OS-TRUST/trusts', body={'trust': ref}, expected_status=404) + self.post('/OS-TRUST/trusts', body={'trust': ref}, + expected_status=http_client.NOT_FOUND) def test_create_trust_role_id_404(self): ref = self.new_trust_ref( @@ -3383,7 +3404,8 @@ class TestTrustAuth(test_v3.RestfulTestCase): trustee_user_id=self.trustee_user_id, project_id=self.project_id, role_ids=[uuid.uuid4().hex]) - self.post('/OS-TRUST/trusts', body={'trust': ref}, expected_status=404) + self.post('/OS-TRUST/trusts', body={'trust': ref}, + expected_status=http_client.NOT_FOUND) def test_create_trust_role_name_404(self): ref = self.new_trust_ref( @@ -3391,7 +3413,8 @@ class TestTrustAuth(test_v3.RestfulTestCase): trustee_user_id=self.trustee_user_id, project_id=self.project_id, role_names=[uuid.uuid4().hex]) - self.post('/OS-TRUST/trusts', body={'trust': ref}, expected_status=404) + self.post('/OS-TRUST/trusts', body={'trust': ref}, + expected_status=http_client.NOT_FOUND) def test_v3_v2_intermix_trustor_not_in_default_domain_failed(self): ref = self.new_trust_ref( @@ -3419,7 +3442,7 @@ class TestTrustAuth(test_v3.RestfulTestCase): path = '/v2.0/tokens/%s' % (token) self.admin_request( path=path, token=CONF.admin_token, - method='GET', expected_status=401) + method='GET', expected_status=http_client.UNAUTHORIZED) def test_v3_v2_intermix_trustor_not_in_default_domaini_failed(self): ref = self.new_trust_ref( @@ -3452,7 +3475,7 @@ class TestTrustAuth(test_v3.RestfulTestCase): path = '/v2.0/tokens/%s' % (token) self.admin_request( path=path, token=CONF.admin_token, - method='GET', expected_status=401) + method='GET', expected_status=http_client.UNAUTHORIZED) def test_v3_v2_intermix_project_not_in_default_domaini_failed(self): # create a trustee in default domain to delegate stuff to @@ -3492,7 +3515,7 @@ class TestTrustAuth(test_v3.RestfulTestCase): path = '/v2.0/tokens/%s' % (token) self.admin_request( path=path, token=CONF.admin_token, - method='GET', expected_status=401) + method='GET', expected_status=http_client.UNAUTHORIZED) def test_v3_v2_intermix(self): # create a trustee in default domain to delegate stuff to @@ -3531,7 +3554,7 @@ class TestTrustAuth(test_v3.RestfulTestCase): path = '/v2.0/tokens/%s' % (token) self.admin_request( path=path, token=CONF.admin_token, - method='GET', expected_status=200) + method='GET', expected_status=http_client.OK) def test_exercise_trust_scoped_token_without_impersonation(self): ref = self.new_trust_ref( @@ -3624,7 +3647,7 @@ class TestTrustAuth(test_v3.RestfulTestCase): self.post('/OS-TRUST/trusts', body={'trust': ref}, token=trust_token, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_trust_deleted_grant(self): # create a new role @@ -3662,7 +3685,8 @@ class TestTrustAuth(test_v3.RestfulTestCase): user_id=self.trustee_user['id'], password=self.trustee_user['password'], trust_id=trust['id']) - r = self.v3_authenticate_token(auth_data, expected_status=403) + r = self.v3_authenticate_token(auth_data, + expected_status=http_client.FORBIDDEN) def test_trust_chained(self): """Test that a trust token can't be used to execute another trust. @@ -3730,11 +3754,11 @@ class TestTrustAuth(test_v3.RestfulTestCase): auth_data = self.build_authentication_request( token=trust_token, trust_id=trust1['id']) - r = self.v3_authenticate_token(auth_data, expected_status=403) + r = self.v3_authenticate_token(auth_data, + expected_status=http_client.FORBIDDEN) def assertTrustTokensRevoked(self, trust_id): - revocation_response = self.get('/OS-REVOKE/events', - expected_status=200) + revocation_response = self.get('/OS-REVOKE/events') revocation_events = revocation_response.json_body['events'] found = False for event in revocation_events: @@ -3763,10 +3787,10 @@ class TestTrustAuth(test_v3.RestfulTestCase): r, self.trustee_user) trust_token = r.headers['X-Subject-Token'] self.delete('/OS-TRUST/trusts/%(trust_id)s' % { - 'trust_id': trust_id}, - expected_status=204) + 'trust_id': trust_id}) headers = {'X-Subject-Token': trust_token} - self.head('/auth/tokens', headers=headers, expected_status=404) + self.head('/auth/tokens', headers=headers, + expected_status=http_client.NOT_FOUND) self.assertTrustTokensRevoked(trust_id) def disable_user(self, user): @@ -3790,7 +3814,7 @@ class TestTrustAuth(test_v3.RestfulTestCase): user_id=self.trustee_user['id'], password=self.trustee_user['password'], trust_id=trust['id']) - self.v3_authenticate_token(auth_data, expected_status=201) + self.v3_authenticate_token(auth_data) self.disable_user(self.user) @@ -3798,7 +3822,8 @@ class TestTrustAuth(test_v3.RestfulTestCase): user_id=self.trustee_user['id'], password=self.trustee_user['password'], trust_id=trust['id']) - self.v3_authenticate_token(auth_data, expected_status=403) + self.v3_authenticate_token(auth_data, + expected_status=http_client.FORBIDDEN) def test_trust_get_token_fails_if_trustee_disabled(self): ref = self.new_trust_ref( @@ -3817,7 +3842,7 @@ class TestTrustAuth(test_v3.RestfulTestCase): user_id=self.trustee_user['id'], password=self.trustee_user['password'], trust_id=trust['id']) - self.v3_authenticate_token(auth_data, expected_status=201) + self.v3_authenticate_token(auth_data) self.disable_user(self.trustee_user) @@ -3825,7 +3850,8 @@ class TestTrustAuth(test_v3.RestfulTestCase): user_id=self.trustee_user['id'], password=self.trustee_user['password'], trust_id=trust['id']) - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) def test_delete_trust(self): ref = self.new_trust_ref( @@ -3841,22 +3867,22 @@ class TestTrustAuth(test_v3.RestfulTestCase): trust = self.assertValidTrustResponse(r, ref) self.delete('/OS-TRUST/trusts/%(trust_id)s' % { - 'trust_id': trust['id']}, - expected_status=204) + 'trust_id': trust['id']}) self.get('/OS-TRUST/trusts/%(trust_id)s' % { 'trust_id': trust['id']}, - expected_status=404) + expected_status=http_client.NOT_FOUND) self.get('/OS-TRUST/trusts/%(trust_id)s' % { 'trust_id': trust['id']}, - expected_status=404) + expected_status=http_client.NOT_FOUND) auth_data = self.build_authentication_request( user_id=self.trustee_user['id'], password=self.trustee_user['password'], trust_id=trust['id']) - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) def test_list_trusts(self): ref = self.new_trust_ref( @@ -3871,19 +3897,19 @@ class TestTrustAuth(test_v3.RestfulTestCase): r = self.post('/OS-TRUST/trusts', body={'trust': ref}) self.assertValidTrustResponse(r, ref) - r = self.get('/OS-TRUST/trusts', expected_status=200) + r = self.get('/OS-TRUST/trusts') trusts = r.result['trusts'] self.assertEqual(3, len(trusts)) self.assertValidTrustListResponse(r) r = self.get('/OS-TRUST/trusts?trustor_user_id=%s' % - self.user_id, expected_status=200) + self.user_id) trusts = r.result['trusts'] self.assertEqual(3, len(trusts)) self.assertValidTrustListResponse(r) r = self.get('/OS-TRUST/trusts?trustee_user_id=%s' % - self.user_id, expected_status=200) + self.user_id) trusts = r.result['trusts'] self.assertEqual(0, len(trusts)) @@ -3909,16 +3935,14 @@ class TestTrustAuth(test_v3.RestfulTestCase): trust_token = r.headers.get('X-Subject-Token') self.get('/OS-TRUST/trusts?trustor_user_id=%s' % - self.user_id, expected_status=200, - token=trust_token) + self.user_id, token=trust_token) self.assertValidUserResponse( self.patch('/users/%s' % self.trustee_user['id'], - body={'user': {'password': uuid.uuid4().hex}}, - expected_status=200)) + body={'user': {'password': uuid.uuid4().hex}})) self.get('/OS-TRUST/trusts?trustor_user_id=%s' % - self.user_id, expected_status=401, + self.user_id, expected_status=http_client.UNAUTHORIZED, token=trust_token) def test_trustee_can_do_role_ops(self): @@ -3947,14 +3971,13 @@ class TestTrustAuth(test_v3.RestfulTestCase): 'trust_id': trust['id'], 'role_id': self.role['id']}, auth=auth_data, - expected_status=200) + expected_status=http_client.OK) r = self.get( '/OS-TRUST/trusts/%(trust_id)s/roles/%(role_id)s' % { 'trust_id': trust['id'], 'role_id': self.role['id']}, - auth=auth_data, - expected_status=200) + auth=auth_data) self.assertValidRoleResponse(r, self.role) def test_do_not_consume_remaining_uses_when_get_token_fails(self): @@ -3977,7 +4000,8 @@ class TestTrustAuth(test_v3.RestfulTestCase): user_id=self.default_domain_user['id'], password=self.default_domain_user['password'], trust_id=trust_id) - self.v3_authenticate_token(auth_data, expected_status=403) + self.v3_authenticate_token(auth_data, + expected_status=http_client.FORBIDDEN) r = self.get('/OS-TRUST/trusts/%s' % trust_id) self.assertEqual(3, r.result.get('trust').get('remaining_uses')) @@ -3998,10 +4022,10 @@ class TestAPIProtectionWithoutAuthContextMiddleware(test_v3.RestfulTestCase): 'query_string': {}, 'environment': {}} r = auth_controller.validate_token(context) - self.assertEqual(200, r.status_code) + self.assertEqual(http_client.OK, r.status_code) -class TestAuthContext(tests.TestCase): +class TestAuthContext(unit.TestCase): def setUp(self): super(TestAuthContext, self).setUp() self.auth_context = auth.controllers.AuthContext() @@ -4058,9 +4082,7 @@ class TestAuthSpecificData(test_v3.RestfulTestCase): def test_get_catalog_project_scoped_token(self): """Call ``GET /auth/catalog`` with a project-scoped token.""" - r = self.get( - '/auth/catalog', - expected_status=200) + r = self.get('/auth/catalog') self.assertValidCatalogResponse(r) def test_get_catalog_domain_scoped_token(self): @@ -4075,7 +4097,7 @@ class TestAuthSpecificData(test_v3.RestfulTestCase): user_id=self.user['id'], password=self.user['password'], domain_id=self.domain['id']), - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_get_catalog_unscoped_token(self): """Call ``GET /auth/catalog`` with an unscoped token.""" @@ -4084,17 +4106,17 @@ class TestAuthSpecificData(test_v3.RestfulTestCase): auth=self.build_authentication_request( user_id=self.default_domain_user['id'], password=self.default_domain_user['password']), - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_get_catalog_no_token(self): """Call ``GET /auth/catalog`` without a token.""" self.get( '/auth/catalog', noauth=True, - expected_status=401) + expected_status=http_client.UNAUTHORIZED) def test_get_projects_project_scoped_token(self): - r = self.get('/auth/projects', expected_status=200) + r = self.get('/auth/projects') self.assertThat(r.json['projects'], matchers.HasLength(1)) self.assertValidProjectListResponse(r) @@ -4102,7 +4124,7 @@ class TestAuthSpecificData(test_v3.RestfulTestCase): self.put(path='/domains/%s/users/%s/roles/%s' % ( self.domain['id'], self.user['id'], self.role['id'])) - r = self.get('/auth/domains', expected_status=200) + r = self.get('/auth/domains') self.assertThat(r.json['domains'], matchers.HasLength(1)) self.assertValidDomainListResponse(r) @@ -4113,7 +4135,7 @@ class TestFernetTokenProvider(test_v3.RestfulTestCase): self.useFixture(ksfixtures.KeyRepository(self.config_fixture)) def _make_auth_request(self, auth_data): - resp = self.post('/auth/tokens', body=auth_data, expected_status=201) + resp = self.post('/auth/tokens', body=auth_data) token = resp.headers.get('X-Subject-Token') self.assertLess(len(token), 255) return token @@ -4145,13 +4167,13 @@ class TestFernetTokenProvider(test_v3.RestfulTestCase): trust_id=trust['id']) return self._make_auth_request(auth_data) - def _validate_token(self, token, expected_status=200): + def _validate_token(self, token, expected_status=http_client.OK): return self.get( '/auth/tokens', headers={'X-Subject-Token': token}, expected_status=expected_status) - def _revoke_token(self, token, expected_status=204): + def _revoke_token(self, token, expected_status=http_client.NO_CONTENT): return self.delete( '/auth/tokens', headers={'X-Subject-Token': token}, @@ -4190,13 +4212,15 @@ class TestFernetTokenProvider(test_v3.RestfulTestCase): unscoped_token = self._get_unscoped_token() tampered_token = (unscoped_token[:50] + uuid.uuid4().hex + unscoped_token[50 + 32:]) - self._validate_token(tampered_token, expected_status=404) + self._validate_token(tampered_token, + expected_status=http_client.NOT_FOUND) def test_revoke_unscoped_token(self): unscoped_token = self._get_unscoped_token() self._validate_token(unscoped_token) self._revoke_token(unscoped_token) - self._validate_token(unscoped_token, expected_status=404) + self._validate_token(unscoped_token, + expected_status=http_client.NOT_FOUND) def test_unscoped_token_is_invalid_after_disabling_user(self): unscoped_token = self._get_unscoped_token() @@ -4270,13 +4294,15 @@ class TestFernetTokenProvider(test_v3.RestfulTestCase): project_scoped_token = self._get_project_scoped_token() tampered_token = (project_scoped_token[:50] + uuid.uuid4().hex + project_scoped_token[50 + 32:]) - self._validate_token(tampered_token, expected_status=404) + self._validate_token(tampered_token, + expected_status=http_client.NOT_FOUND) def test_revoke_project_scoped_token(self): project_scoped_token = self._get_project_scoped_token() self._validate_token(project_scoped_token) self._revoke_token(project_scoped_token) - self._validate_token(project_scoped_token, expected_status=404) + self._validate_token(project_scoped_token, + expected_status=http_client.NOT_FOUND) def test_project_scoped_token_is_invalid_after_disabling_user(self): project_scoped_token = self._get_project_scoped_token() @@ -4378,7 +4404,8 @@ class TestFernetTokenProvider(test_v3.RestfulTestCase): # Get a trust scoped token tampered_token = (trust_scoped_token[:50] + uuid.uuid4().hex + trust_scoped_token[50 + 32:]) - self._validate_token(tampered_token, expected_status=404) + self._validate_token(tampered_token, + expected_status=http_client.NOT_FOUND) def test_revoke_trust_scoped_token(self): trustee_user, trust = self._create_trust() @@ -4386,7 +4413,8 @@ class TestFernetTokenProvider(test_v3.RestfulTestCase): # Validate a trust scoped token self._validate_token(trust_scoped_token) self._revoke_token(trust_scoped_token) - self._validate_token(trust_scoped_token, expected_status=404) + self._validate_token(trust_scoped_token, + expected_status=http_client.NOT_FOUND) def test_trust_scoped_token_is_invalid_after_disabling_trustee(self): trustee_user, trust = self._create_trust() @@ -4460,7 +4488,7 @@ class TestFernetTokenProvider(test_v3.RestfulTestCase): self.token_provider_api.validate_token, trust_scoped_token) - def test_v2_validate_unscoped_token_returns_401(self): + def test_v2_validate_unscoped_token_returns_unauthorized(self): """Test raised exception when validating unscoped token. Test that validating an unscoped token in v2.0 of a v3 user of a @@ -4471,7 +4499,7 @@ class TestFernetTokenProvider(test_v3.RestfulTestCase): self.token_provider_api.validate_v2_token, unscoped_token) - def test_v2_validate_domain_scoped_token_returns_401(self): + def test_v2_validate_domain_scoped_token_returns_unauthorized(self): """Test raised exception when validating a domain scoped token. Test that validating an domain scoped token in v2.0 @@ -4519,7 +4547,8 @@ class TestAuthFernetTokenProvider(TestAuth): self.admin_app.extra_environ.update({'REMOTE_USER': remote_user, 'AUTH_TYPE': 'Negotiate'}) # Bind not current supported by Fernet, see bug 1433311. - self.v3_authenticate_token(auth_data, expected_status=501) + self.v3_authenticate_token(auth_data, + expected_status=http_client.NOT_IMPLEMENTED) def test_v2_v3_bind_token_intermix(self): self.config_fixture.config(group='token', bind='kerberos') @@ -4534,7 +4563,7 @@ class TestAuthFernetTokenProvider(TestAuth): self.admin_request(path='/v2.0/tokens', method='POST', body=body, - expected_status=501) + expected_status=http_client.NOT_IMPLEMENTED) def test_auth_with_bind_token(self): self.config_fixture.config(group='token', bind=['kerberos']) @@ -4544,4 +4573,5 @@ class TestAuthFernetTokenProvider(TestAuth): self.admin_app.extra_environ.update({'REMOTE_USER': remote_user, 'AUTH_TYPE': 'Negotiate'}) # Bind not current supported by Fernet, see bug 1433311. - self.v3_authenticate_token(auth_data, expected_status=501) + self.v3_authenticate_token(auth_data, + expected_status=http_client.NOT_IMPLEMENTED) diff --git a/keystone-moon/keystone/tests/unit/test_v3_catalog.py b/keystone-moon/keystone/tests/unit/test_v3_catalog.py index f96b2a12..0d82390d 100644 --- a/keystone-moon/keystone/tests/unit/test_v3_catalog.py +++ b/keystone-moon/keystone/tests/unit/test_v3_catalog.py @@ -15,10 +15,11 @@ import copy import uuid +from six.moves import http_client from testtools import matchers from keystone import catalog -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit.ksfixtures import database from keystone.tests.unit import test_v3 @@ -35,7 +36,7 @@ class CatalogTestCase(test_v3.RestfulTestCase): r = self.put( '/regions/%s' % region_id, body={'region': ref}, - expected_status=201) + expected_status=http_client.CREATED) self.assertValidRegionResponse(r, ref) # Double-check that the region ID was kept as-is and not # populated with a UUID, as is the case with POST /v3/regions @@ -48,7 +49,7 @@ class CatalogTestCase(test_v3.RestfulTestCase): r = self.put( '/regions/%s' % region_id, body={'region': ref}, - expected_status=201) + expected_status=http_client.CREATED) self.assertValidRegionResponse(r, ref) # Double-check that the region ID was kept as-is and not # populated with a UUID, as is the case with POST /v3/regions @@ -59,7 +60,7 @@ class CatalogTestCase(test_v3.RestfulTestCase): ref = dict(description="my region") self.put( '/regions/myregion', - body={'region': ref}, expected_status=201) + body={'region': ref}, expected_status=http_client.CREATED) # Create region again with duplicate id self.put( '/regions/myregion', @@ -85,9 +86,7 @@ class CatalogTestCase(test_v3.RestfulTestCase): ref = self.new_region_ref() ref['id'] = '' - r = self.post( - '/regions', - body={'region': ref}, expected_status=201) + r = self.post('/regions', body={'region': ref}) self.assertValidRegionResponse(r, ref) self.assertNotEmpty(r.result['region'].get('id')) @@ -99,10 +98,7 @@ class CatalogTestCase(test_v3.RestfulTestCase): del ref['id'] # let the service define the ID - r = self.post( - '/regions', - body={'region': ref}, - expected_status=201) + r = self.post('/regions', body={'region': ref}) self.assertValidRegionResponse(r, ref) def test_create_region_without_description(self): @@ -111,10 +107,7 @@ class CatalogTestCase(test_v3.RestfulTestCase): del ref['description'] - r = self.post( - '/regions', - body={'region': ref}, - expected_status=201) + r = self.post('/regions', body={'region': ref}) # Create the description in the reference to compare to since the # response should now have a description, even though we didn't send # it with the original reference. @@ -134,16 +127,10 @@ class CatalogTestCase(test_v3.RestfulTestCase): ref1['description'] = region_desc ref2['description'] = region_desc - resp1 = self.post( - '/regions', - body={'region': ref1}, - expected_status=201) + resp1 = self.post('/regions', body={'region': ref1}) self.assertValidRegionResponse(resp1, ref1) - resp2 = self.post( - '/regions', - body={'region': ref2}, - expected_status=201) + resp2 = self.post('/regions', body={'region': ref2}) self.assertValidRegionResponse(resp2, ref2) def test_create_regions_without_descriptions(self): @@ -158,15 +145,9 @@ class CatalogTestCase(test_v3.RestfulTestCase): del ref1['description'] ref2['description'] = None - resp1 = self.post( - '/regions', - body={'region': ref1}, - expected_status=201) + resp1 = self.post('/regions', body={'region': ref1}) - resp2 = self.post( - '/regions', - body={'region': ref2}, - expected_status=201) + resp2 = self.post('/regions', body={'region': ref2}) # Create the descriptions in the references to compare to since the # responses should now have descriptions, even though we didn't send # a description with the original references. @@ -184,7 +165,7 @@ class CatalogTestCase(test_v3.RestfulTestCase): self.put( '/regions/%s' % uuid.uuid4().hex, body={'region': ref}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_list_regions(self): """Call ``GET /regions``.""" @@ -230,16 +211,14 @@ class CatalogTestCase(test_v3.RestfulTestCase): """Call ``PATCH /regions/{region_id}``.""" region_ref = self.new_region_ref() - resp = self.post('/regions', body={'region': region_ref}, - expected_status=201) + resp = self.post('/regions', body={'region': region_ref}) region_updates = { # update with something that's not the description 'parent_region_id': self.region_id, } resp = self.patch('/regions/%s' % region_ref['id'], - body={'region': region_updates}, - expected_status=200) + body={'region': region_updates}) # NOTE(dstanek): Keystone should keep the original description. self.assertEqual(region_ref['description'], @@ -326,19 +305,22 @@ class CatalogTestCase(test_v3.RestfulTestCase): """Call ``POST /services``.""" ref = self.new_service_ref() ref['enabled'] = 'True' - self.post('/services', body={'service': ref}, expected_status=400) + self.post('/services', body={'service': ref}, + expected_status=http_client.BAD_REQUEST) def test_create_service_enabled_str_false(self): """Call ``POST /services``.""" ref = self.new_service_ref() ref['enabled'] = 'False' - self.post('/services', body={'service': ref}, expected_status=400) + self.post('/services', body={'service': ref}, + expected_status=http_client.BAD_REQUEST) def test_create_service_enabled_str_random(self): """Call ``POST /services``.""" ref = self.new_service_ref() ref['enabled'] = 'puppies' - self.post('/services', body={'service': ref}, expected_status=400) + self.post('/services', body={'service': ref}, + expected_status=http_client.BAD_REQUEST) def test_list_services(self): """Call ``GET /services``.""" @@ -575,7 +557,7 @@ class CatalogTestCase(test_v3.RestfulTestCase): self.post( '/endpoints', body={'endpoint': ref}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_create_endpoint_enabled_str_false(self): """Call ``POST /endpoints`` with enabled: 'False'.""" @@ -584,7 +566,7 @@ class CatalogTestCase(test_v3.RestfulTestCase): self.post( '/endpoints', body={'endpoint': ref}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_create_endpoint_enabled_str_random(self): """Call ``POST /endpoints`` with enabled: 'puppies'.""" @@ -593,13 +575,14 @@ class CatalogTestCase(test_v3.RestfulTestCase): self.post( '/endpoints', body={'endpoint': ref}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_create_endpoint_with_invalid_region_id(self): """Call ``POST /endpoints``.""" ref = self.new_endpoint_ref(service_id=self.service_id) ref["region_id"] = uuid.uuid4().hex - self.post('/endpoints', body={'endpoint': ref}, expected_status=400) + self.post('/endpoints', body={'endpoint': ref}, + expected_status=http_client.BAD_REQUEST) def test_create_endpoint_with_region(self): """EndpointV3 creates the region before creating the endpoint, if @@ -608,7 +591,7 @@ class CatalogTestCase(test_v3.RestfulTestCase): ref = self.new_endpoint_ref(service_id=self.service_id) ref["region"] = uuid.uuid4().hex ref.pop('region_id') - self.post('/endpoints', body={'endpoint': ref}, expected_status=201) + self.post('/endpoints', body={'endpoint': ref}) # Make sure the region is created self.get('/regions/%(region_id)s' % { 'region_id': ref["region"]}) @@ -617,13 +600,14 @@ class CatalogTestCase(test_v3.RestfulTestCase): """EndpointV3 allows to creates the endpoint without region.""" ref = self.new_endpoint_ref(service_id=self.service_id) ref.pop('region_id') - self.post('/endpoints', body={'endpoint': ref}, expected_status=201) + self.post('/endpoints', body={'endpoint': ref}) def test_create_endpoint_with_empty_url(self): """Call ``POST /endpoints``.""" ref = self.new_endpoint_ref(service_id=self.service_id) ref["url"] = '' - self.post('/endpoints', body={'endpoint': ref}, expected_status=400) + self.post('/endpoints', body={'endpoint': ref}, + expected_status=http_client.BAD_REQUEST) def test_get_endpoint(self): """Call ``GET /endpoints/{endpoint_id}``.""" @@ -667,7 +651,7 @@ class CatalogTestCase(test_v3.RestfulTestCase): '/endpoints/%(endpoint_id)s' % { 'endpoint_id': self.endpoint_id}, body={'endpoint': {'enabled': 'True'}}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_update_endpoint_enabled_str_false(self): """Call ``PATCH /endpoints/{endpoint_id}`` with enabled: 'False'.""" @@ -675,7 +659,7 @@ class CatalogTestCase(test_v3.RestfulTestCase): '/endpoints/%(endpoint_id)s' % { 'endpoint_id': self.endpoint_id}, body={'endpoint': {'enabled': 'False'}}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_update_endpoint_enabled_str_random(self): """Call ``PATCH /endpoints/{endpoint_id}`` with enabled: 'kitties'.""" @@ -683,7 +667,7 @@ class CatalogTestCase(test_v3.RestfulTestCase): '/endpoints/%(endpoint_id)s' % { 'endpoint_id': self.endpoint_id}, body={'endpoint': {'enabled': 'kitties'}}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_delete_endpoint(self): """Call ``DELETE /endpoints/{endpoint_id}``.""" @@ -762,7 +746,8 @@ class CatalogTestCase(test_v3.RestfulTestCase): self.delete('/endpoints/%s' % ref['id']) # make sure it's deleted (GET should return 404) - self.get('/endpoints/%s' % ref['id'], expected_status=404) + self.get('/endpoints/%s' % ref['id'], + expected_status=http_client.NOT_FOUND) def test_endpoint_create_with_valid_url(self): """Create endpoint with valid url should be tested,too.""" @@ -771,9 +756,7 @@ class CatalogTestCase(test_v3.RestfulTestCase): ref = self.new_endpoint_ref(self.service_id) ref['url'] = valid_url - self.post('/endpoints', - body={'endpoint': ref}, - expected_status=201) + self.post('/endpoints', body={'endpoint': ref}) def test_endpoint_create_with_invalid_url(self): """Test the invalid cases: substitutions is not exactly right. @@ -798,10 +781,10 @@ class CatalogTestCase(test_v3.RestfulTestCase): ref['url'] = invalid_url self.post('/endpoints', body={'endpoint': ref}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) -class TestCatalogAPISQL(tests.TestCase): +class TestCatalogAPISQL(unit.TestCase): """Tests for the catalog Manager against the SQL backend. """ @@ -909,7 +892,7 @@ class TestCatalogAPISQL(tests.TestCase): # TODO(dstanek): this needs refactoring with the test above, but we are in a # crunch so that will happen in a future patch. -class TestCatalogAPISQLRegions(tests.TestCase): +class TestCatalogAPISQLRegions(unit.TestCase): """Tests for the catalog Manager against the SQL backend. """ diff --git a/keystone-moon/keystone/tests/unit/test_v3_controller.py b/keystone-moon/keystone/tests/unit/test_v3_controller.py index eef64a82..563e656e 100644 --- a/keystone-moon/keystone/tests/unit/test_v3_controller.py +++ b/keystone-moon/keystone/tests/unit/test_v3_controller.py @@ -20,10 +20,10 @@ from testtools import matchers from keystone.common import controller from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit -class V3ControllerTestCase(tests.TestCase): +class V3ControllerTestCase(unit.TestCase): """Tests for the V3Controller class.""" def setUp(self): super(V3ControllerTestCase, self).setUp() diff --git a/keystone-moon/keystone/tests/unit/test_v3_credential.py b/keystone-moon/keystone/tests/unit/test_v3_credential.py index f8f6d35b..cf504b00 100644 --- a/keystone-moon/keystone/tests/unit/test_v3_credential.py +++ b/keystone-moon/keystone/tests/unit/test_v3_credential.py @@ -18,6 +18,7 @@ import uuid from keystoneclient.contrib.ec2 import utils as ec2_utils from oslo_config import cfg +from six.moves import http_client from testtools import matchers from keystone import exception @@ -98,6 +99,60 @@ class CredentialTestCase(CredentialBaseTestCase): for cred in r.result['credentials']: self.assertEqual(self.user['id'], cred['user_id']) + def test_list_credentials_filtered_by_type(self): + """Call ``GET /credentials?type={type}``.""" + # The type ec2 was chosen, instead of a random string, + # because the type must be in the list of supported types + ec2_credential = self.new_credential_ref(user_id=uuid.uuid4().hex, + project_id=self.project_id, + cred_type='ec2') + + ec2_resp = self.credential_api.create_credential( + ec2_credential['id'], ec2_credential) + + # The type cert was chosen for the same reason as ec2 + r = self.get('/credentials?type=cert') + + # Testing the filter for two different types + self.assertValidCredentialListResponse(r, ref=self.credential) + for cred in r.result['credentials']: + self.assertEqual('cert', cred['type']) + + r_ec2 = self.get('/credentials?type=ec2') + self.assertThat(r_ec2.result['credentials'], matchers.HasLength(1)) + cred_ec2 = r_ec2.result['credentials'][0] + + self.assertValidCredentialListResponse(r_ec2, ref=ec2_resp) + self.assertEqual('ec2', cred_ec2['type']) + self.assertEqual(cred_ec2['id'], ec2_credential['id']) + + def test_list_credentials_filtered_by_type_and_user_id(self): + """Call ``GET /credentials?user_id={user_id}&type={type}``.""" + user1_id = uuid.uuid4().hex + user2_id = uuid.uuid4().hex + + # Creating credentials for two different users + credential_user1_ec2 = self.new_credential_ref( + user_id=user1_id, cred_type='ec2') + credential_user1_cert = self.new_credential_ref( + user_id=user1_id) + credential_user2_cert = self.new_credential_ref( + user_id=user2_id) + + self.credential_api.create_credential( + credential_user1_ec2['id'], credential_user1_ec2) + self.credential_api.create_credential( + credential_user1_cert['id'], credential_user1_cert) + self.credential_api.create_credential( + credential_user2_cert['id'], credential_user2_cert) + + r = self.get('/credentials?user_id=%s&type=ec2' % user1_id) + self.assertValidCredentialListResponse(r, ref=credential_user1_ec2) + self.assertThat(r.result['credentials'], matchers.HasLength(1)) + cred = r.result['credentials'][0] + self.assertEqual('ec2', cred['type']) + self.assertEqual(user1_id, cred['user_id']) + def test_create_credential(self): """Call ``POST /credentials``.""" ref = self.new_credential_ref(user_id=self.user['id']) @@ -198,10 +253,10 @@ class CredentialTestCase(CredentialBaseTestCase): "secret": uuid.uuid4().hex} ref['blob'] = json.dumps(blob) ref['type'] = 'ec2' - # Assert 400 status for bad request with missing project_id + # Assert bad request status when missing project_id self.post( '/credentials', - body={'credential': ref}, expected_status=400) + body={'credential': ref}, expected_status=http_client.BAD_REQUEST) def test_create_ec2_credential_with_invalid_blob(self): """Call ``POST /credentials`` for creating ec2 @@ -211,11 +266,10 @@ class CredentialTestCase(CredentialBaseTestCase): project_id=self.project_id) ref['blob'] = '{"abc":"def"d}' ref['type'] = 'ec2' - # Assert 400 status for bad request containing invalid - # blob + # Assert bad request status when request contains invalid blob response = self.post( '/credentials', - body={'credential': ref}, expected_status=400) + body={'credential': ref}, expected_status=http_client.BAD_REQUEST) self.assertValidErrorResponse(response) def test_create_credential_with_admin_token(self): @@ -328,7 +382,7 @@ class TestCredentialEc2(CredentialBaseTestCase): r = self.post( '/ec2tokens', body={'ec2Credentials': sig_ref}, - expected_status=200) + expected_status=http_client.OK) self.assertValidTokenResponse(r) def test_ec2_credential_signature_validate(self): diff --git a/keystone-moon/keystone/tests/unit/test_v3_domain_config.py b/keystone-moon/keystone/tests/unit/test_v3_domain_config.py index 6f96f0e7..3f7af87d 100644 --- a/keystone-moon/keystone/tests/unit/test_v3_domain_config.py +++ b/keystone-moon/keystone/tests/unit/test_v3_domain_config.py @@ -14,6 +14,7 @@ import copy import uuid from oslo_config import cfg +from six.moves import http_client from keystone import exception from keystone.tests.unit import test_v3 @@ -39,7 +40,7 @@ class DomainConfigTestCase(test_v3.RestfulTestCase): url = '/domains/%(domain_id)s/config' % { 'domain_id': self.domain['id']} r = self.put(url, body={'config': self.config}, - expected_status=201) + expected_status=http_client.CREATED) res = self.domain_config_api.get_config(self.domain['id']) self.assertEqual(self.config, r.result['config']) self.assertEqual(self.config, res) @@ -49,11 +50,11 @@ class DomainConfigTestCase(test_v3.RestfulTestCase): self.put('/domains/%(domain_id)s/config' % { 'domain_id': self.domain['id']}, body={'config': self.config}, - expected_status=201) + expected_status=http_client.CREATED) self.put('/domains/%(domain_id)s/config' % { 'domain_id': self.domain['id']}, body={'config': self.config}, - expected_status=200) + expected_status=http_client.OK) def test_delete_config(self): """Call ``DELETE /domains{domain_id}/config``.""" @@ -79,7 +80,7 @@ class DomainConfigTestCase(test_v3.RestfulTestCase): 'domain_id': self.domain['id']} r = self.get(url) self.assertEqual(self.config, r.result['config']) - self.head(url, expected_status=200) + self.head(url, expected_status=http_client.OK) def test_get_config_by_group(self): """Call ``GET & HEAD /domains{domain_id}/config/{group}``.""" @@ -88,7 +89,7 @@ class DomainConfigTestCase(test_v3.RestfulTestCase): 'domain_id': self.domain['id']} r = self.get(url) self.assertEqual({'ldap': self.config['ldap']}, r.result['config']) - self.head(url, expected_status=200) + self.head(url, expected_status=http_client.OK) def test_get_config_by_option(self): """Call ``GET & HEAD /domains{domain_id}/config/{group}/{option}``.""" @@ -98,26 +99,29 @@ class DomainConfigTestCase(test_v3.RestfulTestCase): r = self.get(url) self.assertEqual({'url': self.config['ldap']['url']}, r.result['config']) - self.head(url, expected_status=200) + self.head(url, expected_status=http_client.OK) def test_get_non_existant_config(self): """Call ``GET /domains{domain_id}/config when no config defined``.""" self.get('/domains/%(domain_id)s/config' % { - 'domain_id': self.domain['id']}, expected_status=404) + 'domain_id': self.domain['id']}, + expected_status=http_client.NOT_FOUND) def test_get_non_existant_config_group(self): """Call ``GET /domains{domain_id}/config/{group_not_exist}``.""" config = {'ldap': {'url': uuid.uuid4().hex}} self.domain_config_api.create_config(self.domain['id'], config) self.get('/domains/%(domain_id)s/config/identity' % { - 'domain_id': self.domain['id']}, expected_status=404) + 'domain_id': self.domain['id']}, + expected_status=http_client.NOT_FOUND) def test_get_non_existant_config_option(self): """Call ``GET /domains{domain_id}/config/group/{option_not_exist}``.""" config = {'ldap': {'url': uuid.uuid4().hex}} self.domain_config_api.create_config(self.domain['id'], config) self.get('/domains/%(domain_id)s/config/ldap/user_tree_dn' % { - 'domain_id': self.domain['id']}, expected_status=404) + 'domain_id': self.domain['id']}, + expected_status=http_client.NOT_FOUND) def test_update_config(self): """Call ``PATCH /domains/{domain_id}/config``.""" @@ -163,7 +167,7 @@ class DomainConfigTestCase(test_v3.RestfulTestCase): self.patch('/domains/%(domain_id)s/config/%(invalid_group)s' % { 'domain_id': self.domain['id'], 'invalid_group': invalid_group}, body={'config': new_config}, - expected_status=403) + expected_status=http_client.FORBIDDEN) # Trying to update a valid group, but one that is not in the current # config should result in NotFound config = {'ldap': {'suffix': uuid.uuid4().hex}} @@ -172,7 +176,7 @@ class DomainConfigTestCase(test_v3.RestfulTestCase): self.patch('/domains/%(domain_id)s/config/identity' % { 'domain_id': self.domain['id']}, body={'config': new_config}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_update_config_option(self): """Call ``PATCH /domains/{domain_id}/config/{group}/{option}``.""" @@ -199,7 +203,7 @@ class DomainConfigTestCase(test_v3.RestfulTestCase): 'domain_id': self.domain['id'], 'invalid_option': invalid_option}, body={'config': new_config}, - expected_status=403) + expected_status=http_client.FORBIDDEN) # Trying to update a valid option, but one that is not in the current # config should result in NotFound new_config = {'suffix': uuid.uuid4().hex} @@ -207,4 +211,4 @@ class DomainConfigTestCase(test_v3.RestfulTestCase): '/domains/%(domain_id)s/config/ldap/suffix' % { 'domain_id': self.domain['id']}, body={'config': new_config}, - expected_status=404) + expected_status=http_client.NOT_FOUND) diff --git a/keystone-moon/keystone/tests/unit/test_v3_endpoint_policy.py b/keystone-moon/keystone/tests/unit/test_v3_endpoint_policy.py index 4daeff4d..b0c8256e 100644 --- a/keystone-moon/keystone/tests/unit/test_v3_endpoint_policy.py +++ b/keystone-moon/keystone/tests/unit/test_v3_endpoint_policy.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. +from six.moves import http_client from testtools import matchers from keystone.tests.unit import test_v3 @@ -48,17 +49,23 @@ class EndpointPolicyTestCase(test_v3.RestfulTestCase): # Test when the resource does not exist also ensures # that there is not a false negative after creation. - self.assert_head_and_get_return_same_response(url, expected_status=404) + self.assert_head_and_get_return_same_response( + url, + expected_status=http_client.NOT_FOUND) - self.put(url, expected_status=204) + self.put(url) # test that the new resource is accessible. - self.assert_head_and_get_return_same_response(url, expected_status=204) + self.assert_head_and_get_return_same_response( + url, + expected_status=http_client.NO_CONTENT) - self.delete(url, expected_status=204) + self.delete(url) # test that the deleted resource is no longer accessible - self.assert_head_and_get_return_same_response(url, expected_status=404) + self.assert_head_and_get_return_same_response( + url, + expected_status=http_client.NOT_FOUND) def test_crud_for_policy_for_explicit_endpoint(self): """PUT, HEAD and DELETE for explicit endpoint policy.""" @@ -94,18 +101,16 @@ class EndpointPolicyTestCase(test_v3.RestfulTestCase): self.put('/policies/%(policy_id)s/OS-ENDPOINT-POLICY' '/endpoints/%(endpoint_id)s' % { 'policy_id': self.policy['id'], - 'endpoint_id': self.endpoint['id']}, - expected_status=204) + 'endpoint_id': self.endpoint['id']}) self.head('/endpoints/%(endpoint_id)s/OS-ENDPOINT-POLICY' '/policy' % { 'endpoint_id': self.endpoint['id']}, - expected_status=200) + expected_status=http_client.OK) r = self.get('/endpoints/%(endpoint_id)s/OS-ENDPOINT-POLICY' '/policy' % { - 'endpoint_id': self.endpoint['id']}, - expected_status=200) + 'endpoint_id': self.endpoint['id']}) self.assertValidPolicyResponse(r, ref=self.policy) def test_list_endpoints_for_policy(self): @@ -114,13 +119,11 @@ class EndpointPolicyTestCase(test_v3.RestfulTestCase): self.put('/policies/%(policy_id)s/OS-ENDPOINT-POLICY' '/endpoints/%(endpoint_id)s' % { 'policy_id': self.policy['id'], - 'endpoint_id': self.endpoint['id']}, - expected_status=204) + 'endpoint_id': self.endpoint['id']}) r = self.get('/policies/%(policy_id)s/OS-ENDPOINT-POLICY' '/endpoints' % { - 'policy_id': self.policy['id']}, - expected_status=200) + 'policy_id': self.policy['id']}) self.assertValidEndpointListResponse(r, ref=self.endpoint) self.assertThat(r.result.get('endpoints'), matchers.HasLength(1)) @@ -130,13 +133,13 @@ class EndpointPolicyTestCase(test_v3.RestfulTestCase): 'policy_id': self.policy['id'], 'endpoint_id': self.endpoint['id']} - self.put(url, expected_status=204) - self.head(url, expected_status=204) + self.put(url) + self.head(url) self.delete('/endpoints/%(endpoint_id)s' % { 'endpoint_id': self.endpoint['id']}) - self.head(url, expected_status=404) + self.head(url, expected_status=http_client.NOT_FOUND) def test_region_service_association_cleanup_when_region_deleted(self): url = ('/policies/%(policy_id)s/OS-ENDPOINT-POLICY' @@ -145,13 +148,13 @@ class EndpointPolicyTestCase(test_v3.RestfulTestCase): 'service_id': self.service['id'], 'region_id': self.region['id']} - self.put(url, expected_status=204) - self.head(url, expected_status=204) + self.put(url) + self.head(url) self.delete('/regions/%(region_id)s' % { 'region_id': self.region['id']}) - self.head(url, expected_status=404) + self.head(url, expected_status=http_client.NOT_FOUND) def test_region_service_association_cleanup_when_service_deleted(self): url = ('/policies/%(policy_id)s/OS-ENDPOINT-POLICY' @@ -160,13 +163,13 @@ class EndpointPolicyTestCase(test_v3.RestfulTestCase): 'service_id': self.service['id'], 'region_id': self.region['id']} - self.put(url, expected_status=204) - self.head(url, expected_status=204) + self.put(url) + self.head(url) self.delete('/services/%(service_id)s' % { 'service_id': self.service['id']}) - self.head(url, expected_status=404) + self.head(url, expected_status=http_client.NOT_FOUND) def test_service_association_cleanup_when_service_deleted(self): url = ('/policies/%(policy_id)s/OS-ENDPOINT-POLICY' @@ -174,13 +177,13 @@ class EndpointPolicyTestCase(test_v3.RestfulTestCase): 'policy_id': self.policy['id'], 'service_id': self.service['id']} - self.put(url, expected_status=204) - self.get(url, expected_status=204) + self.put(url) + self.get(url, expected_status=http_client.NO_CONTENT) self.delete('/policies/%(policy_id)s' % { 'policy_id': self.policy['id']}) - self.head(url, expected_status=404) + self.head(url, expected_status=http_client.NOT_FOUND) def test_service_association_cleanup_when_policy_deleted(self): url = ('/policies/%(policy_id)s/OS-ENDPOINT-POLICY' @@ -188,13 +191,13 @@ class EndpointPolicyTestCase(test_v3.RestfulTestCase): 'policy_id': self.policy['id'], 'service_id': self.service['id']} - self.put(url, expected_status=204) - self.get(url, expected_status=204) + self.put(url) + self.get(url, expected_status=http_client.NO_CONTENT) self.delete('/services/%(service_id)s' % { 'service_id': self.service['id']}) - self.head(url, expected_status=404) + self.head(url, expected_status=http_client.NOT_FOUND) class JsonHomeTests(test_v3.JsonHomeTestMixin): diff --git a/keystone-moon/keystone/tests/unit/test_v3_federation.py b/keystone-moon/keystone/tests/unit/test_v3_federation.py index e646bc0a..5717e67b 100644 --- a/keystone-moon/keystone/tests/unit/test_v3_federation.py +++ b/keystone-moon/keystone/tests/unit/test_v3_federation.py @@ -12,7 +12,6 @@ import os import random -import subprocess from testtools import matchers import uuid @@ -26,12 +25,14 @@ from oslotest import mockpatch import saml2 from saml2 import saml from saml2 import sigver +from six.moves import http_client from six.moves import range, urllib, zip xmldsig = importutils.try_import("saml2.xmldsig") if not xmldsig: xmldsig = importutils.try_import("xmldsig") from keystone.auth import controllers as auth_controllers +from keystone.common import environment from keystone.contrib.federation import controllers as federation_controllers from keystone.contrib.federation import idp as keystone_idp from keystone import exception @@ -44,6 +45,8 @@ from keystone.tests.unit import test_v3 from keystone.token.providers import common as token_common +subprocess = environment.subprocess + CONF = cfg.CONF LOG = log.getLogger(__name__) ROOTDIR = os.path.dirname(os.path.abspath(__file__)) @@ -109,25 +112,43 @@ class FederatedSetupMixin(object): self.assertEqual(token_projects, projects_ref) def _check_scoped_token_attributes(self, token): - def xor_project_domain(token_keys): - return sum(('project' in token_keys, 'domain' in token_keys)) % 2 for obj in ('user', 'catalog', 'expires_at', 'issued_at', 'methods', 'roles'): self.assertIn(obj, token) - # Check for either project or domain - if not xor_project_domain(list(token.keys())): - raise AssertionError("You must specify either" - "project or domain.") - self.assertIn('OS-FEDERATION', token['user']) os_federation = token['user']['OS-FEDERATION'] + + self.assertIn('groups', os_federation) + self.assertIn('identity_provider', os_federation) + self.assertIn('protocol', os_federation) + self.assertThat(os_federation, matchers.HasLength(3)) + self.assertEqual(self.IDP, os_federation['identity_provider']['id']) self.assertEqual(self.PROTOCOL, os_federation['protocol']['id']) - self.assertListEqual(sorted(['groups', - 'identity_provider', - 'protocol']), - sorted(os_federation.keys())) + + def _check_project_scoped_token_attributes(self, token, project_id): + self.assertEqual(project_id, token['project']['id']) + self._check_scoped_token_attributes(token) + + def _check_domain_scoped_token_attributes(self, token, domain_id): + self.assertEqual(domain_id, token['domain']['id']) + self._check_scoped_token_attributes(token) + + def assertValidMappedUser(self, token): + """Check if user object meets all the criteria.""" + + user = token['user'] + self.assertIn('id', user) + self.assertIn('name', user) + self.assertIn('domain', user) + + self.assertIn('groups', user['OS-FEDERATION']) + self.assertIn('identity_provider', user['OS-FEDERATION']) + self.assertIn('protocol', user['OS-FEDERATION']) + + # Make sure user_id is url safe + self.assertEqual(urllib.parse.quote(user['name']), user['id']) def _issue_unscoped_token(self, idp=None, @@ -794,7 +815,7 @@ class FederatedIdentityProviderTests(FederationTests): if body is None: body = self._http_idp_input() resp = self.put(url, body={'identity_provider': body}, - expected_status=201) + expected_status=http_client.CREATED) return resp def _http_idp_input(self, **kwargs): @@ -881,7 +902,7 @@ class FederatedIdentityProviderTests(FederationTests): body['remote_ids'] = [uuid.uuid4().hex, repeated_remote_id] self.put(url, body={'identity_provider': body}, - expected_status=409) + expected_status=http_client.CONFLICT) def test_create_idp_remote_empty(self): """Creates an IdP with empty remote_ids.""" @@ -1006,9 +1027,9 @@ class FederatedIdentityProviderTests(FederationTests): url = self.base_url(suffix=uuid.uuid4().hex) body = self._http_idp_input() self.put(url, body={'identity_provider': body}, - expected_status=201) + expected_status=http_client.CREATED) self.put(url, body={'identity_provider': body}, - expected_status=409) + expected_status=http_client.CONFLICT) def test_get_idp(self): """Create and later fetch IdP.""" @@ -1033,7 +1054,7 @@ class FederatedIdentityProviderTests(FederationTests): self.assertIsNotNone(idp_id) url = self.base_url(suffix=idp_id) - self.get(url, expected_status=404) + self.get(url, expected_status=http_client.NOT_FOUND) def test_delete_existing_idp(self): """Create and later delete IdP. @@ -1047,7 +1068,7 @@ class FederatedIdentityProviderTests(FederationTests): self.assertIsNotNone(idp_id) url = self.base_url(suffix=idp_id) self.delete(url) - self.get(url, expected_status=404) + self.get(url, expected_status=http_client.NOT_FOUND) def test_delete_idp_also_deletes_assigned_protocols(self): """Deleting an IdP will delete its assigned protocol.""" @@ -1063,7 +1084,7 @@ class FederatedIdentityProviderTests(FederationTests): idp_url = self.base_url(suffix=idp_id) # assign protocol to IdP - kwargs = {'expected_status': 201} + kwargs = {'expected_status': http_client.CREATED} resp, idp_id, proto = self._assign_protocol_to_idp( url=url, idp_id=idp_id, @@ -1073,7 +1094,7 @@ class FederatedIdentityProviderTests(FederationTests): # removing IdP will remove the assigned protocol as well self.assertEqual(1, len(self.federation_api.list_protocols(idp_id))) self.delete(idp_url) - self.get(idp_url, expected_status=404) + self.get(idp_url, expected_status=http_client.NOT_FOUND) self.assertEqual(0, len(self.federation_api.list_protocols(idp_id))) def test_delete_nonexisting_idp(self): @@ -1083,7 +1104,7 @@ class FederatedIdentityProviderTests(FederationTests): """ idp_id = uuid.uuid4().hex url = self.base_url(suffix=idp_id) - self.delete(url, expected_status=404) + self.delete(url, expected_status=http_client.NOT_FOUND) def test_update_idp_mutable_attributes(self): """Update IdP's mutable parameters.""" @@ -1124,7 +1145,7 @@ class FederatedIdentityProviderTests(FederationTests): def test_update_idp_immutable_attributes(self): """Update IdP's immutable parameters. - Expect HTTP 403 code. + Expect HTTP FORBIDDEN. """ default_resp = self._create_default_idp() @@ -1138,7 +1159,8 @@ class FederatedIdentityProviderTests(FederationTests): body['protocols'] = [uuid.uuid4().hex, uuid.uuid4().hex] url = self.base_url(suffix=idp_id) - self.patch(url, body={'identity_provider': body}, expected_status=403) + self.patch(url, body={'identity_provider': body}, + expected_status=http_client.FORBIDDEN) def test_update_nonexistent_idp(self): """Update nonexistent IdP @@ -1152,12 +1174,12 @@ class FederatedIdentityProviderTests(FederationTests): body['enabled'] = False body = {'identity_provider': body} - self.patch(url, body=body, expected_status=404) + self.patch(url, body=body, expected_status=http_client.NOT_FOUND) def test_assign_protocol_to_idp(self): """Assign a protocol to existing IdP.""" - self._assign_protocol_to_idp(expected_status=201) + self._assign_protocol_to_idp(expected_status=http_client.CREATED) def test_protocol_composite_pk(self): """Test whether Keystone let's add two entities with identical @@ -1171,7 +1193,7 @@ class FederatedIdentityProviderTests(FederationTests): """ url = self.base_url(suffix='%(idp_id)s/protocols/%(protocol_id)s') - kwargs = {'expected_status': 201} + kwargs = {'expected_status': http_client.CREATED} self._assign_protocol_to_idp(proto='saml2', url=url, **kwargs) @@ -1187,10 +1209,10 @@ class FederatedIdentityProviderTests(FederationTests): """ url = self.base_url(suffix='%(idp_id)s/protocols/%(protocol_id)s') - kwargs = {'expected_status': 201} + kwargs = {'expected_status': http_client.CREATED} resp, idp_id, proto = self._assign_protocol_to_idp(proto='saml2', url=url, **kwargs) - kwargs = {'expected_status': 409} + kwargs = {'expected_status': http_client.CONFLICT} resp, idp_id, proto = self._assign_protocol_to_idp(idp_id=idp_id, proto='saml2', validate=False, @@ -1204,7 +1226,7 @@ class FederatedIdentityProviderTests(FederationTests): """ idp_id = uuid.uuid4().hex - kwargs = {'expected_status': 404} + kwargs = {'expected_status': http_client.NOT_FOUND} self._assign_protocol_to_idp(proto='saml2', idp_id=idp_id, validate=False, @@ -1213,7 +1235,8 @@ class FederatedIdentityProviderTests(FederationTests): def test_get_protocol(self): """Create and later fetch protocol tied to IdP.""" - resp, idp_id, proto = self._assign_protocol_to_idp(expected_status=201) + resp, idp_id, proto = self._assign_protocol_to_idp( + expected_status=http_client.CREATED) proto_id = self._fetch_attribute_from_response(resp, 'protocol')['id'] url = "%s/protocols/%s" % (idp_id, proto_id) url = self.base_url(suffix=url) @@ -1232,12 +1255,14 @@ class FederatedIdentityProviderTests(FederationTests): Compare input and output id sets. """ - resp, idp_id, proto = self._assign_protocol_to_idp(expected_status=201) + resp, idp_id, proto = self._assign_protocol_to_idp( + expected_status=http_client.CREATED) iterations = random.randint(0, 16) protocol_ids = [] for _ in range(iterations): - resp, _, proto = self._assign_protocol_to_idp(idp_id=idp_id, - expected_status=201) + resp, _, proto = self._assign_protocol_to_idp( + idp_id=idp_id, + expected_status=http_client.CREATED) proto_id = self._fetch_attribute_from_response(resp, 'protocol') proto_id = proto_id['id'] protocol_ids.append(proto_id) @@ -1256,7 +1281,8 @@ class FederatedIdentityProviderTests(FederationTests): def test_update_protocols_attribute(self): """Update protocol's attribute.""" - resp, idp_id, proto = self._assign_protocol_to_idp(expected_status=201) + resp, idp_id, proto = self._assign_protocol_to_idp( + expected_status=http_client.CREATED) new_mapping_id = uuid.uuid4().hex url = "%s/protocols/%s" % (idp_id, proto) @@ -1277,11 +1303,12 @@ class FederatedIdentityProviderTests(FederationTests): """ url = self.base_url(suffix='/%(idp_id)s/' 'protocols/%(protocol_id)s') - resp, idp_id, proto = self._assign_protocol_to_idp(expected_status=201) + resp, idp_id, proto = self._assign_protocol_to_idp( + expected_status=http_client.CREATED) url = url % {'idp_id': idp_id, 'protocol_id': proto} self.delete(url) - self.get(url, expected_status=404) + self.get(url, expected_status=http_client.NOT_FOUND) class MappingCRUDTests(FederationTests): @@ -1318,7 +1345,7 @@ class MappingCRUDTests(FederationTests): url = self.MAPPING_URL + uuid.uuid4().hex resp = self.put(url, body={'mapping': mapping_fixtures.MAPPING_LARGE}, - expected_status=201) + expected_status=http_client.CREATED) return resp def _get_id_from_response(self, resp): @@ -1335,7 +1362,7 @@ class MappingCRUDTests(FederationTests): resp = self.get(url) entities = resp.result.get('mappings') self.assertIsNotNone(entities) - self.assertResponseStatus(resp, 200) + self.assertResponseStatus(resp, http_client.OK) self.assertValidListLinks(resp.result.get('links')) self.assertEqual(1, len(entities)) @@ -1345,8 +1372,8 @@ class MappingCRUDTests(FederationTests): mapping_id = self._get_id_from_response(resp) url = url % {'mapping_id': str(mapping_id)} resp = self.delete(url) - self.assertResponseStatus(resp, 204) - self.get(url, expected_status=404) + self.assertResponseStatus(resp, http_client.NO_CONTENT) + self.get(url, expected_status=http_client.NOT_FOUND) def test_mapping_get(self): url = self.MAPPING_URL + '%(mapping_id)s' @@ -1369,70 +1396,73 @@ class MappingCRUDTests(FederationTests): def test_delete_mapping_dne(self): url = self.MAPPING_URL + uuid.uuid4().hex - self.delete(url, expected_status=404) + self.delete(url, expected_status=http_client.NOT_FOUND) def test_get_mapping_dne(self): url = self.MAPPING_URL + uuid.uuid4().hex - self.get(url, expected_status=404) + self.get(url, expected_status=http_client.NOT_FOUND) def test_create_mapping_bad_requirements(self): url = self.MAPPING_URL + uuid.uuid4().hex - self.put(url, expected_status=400, + self.put(url, expected_status=http_client.BAD_REQUEST, body={'mapping': mapping_fixtures.MAPPING_BAD_REQ}) def test_create_mapping_no_rules(self): url = self.MAPPING_URL + uuid.uuid4().hex - self.put(url, expected_status=400, + self.put(url, expected_status=http_client.BAD_REQUEST, body={'mapping': mapping_fixtures.MAPPING_NO_RULES}) def test_create_mapping_no_remote_objects(self): url = self.MAPPING_URL + uuid.uuid4().hex - self.put(url, expected_status=400, + self.put(url, expected_status=http_client.BAD_REQUEST, body={'mapping': mapping_fixtures.MAPPING_NO_REMOTE}) def test_create_mapping_bad_value(self): url = self.MAPPING_URL + uuid.uuid4().hex - self.put(url, expected_status=400, + self.put(url, expected_status=http_client.BAD_REQUEST, body={'mapping': mapping_fixtures.MAPPING_BAD_VALUE}) def test_create_mapping_missing_local(self): url = self.MAPPING_URL + uuid.uuid4().hex - self.put(url, expected_status=400, + self.put(url, expected_status=http_client.BAD_REQUEST, body={'mapping': mapping_fixtures.MAPPING_MISSING_LOCAL}) def test_create_mapping_missing_type(self): url = self.MAPPING_URL + uuid.uuid4().hex - self.put(url, expected_status=400, + self.put(url, expected_status=http_client.BAD_REQUEST, body={'mapping': mapping_fixtures.MAPPING_MISSING_TYPE}) def test_create_mapping_wrong_type(self): url = self.MAPPING_URL + uuid.uuid4().hex - self.put(url, expected_status=400, + self.put(url, expected_status=http_client.BAD_REQUEST, body={'mapping': mapping_fixtures.MAPPING_WRONG_TYPE}) def test_create_mapping_extra_remote_properties_not_any_of(self): url = self.MAPPING_URL + uuid.uuid4().hex mapping = mapping_fixtures.MAPPING_EXTRA_REMOTE_PROPS_NOT_ANY_OF - self.put(url, expected_status=400, body={'mapping': mapping}) + self.put(url, expected_status=http_client.BAD_REQUEST, + body={'mapping': mapping}) def test_create_mapping_extra_remote_properties_any_one_of(self): url = self.MAPPING_URL + uuid.uuid4().hex mapping = mapping_fixtures.MAPPING_EXTRA_REMOTE_PROPS_ANY_ONE_OF - self.put(url, expected_status=400, body={'mapping': mapping}) + self.put(url, expected_status=http_client.BAD_REQUEST, + body={'mapping': mapping}) def test_create_mapping_extra_remote_properties_just_type(self): url = self.MAPPING_URL + uuid.uuid4().hex mapping = mapping_fixtures.MAPPING_EXTRA_REMOTE_PROPS_JUST_TYPE - self.put(url, expected_status=400, body={'mapping': mapping}) + self.put(url, expected_status=http_client.BAD_REQUEST, + body={'mapping': mapping}) def test_create_mapping_empty_map(self): url = self.MAPPING_URL + uuid.uuid4().hex - self.put(url, expected_status=400, + self.put(url, expected_status=http_client.BAD_REQUEST, body={'mapping': {}}) def test_create_mapping_extra_rules_properties(self): url = self.MAPPING_URL + uuid.uuid4().hex - self.put(url, expected_status=400, + self.put(url, expected_status=http_client.BAD_REQUEST, body={'mapping': mapping_fixtures.MAPPING_EXTRA_RULES_PROPS}) def test_create_mapping_with_blacklist_and_whitelist(self): @@ -1444,7 +1474,8 @@ class MappingCRUDTests(FederationTests): """ url = self.MAPPING_URL + uuid.uuid4().hex mapping = mapping_fixtures.MAPPING_GROUPS_WHITELIST_AND_BLACKLIST - self.put(url, expected_status=400, body={'mapping': mapping}) + self.put(url, expected_status=http_client.BAD_REQUEST, + body={'mapping': mapping}) class FederatedTokenTests(FederationTests, FederatedSetupMixin): @@ -1494,6 +1525,7 @@ class FederatedTokenTests(FederationTests, FederatedSetupMixin): def test_issue_unscoped_token(self): r = self._issue_unscoped_token() self.assertIsNotNone(r.headers.get('X-Subject-Token')) + self.assertValidMappedUser(r.json['token']) def test_issue_unscoped_token_disabled_idp(self): """Checks if authentication works with disabled identity providers. @@ -1632,11 +1664,12 @@ class FederatedTokenTests(FederationTests, FederatedSetupMixin): self.TOKEN_SCOPE_PROJECT_EMPLOYEE_FROM_EMPLOYEE) token_resp = r.result['token'] project_id = token_resp['project']['id'] - self.assertEqual(project_id, self.proj_employees['id']) - self._check_scoped_token_attributes(token_resp) + self._check_project_scoped_token_attributes(token_resp, project_id) roles_ref = [self.role_employee] + projects_ref = self.proj_employees self._check_projects_and_roles(token_resp, roles_ref, projects_ref) + self.assertValidMappedUser(token_resp) def test_scope_token_with_idp_disabled(self): """Scope token issued by disabled IdP. @@ -1659,14 +1692,14 @@ class FederatedTokenTests(FederationTests, FederatedSetupMixin): self.federation_api.update_idp(self.IDP, enabled_false) self.v3_authenticate_token( self.TOKEN_SCOPE_PROJECT_EMPLOYEE_FROM_CUSTOMER, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_scope_to_bad_project(self): """Scope unscoped token with a project we don't have access to.""" self.v3_authenticate_token( self.TOKEN_SCOPE_PROJECT_EMPLOYEE_FROM_CUSTOMER, - expected_status=401) + expected_status=http_client.UNAUTHORIZED) def test_scope_to_project_multiple_times(self): """Try to scope the unscoped token multiple times. @@ -1685,9 +1718,8 @@ class FederatedTokenTests(FederationTests, FederatedSetupMixin): for body, project_id_ref in zip(bodies, project_ids): r = self.v3_authenticate_token(body) token_resp = r.result['token'] - project_id = token_resp['project']['id'] - self.assertEqual(project_id, project_id_ref) - self._check_scoped_token_attributes(token_resp) + self._check_project_scoped_token_attributes(token_resp, + project_id_ref) def test_scope_to_project_with_only_inherited_roles(self): """Try to scope token whose only roles are inherited.""" @@ -1695,18 +1727,18 @@ class FederatedTokenTests(FederationTests, FederatedSetupMixin): r = self.v3_authenticate_token( self.TOKEN_SCOPE_PROJECT_INHERITED_FROM_CUSTOMER) token_resp = r.result['token'] - project_id = token_resp['project']['id'] - self.assertEqual(project_id, self.project_inherited['id']) - self._check_scoped_token_attributes(token_resp) + self._check_project_scoped_token_attributes( + token_resp, self.project_inherited['id']) roles_ref = [self.role_customer] projects_ref = self.project_inherited self._check_projects_and_roles(token_resp, roles_ref, projects_ref) + self.assertValidMappedUser(token_resp) def test_scope_token_from_nonexistent_unscoped_token(self): """Try to scope token from non-existent unscoped token.""" self.v3_authenticate_token( self.TOKEN_SCOPE_PROJECT_FROM_NONEXISTENT_TOKEN, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_issue_token_from_rules_without_user(self): api = auth_controllers.Auth() @@ -1730,9 +1762,8 @@ class FederatedTokenTests(FederationTests, FederatedSetupMixin): def test_scope_to_domain_once(self): r = self.v3_authenticate_token(self.TOKEN_SCOPE_DOMAIN_A_FROM_CUSTOMER) token_resp = r.result['token'] - domain_id = token_resp['domain']['id'] - self.assertEqual(self.domainA['id'], domain_id) - self._check_scoped_token_attributes(token_resp) + self._check_domain_scoped_token_attributes(token_resp, + self.domainA['id']) def test_scope_to_domain_multiple_tokens(self): """Issue multiple tokens scoping to different domains. @@ -1754,15 +1785,14 @@ class FederatedTokenTests(FederationTests, FederatedSetupMixin): for body, domain_id_ref in zip(bodies, domain_ids): r = self.v3_authenticate_token(body) token_resp = r.result['token'] - domain_id = token_resp['domain']['id'] - self.assertEqual(domain_id_ref, domain_id) - self._check_scoped_token_attributes(token_resp) + self._check_domain_scoped_token_attributes(token_resp, + domain_id_ref) def test_scope_to_domain_with_only_inherited_roles_fails(self): """Try to scope to a domain that has no direct roles.""" self.v3_authenticate_token( self.TOKEN_SCOPE_DOMAIN_D_FROM_CUSTOMER, - expected_status=401) + expected_status=http_client.UNAUTHORIZED) def test_list_projects(self): urls = ('/OS-FEDERATION/projects', '/auth/projects') @@ -1863,9 +1893,10 @@ class FederatedTokenTests(FederationTests, FederatedSetupMixin): """ r = self._issue_unscoped_token() + token_resp = r.json_body['token'] + self.assertValidMappedUser(token_resp) employee_unscoped_token_id = r.headers.get('X-Subject-Token') - r = self.get('/OS-FEDERATION/projects', - token=employee_unscoped_token_id) + r = self.get('/auth/projects', token=employee_unscoped_token_id) projects = r.result['projects'] random_project = random.randint(0, len(projects)) - 1 project = projects[random_project] @@ -1875,9 +1906,7 @@ class FederatedTokenTests(FederationTests, FederatedSetupMixin): r = self.v3_authenticate_token(v3_scope_request) token_resp = r.result['token'] - project_id = token_resp['project']['id'] - self.assertEqual(project['id'], project_id) - self._check_scoped_token_attributes(token_resp) + self._check_project_scoped_token_attributes(token_resp, project['id']) def test_workflow_with_groups_deletion(self): """Test full workflow with groups deletion before token scoping. @@ -1947,7 +1976,8 @@ class FederatedTokenTests(FederationTests, FederatedSetupMixin): token_id, 'project', self.project_all['id']) - self.v3_authenticate_token(scoped_token, expected_status=500) + self.v3_authenticate_token( + scoped_token, expected_status=http_client.INTERNAL_SERVER_ERROR) def test_lists_with_missing_group_in_backend(self): """Test a mapping that points to a group that does not exist @@ -2379,11 +2409,13 @@ class FernetFederatedTokenTests(FederationTests, FederatedSetupMixin): def test_federated_unscoped_token(self): resp = self._issue_unscoped_token() self.assertEqual(204, len(resp.headers['X-Subject-Token'])) + self.assertValidMappedUser(resp.json_body['token']) def test_federated_unscoped_token_with_multiple_groups(self): assertion = 'ANOTHER_CUSTOMER_ASSERTION' resp = self._issue_unscoped_token(assertion=assertion) - self.assertEqual(232, len(resp.headers['X-Subject-Token'])) + self.assertEqual(226, len(resp.headers['X-Subject-Token'])) + self.assertValidMappedUser(resp.json_body['token']) def test_validate_federated_unscoped_token(self): resp = self._issue_unscoped_token() @@ -2400,9 +2432,9 @@ class FernetFederatedTokenTests(FederationTests, FederatedSetupMixin): """ resp = self._issue_unscoped_token() + self.assertValidMappedUser(resp.json_body['token']) unscoped_token = resp.headers.get('X-Subject-Token') - resp = self.get('/OS-FEDERATION/projects', - token=unscoped_token) + resp = self.get('/auth/projects', token=unscoped_token) projects = resp.result['projects'] random_project = random.randint(0, len(projects)) - 1 project = projects[random_project] @@ -2412,9 +2444,7 @@ class FernetFederatedTokenTests(FederationTests, FederatedSetupMixin): resp = self.v3_authenticate_token(v3_scope_request) token_resp = resp.result['token'] - project_id = token_resp['project']['id'] - self.assertEqual(project['id'], project_id) - self._check_scoped_token_attributes(token_resp) + self._check_project_scoped_token_attributes(token_resp, project['id']) class FederatedTokenTestsMethodToken(FederatedTokenTests): @@ -2499,7 +2529,7 @@ class SAMLGenerationTests(FederationTests): self.sp = self.sp_ref() url = '/OS-FEDERATION/service_providers/' + self.SERVICE_PROVDIER_ID self.put(url, body={'service_provider': self.sp}, - expected_status=201) + expected_status=http_client.CREATED) def test_samlize_token_values(self): """Test the SAML generator produces a SAML object. @@ -2615,8 +2645,8 @@ class SAMLGenerationTests(FederationTests): # the assertion as is without signing it return assertion_content - with mock.patch('subprocess.check_output', - side_effect=mocked_subprocess_check_output): + with mock.patch.object(subprocess, 'check_output', + side_effect=mocked_subprocess_check_output): generator = keystone_idp.SAMLGenerator() response = generator.samlize_token(self.ISSUER, self.RECIPIENT, self.SUBJECT, @@ -2711,7 +2741,7 @@ class SAMLGenerationTests(FederationTests): with mock.patch.object(keystone_idp, '_sign_assertion', return_value=self.signed_assertion): self.post(self.SAML_GENERATION_ROUTE, body=body, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_generate_saml_route(self): """Test that the SAML generation endpoint produces XML. @@ -2733,7 +2763,7 @@ class SAMLGenerationTests(FederationTests): return_value=self.signed_assertion): http_response = self.post(self.SAML_GENERATION_ROUTE, body=body, response_content_type='text/xml', - expected_status=200) + expected_status=http_client.OK) response = etree.fromstring(http_response.result) issuer = response[0] @@ -2774,7 +2804,8 @@ class SAMLGenerationTests(FederationTests): self.SERVICE_PROVDIER_ID) del body['auth']['scope'] - self.post(self.SAML_GENERATION_ROUTE, body=body, expected_status=400) + self.post(self.SAML_GENERATION_ROUTE, body=body, + expected_status=http_client.BAD_REQUEST) def test_invalid_token_body(self): """Test that missing the token in request body raises an exception. @@ -2788,7 +2819,8 @@ class SAMLGenerationTests(FederationTests): self.SERVICE_PROVDIER_ID) del body['auth']['identity']['token'] - self.post(self.SAML_GENERATION_ROUTE, body=body, expected_status=400) + self.post(self.SAML_GENERATION_ROUTE, body=body, + expected_status=http_client.BAD_REQUEST) def test_sp_not_found(self): """Test SAML generation with an invalid service provider ID. @@ -2799,7 +2831,8 @@ class SAMLGenerationTests(FederationTests): sp_id = uuid.uuid4().hex token_id = self._fetch_valid_token() body = self._create_generate_saml_request(token_id, sp_id) - self.post(self.SAML_GENERATION_ROUTE, body=body, expected_status=404) + self.post(self.SAML_GENERATION_ROUTE, body=body, + expected_status=http_client.NOT_FOUND) def test_sp_disabled(self): """Try generating assertion for disabled Service Provider.""" @@ -2811,7 +2844,8 @@ class SAMLGenerationTests(FederationTests): token_id = self._fetch_valid_token() body = self._create_generate_saml_request(token_id, self.SERVICE_PROVDIER_ID) - self.post(self.SAML_GENERATION_ROUTE, body=body, expected_status=403) + self.post(self.SAML_GENERATION_ROUTE, body=body, + expected_status=http_client.FORBIDDEN) def test_token_not_found(self): """Test that an invalid token in the request body raises an exception. @@ -2823,7 +2857,8 @@ class SAMLGenerationTests(FederationTests): token_id = uuid.uuid4().hex body = self._create_generate_saml_request(token_id, self.SERVICE_PROVDIER_ID) - self.post(self.SAML_GENERATION_ROUTE, body=body, expected_status=404) + self.post(self.SAML_GENERATION_ROUTE, body=body, + expected_status=http_client.NOT_FOUND) def test_generate_ecp_route(self): """Test that the ECP generation endpoint produces XML. @@ -2844,7 +2879,7 @@ class SAMLGenerationTests(FederationTests): return_value=self.signed_assertion): http_response = self.post(self.ECP_GENERATION_ROUTE, body=body, response_content_type='text/xml', - expected_status=200) + expected_status=http_client.OK) env_response = etree.fromstring(http_response.result) header = env_response[0] @@ -2879,7 +2914,7 @@ class SAMLGenerationTests(FederationTests): @mock.patch('saml2.create_class_from_xml_string') @mock.patch('oslo_utils.fileutils.write_to_tempfile') - @mock.patch('subprocess.check_output') + @mock.patch.object(subprocess, 'check_output') def test__sign_assertion(self, check_output_mock, write_to_tempfile_mock, create_class_mock): write_to_tempfile_mock.return_value = 'tmp_path' @@ -2890,7 +2925,7 @@ class SAMLGenerationTests(FederationTests): create_class_mock.assert_called_with(saml.Assertion, 'fakeoutput') @mock.patch('oslo_utils.fileutils.write_to_tempfile') - @mock.patch('subprocess.check_output') + @mock.patch.object(subprocess, 'check_output') def test__sign_assertion_exc(self, check_output_mock, write_to_tempfile_mock): # If the command fails the command output is logged. @@ -2903,12 +2938,15 @@ class SAMLGenerationTests(FederationTests): returncode=sample_returncode, cmd=CONF.saml.xmlsec1_binary, output=sample_output) - # FIXME(blk-u): This should raise exception.SAMLSigningError instead, - # but fails with TypeError due to concatenating string to Message, see - # bug 1484735. - self.assertRaises(TypeError, + logger_fixture = self.useFixture(fixtures.LoggerFixture()) + self.assertRaises(exception.SAMLSigningError, keystone_idp._sign_assertion, self.signed_assertion) + expected_log = ( + "Error when signing assertion, reason: Command '%s' returned " + "non-zero exit status %s %s\n" % + (CONF.saml.xmlsec1_binary, sample_returncode, sample_output)) + self.assertEqual(expected_log, logger_fixture.output) @mock.patch('oslo_utils.fileutils.write_to_tempfile') def test__sign_assertion_fileutils_exc(self, write_to_tempfile_mock): @@ -3041,13 +3079,13 @@ class IdPMetadataGenerationTests(FederationTests): self.generator.generate_metadata) def test_get_metadata_with_no_metadata_file_configured(self): - self.get(self.METADATA_URL, expected_status=500) + self.get(self.METADATA_URL, + expected_status=http_client.INTERNAL_SERVER_ERROR) def test_get_metadata(self): self.config_fixture.config( group='saml', idp_metadata_path=XMLDIR + '/idp_saml2_metadata.xml') - r = self.get(self.METADATA_URL, response_content_type='text/xml', - expected_status=200) + r = self.get(self.METADATA_URL, response_content_type='text/xml') self.assertEqual('text/xml', r.headers.get('Content-Type')) reference_file = _load_xml('idp_saml2_metadata.xml') @@ -3070,7 +3108,7 @@ class ServiceProviderTests(FederationTests): self.SP_REF = self.sp_ref() self.SERVICE_PROVIDER = self.put( url, body={'service_provider': self.SP_REF}, - expected_status=201).result + expected_status=http_client.CREATED).result def sp_ref(self): ref = { @@ -3089,19 +3127,19 @@ class ServiceProviderTests(FederationTests): def test_get_service_provider(self): url = self.base_url(suffix=self.SERVICE_PROVIDER_ID) - resp = self.get(url, expected_status=200) + resp = self.get(url) self.assertValidEntity(resp.result['service_provider'], keys_to_check=self.SP_KEYS) def test_get_service_provider_fail(self): url = self.base_url(suffix=uuid.uuid4().hex) - self.get(url, expected_status=404) + self.get(url, expected_status=http_client.NOT_FOUND) def test_create_service_provider(self): url = self.base_url(suffix=uuid.uuid4().hex) sp = self.sp_ref() resp = self.put(url, body={'service_provider': sp}, - expected_status=201) + expected_status=http_client.CREATED) self.assertValidEntity(resp.result['service_provider'], keys_to_check=self.SP_KEYS) @@ -3111,7 +3149,7 @@ class ServiceProviderTests(FederationTests): sp = self.sp_ref() del sp['relay_state_prefix'] resp = self.put(url, body={'service_provider': sp}, - expected_status=201) + expected_status=http_client.CREATED) sp_result = resp.result['service_provider'] self.assertEqual(CONF.saml.relay_state_prefix, sp_result['relay_state_prefix']) @@ -3123,7 +3161,7 @@ class ServiceProviderTests(FederationTests): non_default_prefix = uuid.uuid4().hex sp['relay_state_prefix'] = non_default_prefix resp = self.put(url, body={'service_provider': sp}, - expected_status=201) + expected_status=http_client.CREATED) sp_result = resp.result['service_provider'] self.assertEqual(non_default_prefix, sp_result['relay_state_prefix']) @@ -3134,7 +3172,7 @@ class ServiceProviderTests(FederationTests): sp = self.sp_ref() sp[uuid.uuid4().hex] = uuid.uuid4().hex self.put(url, body={'service_provider': sp}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_list_service_providers(self): """Test listing of service provider objects. @@ -3150,7 +3188,8 @@ class ServiceProviderTests(FederationTests): } for id, sp in ref_service_providers.items(): url = self.base_url(suffix=id) - self.put(url, body={'service_provider': sp}, expected_status=201) + self.put(url, body={'service_provider': sp}, + expected_status=http_client.CREATED) # Insert ids into service provider object, we will compare it with # responses from server and those include 'id' attribute. @@ -3177,15 +3216,14 @@ class ServiceProviderTests(FederationTests): """ new_sp_ref = self.sp_ref() url = self.base_url(suffix=self.SERVICE_PROVIDER_ID) - resp = self.patch(url, body={'service_provider': new_sp_ref}, - expected_status=200) + resp = self.patch(url, body={'service_provider': new_sp_ref}) patch_result = resp.result new_sp_ref['id'] = self.SERVICE_PROVIDER_ID self.assertValidEntity(patch_result['service_provider'], ref=new_sp_ref, keys_to_check=self.SP_KEYS) - resp = self.get(url, expected_status=200) + resp = self.get(url) get_result = resp.result self.assertDictEqual(patch_result['service_provider'], @@ -3201,21 +3239,21 @@ class ServiceProviderTests(FederationTests): new_sp_ref = {'id': uuid.uuid4().hex} url = self.base_url(suffix=self.SERVICE_PROVIDER_ID) self.patch(url, body={'service_provider': new_sp_ref}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_update_service_provider_unknown_parameter(self): new_sp_ref = self.sp_ref() new_sp_ref[uuid.uuid4().hex] = uuid.uuid4().hex url = self.base_url(suffix=self.SERVICE_PROVIDER_ID) self.patch(url, body={'service_provider': new_sp_ref}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_update_service_provider_404(self): new_sp_ref = self.sp_ref() new_sp_ref['description'] = uuid.uuid4().hex url = self.base_url(suffix=uuid.uuid4().hex) self.patch(url, body={'service_provider': new_sp_ref}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_update_sp_relay_state(self): """Update an SP with custome relay state.""" @@ -3223,19 +3261,18 @@ class ServiceProviderTests(FederationTests): non_default_prefix = uuid.uuid4().hex new_sp_ref['relay_state_prefix'] = non_default_prefix url = self.base_url(suffix=self.SERVICE_PROVIDER_ID) - resp = self.patch(url, body={'service_provider': new_sp_ref}, - expected_status=200) + resp = self.patch(url, body={'service_provider': new_sp_ref}) sp_result = resp.result['service_provider'] self.assertEqual(non_default_prefix, sp_result['relay_state_prefix']) def test_delete_service_provider(self): url = self.base_url(suffix=self.SERVICE_PROVIDER_ID) - self.delete(url, expected_status=204) + self.delete(url) def test_delete_service_provider_404(self): url = self.base_url(suffix=uuid.uuid4().hex) - self.delete(url, expected_status=404) + self.delete(url, expected_status=http_client.NOT_FOUND) class WebSSOTests(FederatedTokenTests): @@ -3337,6 +3374,16 @@ class WebSSOTests(FederatedTokenTests): self.api.federated_sso_auth, context, self.PROTOCOL) + def test_identity_provider_specific_federated_authentication(self): + environment = {self.REMOTE_ID_ATTR: self.REMOTE_IDS[0]} + context = {'environment': environment} + query_string = {'origin': self.ORIGIN} + self._inject_assertion(context, 'EMPLOYEE_ASSERTION', query_string) + resp = self.api.federated_idp_specific_sso_auth(context, + self.idp['id'], + self.PROTOCOL) + self.assertIn(self.TRUSTED_DASHBOARD, resp.body) + class K2KServiceCatalogTests(FederationTests): SP1 = 'SP1' diff --git a/keystone-moon/keystone/tests/unit/test_v3_identity.py b/keystone-moon/keystone/tests/unit/test_v3_identity.py index e0090829..3d424cea 100644 --- a/keystone-moon/keystone/tests/unit/test_v3_identity.py +++ b/keystone-moon/keystone/tests/unit/test_v3_identity.py @@ -16,12 +16,14 @@ import logging import uuid import fixtures +import mock from oslo_config import cfg +from six.moves import http_client from testtools import matchers from keystone.common import controller from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import test_v3 @@ -96,17 +98,24 @@ class IdentityTestCase(test_v3.RestfulTestCase): user_id=self.user['id'], password=self.user['password'], project_id=self.project['id']) - r = self.post('/users', body={'user': ref_nd}, auth=auth) + # TODO(henry-nash): Due to bug #1283539 we currently automatically # use the default domain_id if a domain scoped token is not being - # used. Change the code below to expect a failure once this bug is + # used. For now we just check that a deprecation warning has been + # issued. Change the code below to expect a failure once this bug is # fixed. + with mock.patch( + 'oslo_log.versionutils.report_deprecated_feature') as mock_dep: + r = self.post('/users', body={'user': ref_nd}, auth=auth) + self.assertTrue(mock_dep.called) + ref['domain_id'] = CONF.identity.default_domain_id return self.assertValidUserResponse(r, ref) - def test_create_user_400(self): + def test_create_user_bad_request(self): """Call ``POST /users``.""" - self.post('/users', body={'user': {}}, expected_status=400) + self.post('/users', body={'user': {}}, + expected_status=http_client.BAD_REQUEST) def test_list_users(self): """Call ``GET /users``.""" @@ -286,30 +295,31 @@ class IdentityTestCase(test_v3.RestfulTestCase): old_password_auth = self.build_authentication_request( user_id=user_ref['id'], password=password) - r = self.v3_authenticate_token(old_password_auth, expected_status=201) + r = self.v3_authenticate_token(old_password_auth) old_token = r.headers.get('X-Subject-Token') # auth as user with a token should work before a password change old_token_auth = self.build_authentication_request(token=old_token) - self.v3_authenticate_token(old_token_auth, expected_status=201) + self.v3_authenticate_token(old_token_auth) # administrative password reset new_password = uuid.uuid4().hex self.patch('/users/%s' % user_ref['id'], - body={'user': {'password': new_password}}, - expected_status=200) + body={'user': {'password': new_password}}) # auth as user with original password should not work after change - self.v3_authenticate_token(old_password_auth, expected_status=401) + self.v3_authenticate_token(old_password_auth, + expected_status=http_client.UNAUTHORIZED) # auth as user with an old token should not work after change - self.v3_authenticate_token(old_token_auth, expected_status=404) + self.v3_authenticate_token(old_token_auth, + expected_status=http_client.NOT_FOUND) # new password should work new_password_auth = self.build_authentication_request( user_id=user_ref['id'], password=new_password) - self.v3_authenticate_token(new_password_auth, expected_status=201) + self.v3_authenticate_token(new_password_auth) def test_update_user_domain_id(self): """Call ``PATCH /users/{user_id}`` with domain_id.""" @@ -360,7 +370,7 @@ class IdentityTestCase(test_v3.RestfulTestCase): # Confirm token is valid for now self.head('/auth/tokens', headers={'X-Subject-Token': token}, - expected_status=200) + expected_status=http_client.OK) # Now delete the user self.delete('/users/%(user_id)s' % { @@ -389,9 +399,10 @@ class IdentityTestCase(test_v3.RestfulTestCase): body={'group': ref}) return self.assertValidGroupResponse(r, ref) - def test_create_group_400(self): + def test_create_group_bad_request(self): """Call ``POST /groups``.""" - self.post('/groups', body={'group': {}}, expected_status=400) + self.post('/groups', body={'group': {}}, + expected_status=http_client.BAD_REQUEST) def test_list_groups(self): """Call ``GET /groups``.""" @@ -462,14 +473,13 @@ class IdentityTestCase(test_v3.RestfulTestCase): # administrative password reset new_password = uuid.uuid4().hex self.patch('/users/%s' % user_ref['id'], - body={'user': {'password': new_password}}, - expected_status=200) + body={'user': {'password': new_password}}) self.assertNotIn(password, log_fix.output) self.assertNotIn(new_password, log_fix.output) -class IdentityV3toV2MethodsTestCase(tests.TestCase): +class IdentityV3toV2MethodsTestCase(unit.TestCase): """Test users V3 to V2 conversion methods.""" def setUp(self): @@ -549,7 +559,8 @@ class UserSelfServiceChangingPasswordsTestCase(test_v3.RestfulTestCase): password = self.user_ref['password'] self.user_ref = self.identity_api.create_user(self.user_ref) self.user_ref['password'] = password - self.token = self.get_request_token(self.user_ref['password'], 201) + self.token = self.get_request_token(self.user_ref['password'], + http_client.CREATED) def get_request_token(self, password, expected_status): auth_data = self.build_authentication_request( @@ -569,42 +580,45 @@ class UserSelfServiceChangingPasswordsTestCase(test_v3.RestfulTestCase): def test_changing_password(self): # original password works token_id = self.get_request_token(self.user_ref['password'], - expected_status=201) + expected_status=http_client.CREATED) # original token works old_token_auth = self.build_authentication_request(token=token_id) - self.v3_authenticate_token(old_token_auth, expected_status=201) + self.v3_authenticate_token(old_token_auth) # change password new_password = uuid.uuid4().hex self.change_password(password=new_password, original_password=self.user_ref['password'], - expected_status=204) + expected_status=http_client.NO_CONTENT) # old password fails - self.get_request_token(self.user_ref['password'], expected_status=401) + self.get_request_token(self.user_ref['password'], + expected_status=http_client.UNAUTHORIZED) # old token fails - self.v3_authenticate_token(old_token_auth, expected_status=404) + self.v3_authenticate_token(old_token_auth, + expected_status=http_client.NOT_FOUND) # new password works - self.get_request_token(new_password, expected_status=201) + self.get_request_token(new_password, + expected_status=http_client.CREATED) def test_changing_password_with_missing_original_password_fails(self): r = self.change_password(password=uuid.uuid4().hex, - expected_status=400) + expected_status=http_client.BAD_REQUEST) self.assertThat(r.result['error']['message'], matchers.Contains('original_password')) def test_changing_password_with_missing_password_fails(self): r = self.change_password(original_password=self.user_ref['password'], - expected_status=400) + expected_status=http_client.BAD_REQUEST) self.assertThat(r.result['error']['message'], matchers.Contains('password')) def test_changing_password_with_incorrect_password_fails(self): self.change_password(password=uuid.uuid4().hex, original_password=uuid.uuid4().hex, - expected_status=401) + expected_status=http_client.UNAUTHORIZED) def test_changing_password_with_disabled_user_fails(self): # disable the user account @@ -614,7 +628,7 @@ class UserSelfServiceChangingPasswordsTestCase(test_v3.RestfulTestCase): self.change_password(password=uuid.uuid4().hex, original_password=self.user_ref['password'], - expected_status=401) + expected_status=http_client.UNAUTHORIZED) def test_changing_password_not_logged(self): # When a user changes their password, the password isn't logged at any @@ -626,7 +640,7 @@ class UserSelfServiceChangingPasswordsTestCase(test_v3.RestfulTestCase): new_password = uuid.uuid4().hex self.change_password(password=new_password, original_password=self.user_ref['password'], - expected_status=204) + expected_status=http_client.NO_CONTENT) self.assertNotIn(self.user_ref['password'], log_fix.output) self.assertNotIn(new_password, log_fix.output) diff --git a/keystone-moon/keystone/tests/unit/test_v3_oauth1.py b/keystone-moon/keystone/tests/unit/test_v3_oauth1.py index 6c063c5e..3a0d481c 100644 --- a/keystone-moon/keystone/tests/unit/test_v3_oauth1.py +++ b/keystone-moon/keystone/tests/unit/test_v3_oauth1.py @@ -18,6 +18,7 @@ import uuid from oslo_config import cfg from oslo_serialization import jsonutils from pycadf import cadftaxonomy +from six.moves import http_client from six.moves import urllib from keystone.contrib import oauth1 @@ -139,7 +140,7 @@ class ConsumerCRUDTests(OAuth1Tests): consumer = self._create_single_consumer() consumer_id = consumer['id'] resp = self.delete(self.CONSUMER_URL + '/%s' % consumer_id) - self.assertResponseStatus(resp, 204) + self.assertResponseStatus(resp, http_client.NO_CONTENT) def test_consumer_get(self): consumer = self._create_single_consumer() @@ -182,7 +183,7 @@ class ConsumerCRUDTests(OAuth1Tests): update_ref['secret'] = uuid.uuid4().hex self.patch(self.CONSUMER_URL + '/%s' % original_id, body={'consumer': update_ref}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_consumer_update_bad_id(self): consumer = self._create_single_consumer() @@ -195,7 +196,7 @@ class ConsumerCRUDTests(OAuth1Tests): update_ref['id'] = update_description self.patch(self.CONSUMER_URL + '/%s' % original_id, body={'consumer': update_ref}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_consumer_update_normalize_field(self): # If update a consumer with a field with : or - in the name, @@ -236,7 +237,7 @@ class ConsumerCRUDTests(OAuth1Tests): def test_consumer_get_bad_id(self): self.get(self.CONSUMER_URL + '/%(consumer_id)s' % {'consumer_id': uuid.uuid4().hex}, - expected_status=404) + expected_status=http_client.NOT_FOUND) class OAuthFlowTests(OAuth1Tests): @@ -261,7 +262,7 @@ class OAuthFlowTests(OAuth1Tests): url = self._authorize_request_token(request_key) body = {'roles': [{'id': self.role_id}]} - resp = self.put(url, body=body, expected_status=200) + resp = self.put(url, body=body, expected_status=http_client.OK) self.verifier = resp.result['token']['oauth_verifier'] self.assertTrue(all(i in core.VERIFIER_CHARS for i in self.verifier)) self.assertEqual(8, len(self.verifier)) @@ -291,7 +292,7 @@ class AccessTokenCRUDTests(OAuthFlowTests): self.delete('/users/%(user)s/OS-OAUTH1/access_tokens/%(auth)s' % {'user': self.user_id, 'auth': uuid.uuid4().hex}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_list_no_access_tokens(self): resp = self.get('/users/%(user_id)s/OS-OAUTH1/access_tokens' @@ -316,7 +317,7 @@ class AccessTokenCRUDTests(OAuthFlowTests): self.get('/users/%(user_id)s/OS-OAUTH1/access_tokens/%(key)s' % {'user_id': self.user_id, 'key': uuid.uuid4().hex}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_list_all_roles_in_access_token(self): self.test_oauth_flow() @@ -341,7 +342,7 @@ class AccessTokenCRUDTests(OAuthFlowTests): url = ('/users/%(id)s/OS-OAUTH1/access_tokens/%(key)s/roles/%(role)s' % {'id': self.user_id, 'key': self.access_token.key, 'role': uuid.uuid4().hex}) - self.get(url, expected_status=404) + self.get(url, expected_status=http_client.NOT_FOUND) def test_list_and_delete_access_tokens(self): self.test_oauth_flow() @@ -356,7 +357,7 @@ class AccessTokenCRUDTests(OAuthFlowTests): resp = self.delete('/users/%(user)s/OS-OAUTH1/access_tokens/%(auth)s' % {'user': self.user_id, 'auth': self.access_token.key}) - self.assertResponseStatus(resp, 204) + self.assertResponseStatus(resp, http_client.NO_CONTENT) # List access_token should be 0 resp = self.get('/users/%(user_id)s/OS-OAUTH1/access_tokens' @@ -399,13 +400,13 @@ class AuthTokenTests(OAuthFlowTests): resp = self.delete('/users/%(user)s/OS-OAUTH1/access_tokens/%(auth)s' % {'user': self.user_id, 'auth': self.access_token.key}) - self.assertResponseStatus(resp, 204) + self.assertResponseStatus(resp, http_client.NO_CONTENT) # Check Keystone Token no longer exists headers = {'X-Subject-Token': self.keystone_token_id, 'X-Auth-Token': self.keystone_token_id} self.get('/auth/tokens', headers=headers, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_deleting_consumer_also_deletes_tokens(self): self.test_oauth_flow() @@ -414,7 +415,7 @@ class AuthTokenTests(OAuthFlowTests): consumer_id = self.consumer['key'] resp = self.delete('/OS-OAUTH1/consumers/%(consumer_id)s' % {'consumer_id': consumer_id}) - self.assertResponseStatus(resp, 204) + self.assertResponseStatus(resp, http_client.NO_CONTENT) # List access_token should be 0 resp = self.get('/users/%(user_id)s/OS-OAUTH1/access_tokens' @@ -426,7 +427,7 @@ class AuthTokenTests(OAuthFlowTests): headers = {'X-Subject-Token': self.keystone_token_id, 'X-Auth-Token': self.keystone_token_id} self.head('/auth/tokens', headers=headers, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_change_user_password_also_deletes_tokens(self): self.test_oauth_flow() @@ -445,7 +446,7 @@ class AuthTokenTests(OAuthFlowTests): headers = {'X-Subject-Token': self.keystone_token_id, 'X-Auth-Token': self.keystone_token_id} self.admin_request(path='/auth/tokens', headers=headers, - method='GET', expected_status=404) + method='GET', expected_status=http_client.NOT_FOUND) def test_deleting_project_also_invalidates_tokens(self): self.test_oauth_flow() @@ -462,7 +463,7 @@ class AuthTokenTests(OAuthFlowTests): headers = {'X-Subject-Token': self.keystone_token_id, 'X-Auth-Token': self.keystone_token_id} self.admin_request(path='/auth/tokens', headers=headers, - method='GET', expected_status=404) + method='GET', expected_status=http_client.NOT_FOUND) def test_token_chaining_is_not_allowed(self): self.test_oauth_flow() @@ -477,7 +478,7 @@ class AuthTokenTests(OAuthFlowTests): body=auth_data, token=self.keystone_token_id, method='POST', - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_delete_keystone_tokens_by_consumer_id(self): self.test_oauth_flow() @@ -545,14 +546,14 @@ class AuthTokenTests(OAuthFlowTests): self.post('/OS-TRUST/trusts', body={'trust': ref}, token=self.keystone_token_id, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_oauth_token_cannot_authorize_request_token(self): self.test_oauth_flow() url = self._approve_request_token_url() body = {'roles': [{'id': self.role_id}]} self.put(url, body=body, token=self.keystone_token_id, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_oauth_token_cannot_list_request_tokens(self): self._set_policy({"identity:list_access_tokens": [], @@ -561,7 +562,7 @@ class AuthTokenTests(OAuthFlowTests): self.test_oauth_flow() url = '/users/%s/OS-OAUTH1/access_tokens' % self.user_id self.get(url, token=self.keystone_token_id, - expected_status=403) + expected_status=http_client.FORBIDDEN) def _set_policy(self, new_policy): self.tempfile = self.useFixture(temporaryfile.SecureTempFile()) @@ -575,14 +576,16 @@ class AuthTokenTests(OAuthFlowTests): trust_token = self._create_trust_get_token() url = self._approve_request_token_url() body = {'roles': [{'id': self.role_id}]} - self.put(url, body=body, token=trust_token, expected_status=403) + self.put(url, body=body, token=trust_token, + expected_status=http_client.FORBIDDEN) def test_trust_token_cannot_list_request_tokens(self): self._set_policy({"identity:list_access_tokens": [], "identity:create_trust": []}) trust_token = self._create_trust_get_token() url = '/users/%s/OS-OAUTH1/access_tokens' % self.user_id - self.get(url, token=trust_token, expected_status=403) + self.get(url, token=trust_token, + expected_status=http_client.FORBIDDEN) class MaliciousOAuth1Tests(OAuth1Tests): @@ -592,7 +595,8 @@ class MaliciousOAuth1Tests(OAuth1Tests): consumer_id = consumer['id'] consumer = {'key': consumer_id, 'secret': uuid.uuid4().hex} url, headers = self._create_request_token(consumer, self.project_id) - self.post(url, headers=headers, expected_status=401) + self.post(url, headers=headers, + expected_status=http_client.UNAUTHORIZED) def test_bad_request_token_key(self): consumer = self._create_single_consumer() @@ -605,7 +609,7 @@ class MaliciousOAuth1Tests(OAuth1Tests): response_content_type='application/x-www-urlformencoded') url = self._authorize_request_token(uuid.uuid4().hex) body = {'roles': [{'id': self.role_id}]} - self.put(url, body=body, expected_status=404) + self.put(url, body=body, expected_status=http_client.NOT_FOUND) def test_bad_consumer_id(self): consumer = self._create_single_consumer() @@ -613,7 +617,7 @@ class MaliciousOAuth1Tests(OAuth1Tests): consumer_secret = consumer['secret'] consumer = {'key': consumer_id, 'secret': consumer_secret} url, headers = self._create_request_token(consumer, self.project_id) - self.post(url, headers=headers, expected_status=404) + self.post(url, headers=headers, expected_status=http_client.NOT_FOUND) def test_bad_requested_project_id(self): consumer = self._create_single_consumer() @@ -622,7 +626,7 @@ class MaliciousOAuth1Tests(OAuth1Tests): consumer = {'key': consumer_id, 'secret': consumer_secret} project_id = uuid.uuid4().hex url, headers = self._create_request_token(consumer, project_id) - self.post(url, headers=headers, expected_status=404) + self.post(url, headers=headers, expected_status=http_client.NOT_FOUND) def test_bad_verifier(self): consumer = self._create_single_consumer() @@ -641,13 +645,14 @@ class MaliciousOAuth1Tests(OAuth1Tests): url = self._authorize_request_token(request_key) body = {'roles': [{'id': self.role_id}]} - resp = self.put(url, body=body, expected_status=200) + resp = self.put(url, body=body, expected_status=http_client.OK) verifier = resp.result['token']['oauth_verifier'] self.assertIsNotNone(verifier) request_token.set_verifier(uuid.uuid4().hex) url, headers = self._create_access_token(consumer, request_token) - self.post(url, headers=headers, expected_status=401) + self.post(url, headers=headers, + expected_status=http_client.UNAUTHORIZED) def test_bad_authorizing_roles(self): consumer = self._create_single_consumer() @@ -667,7 +672,7 @@ class MaliciousOAuth1Tests(OAuth1Tests): url = self._authorize_request_token(request_key) body = {'roles': [{'id': self.role_id}]} self.admin_request(path=url, method='PUT', - body=body, expected_status=404) + body=body, expected_status=http_client.NOT_FOUND) def test_expired_authorizing_request_token(self): self.config_fixture.config(group='oauth1', request_token_duration=-1) @@ -691,7 +696,7 @@ class MaliciousOAuth1Tests(OAuth1Tests): url = self._authorize_request_token(request_key) body = {'roles': [{'id': self.role_id}]} - self.put(url, body=body, expected_status=401) + self.put(url, body=body, expected_status=http_client.UNAUTHORIZED) def test_expired_creating_keystone_token(self): self.config_fixture.config(group='oauth1', access_token_duration=-1) @@ -714,7 +719,7 @@ class MaliciousOAuth1Tests(OAuth1Tests): url = self._authorize_request_token(request_key) body = {'roles': [{'id': self.role_id}]} - resp = self.put(url, body=body, expected_status=200) + resp = self.put(url, body=body, expected_status=http_client.OK) self.verifier = resp.result['token']['oauth_verifier'] self.request_token.set_verifier(self.verifier) @@ -731,7 +736,8 @@ class MaliciousOAuth1Tests(OAuth1Tests): url, headers, body = self._get_oauth_token(self.consumer, self.access_token) - self.post(url, headers=headers, body=body, expected_status=401) + self.post(url, headers=headers, body=body, + expected_status=http_client.UNAUTHORIZED) def test_missing_oauth_headers(self): endpoint = '/OS-OAUTH1/request_token' @@ -747,7 +753,8 @@ class MaliciousOAuth1Tests(OAuth1Tests): # NOTE(stevemar): To simulate this error, we remove the Authorization # header from the post request. del headers['Authorization'] - self.post(endpoint, headers=headers, expected_status=500) + self.post(endpoint, headers=headers, + expected_status=http_client.INTERNAL_SERVER_ERROR) class OAuthNotificationTests(OAuth1Tests, @@ -823,7 +830,7 @@ class OAuthNotificationTests(OAuth1Tests, url = self._authorize_request_token(request_key) body = {'roles': [{'id': self.role_id}]} - resp = self.put(url, body=body, expected_status=200) + resp = self.put(url, body=body, expected_status=http_client.OK) self.verifier = resp.result['token']['oauth_verifier'] self.assertTrue(all(i in core.VERIFIER_CHARS for i in self.verifier)) self.assertEqual(8, len(self.verifier)) @@ -852,7 +859,7 @@ class OAuthNotificationTests(OAuth1Tests, resp = self.delete('/users/%(user)s/OS-OAUTH1/access_tokens/%(auth)s' % {'user': self.user_id, 'auth': self.access_token.key}) - self.assertResponseStatus(resp, 204) + self.assertResponseStatus(resp, http_client.NO_CONTENT) # Test to ensure the delete access token notification is sent self._assert_notify_sent(access_key, diff --git a/keystone-moon/keystone/tests/unit/test_v3_os_revoke.py b/keystone-moon/keystone/tests/unit/test_v3_os_revoke.py index 48226cd4..86ced724 100644 --- a/keystone-moon/keystone/tests/unit/test_v3_os_revoke.py +++ b/keystone-moon/keystone/tests/unit/test_v3_os_revoke.py @@ -15,6 +15,7 @@ import uuid from oslo_utils import timeutils import six +from six.moves import http_client from testtools import matchers from keystone.common import utils @@ -112,7 +113,8 @@ class OSRevokeTests(test_v3.RestfulTestCase, test_v3.JsonHomeTestMixin): self.assertReportedEventMatchesRecorded(events[0], sample, before_time) def test_list_since_invalid(self): - self.get('/OS-REVOKE/events?since=blah', expected_status=400) + self.get('/OS-REVOKE/events?since=blah', + expected_status=http_client.BAD_REQUEST) def test_list_since_valid(self): resp = self.get('/OS-REVOKE/events?since=2013-02-27T18:30:59.999999Z') diff --git a/keystone-moon/keystone/tests/unit/test_v3_protection.py b/keystone-moon/keystone/tests/unit/test_v3_protection.py index 458c61de..296e1d4b 100644 --- a/keystone-moon/keystone/tests/unit/test_v3_protection.py +++ b/keystone-moon/keystone/tests/unit/test_v3_protection.py @@ -17,10 +17,11 @@ import uuid from oslo_config import cfg from oslo_serialization import jsonutils +from six.moves import http_client from keystone import exception from keystone.policy.backends import rules -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit.ksfixtures import temporaryfile from keystone.tests.unit import test_v3 @@ -428,7 +429,8 @@ class IdentityTestPolicySample(test_v3.RestfulTestCase): user2_token = self.get_requested_token(user2_auth) self.get('/auth/tokens', token=user1_token, - headers={'X-Subject-Token': user2_token}, expected_status=403) + headers={'X-Subject-Token': user2_token}, + expected_status=http_client.FORBIDDEN) def test_admin_validate_user_token(self): # An admin can validate a user's token. @@ -459,7 +461,8 @@ class IdentityTestPolicySample(test_v3.RestfulTestCase): token = self.get_requested_token(auth) self.head('/auth/tokens', token=token, - headers={'X-Subject-Token': token}, expected_status=200) + headers={'X-Subject-Token': token}, + expected_status=http_client.OK) def test_user_check_user_token(self): # A user can check one of their own tokens. @@ -472,7 +475,8 @@ class IdentityTestPolicySample(test_v3.RestfulTestCase): token2 = self.get_requested_token(auth) self.head('/auth/tokens', token=token1, - headers={'X-Subject-Token': token2}, expected_status=200) + headers={'X-Subject-Token': token2}, + expected_status=http_client.OK) def test_user_check_other_user_token_rejected(self): # A user cannot check another user's token. @@ -490,7 +494,7 @@ class IdentityTestPolicySample(test_v3.RestfulTestCase): self.head('/auth/tokens', token=user1_token, headers={'X-Subject-Token': user2_token}, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_admin_check_user_token(self): # An admin can check a user's token. @@ -508,7 +512,8 @@ class IdentityTestPolicySample(test_v3.RestfulTestCase): user_token = self.get_requested_token(user_auth) self.head('/auth/tokens', token=admin_token, - headers={'X-Subject-Token': user_token}, expected_status=200) + headers={'X-Subject-Token': user_token}, + expected_status=http_client.OK) def test_user_revoke_same_token(self): # Given a non-admin user token, the token can be used to revoke @@ -552,7 +557,7 @@ class IdentityTestPolicySample(test_v3.RestfulTestCase): self.delete('/auth/tokens', token=user1_token, headers={'X-Subject-Token': user2_token}, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_admin_revoke_user_token(self): # An admin can revoke a user's token. @@ -607,7 +612,7 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase, rules.reset() self.config_fixture.config( group='oslo_policy', - policy_file=tests.dirs.etc('policy.v3cloudsample.json')) + policy_file=unit.dirs.etc('policy.v3cloudsample.json')) def load_sample_data(self): # Start by creating a couple of domains @@ -681,7 +686,8 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase, # Return the expected return codes for APIs with and without data # with any specified status overriding the normal values if expected_status is None: - return (200, 201, 204) + return (http_client.OK, http_client.CREATED, + http_client.NO_CONTENT) else: return (expected_status, expected_status, expected_status) @@ -948,7 +954,8 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase, collection_url = self.build_role_assignment_query_url( domain_id=self.domainB['id']) - self.get(collection_url, auth=self.auth, expected_status=403) + self.get(collection_url, auth=self.auth, + expected_status=http_client.FORBIDDEN) def test_domain_user_list_assignments_of_domain_failed(self): self.auth = self.build_authentication_request( @@ -958,7 +965,8 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase, collection_url = self.build_role_assignment_query_url( domain_id=self.domainA['id']) - self.get(collection_url, auth=self.auth, expected_status=403) + self.get(collection_url, auth=self.auth, + expected_status=http_client.FORBIDDEN) def test_cloud_admin_list_assignments_of_project(self): self.auth = self.build_authentication_request( @@ -986,7 +994,7 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase, self.assertRoleAssignmentInListResponse(r, project_admin_entity) self.assertRoleAssignmentInListResponse(r, project_user_entity) - @tests.utils.wip('waiting on bug #1437407') + @unit.utils.wip('waiting on bug #1437407') def test_domain_admin_list_assignments_of_project(self): self.auth = self.build_authentication_request( user_id=self.domain_admin_user['id'], @@ -1021,7 +1029,8 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase, collection_url = self.build_role_assignment_query_url( project_id=self.project['id']) - self.get(collection_url, auth=self.auth, expected_status=403) + self.get(collection_url, auth=self.auth, + expected_status=http_client.FORBIDDEN) def test_cloud_admin(self): self.auth = self.build_authentication_request( @@ -1045,7 +1054,7 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase, password=self.domain_admin_user['password'], domain_id=self.domainA['id']) entity_url = '/domains/%s' % self.domainA['id'] - self.get(entity_url, auth=self.auth, expected_status=200) + self.get(entity_url, auth=self.auth) def test_list_user_credentials(self): self.credential_user = self.new_credential_ref(self.just_a_user['id']) @@ -1145,7 +1154,8 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase, user2_token = self.get_requested_token(user2_auth) self.get('/auth/tokens', token=user1_token, - headers={'X-Subject-Token': user2_token}, expected_status=403) + headers={'X-Subject-Token': user2_token}, + expected_status=http_client.FORBIDDEN) def test_admin_validate_user_token(self): # An admin can validate a user's token. @@ -1176,7 +1186,8 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase, token = self.get_requested_token(auth) self.head('/auth/tokens', token=token, - headers={'X-Subject-Token': token}, expected_status=200) + headers={'X-Subject-Token': token}, + expected_status=http_client.OK) def test_user_check_user_token(self): # A user can check one of their own tokens. @@ -1189,7 +1200,8 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase, token2 = self.get_requested_token(auth) self.head('/auth/tokens', token=token1, - headers={'X-Subject-Token': token2}, expected_status=200) + headers={'X-Subject-Token': token2}, + expected_status=http_client.OK) def test_user_check_other_user_token_rejected(self): # A user cannot check another user's token. @@ -1207,7 +1219,7 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase, self.head('/auth/tokens', token=user1_token, headers={'X-Subject-Token': user2_token}, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_admin_check_user_token(self): # An admin can check a user's token. @@ -1225,7 +1237,8 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase, user_token = self.get_requested_token(user_auth) self.head('/auth/tokens', token=admin_token, - headers={'X-Subject-Token': user_token}, expected_status=200) + headers={'X-Subject-Token': user_token}, + expected_status=http_client.OK) def test_user_revoke_same_token(self): # Given a non-admin user token, the token can be used to revoke @@ -1269,7 +1282,7 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase, self.delete('/auth/tokens', token=user1_token, headers={'X-Subject-Token': user2_token}, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_admin_revoke_user_token(self): # An admin can revoke a user's token. diff --git a/keystone-moon/keystone/tests/unit/test_versions.py b/keystone-moon/keystone/tests/unit/test_versions.py index 7f722f94..fc8051b2 100644 --- a/keystone-moon/keystone/tests/unit/test_versions.py +++ b/keystone-moon/keystone/tests/unit/test_versions.py @@ -20,11 +20,13 @@ import random import mock from oslo_config import cfg from oslo_serialization import jsonutils +from six.moves import http_client from testtools import matchers as tt_matchers +import webob from keystone.common import json_home from keystone import controllers -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import utils @@ -170,6 +172,8 @@ BASE_ACCESS_TOKEN = ( # TODO(stevemar): Use BASE_IDP_PROTOCOL when bug 1420125 is resolved. FEDERATED_AUTH_URL = ('/OS-FEDERATION/identity_providers/{identity_provider}' '/protocols/{protocol}/auth') +FEDERATED_IDP_SPECIFIC_WEBSSO = ('/auth/OS-FEDERATION/identity_providers/' + '{idp_id}/protocols/{protocol_id}/websso') V3_JSON_HOME_RESOURCES_INHERIT_DISABLED = { json_home.build_v3_resource_relation('auth_tokens'): { @@ -345,13 +349,13 @@ V3_JSON_HOME_RESOURCES_INHERIT_DISABLED = { 'href-vars': {'user_id': json_home.Parameters.USER_ID, }}, json_home.build_v3_resource_relation('users'): {'href': '/users'}, _build_federation_rel(resource_name='domains'): { - 'href': '/OS-FEDERATION/domains'}, + 'href': '/auth/domains'}, _build_federation_rel(resource_name='websso'): { 'href-template': '/auth/OS-FEDERATION/websso/{protocol_id}', 'href-vars': { 'protocol_id': PROTOCOL_ID_PARAM_RELATION, }}, _build_federation_rel(resource_name='projects'): { - 'href': '/OS-FEDERATION/projects'}, + 'href': '/auth/projects'}, _build_federation_rel(resource_name='saml2'): { 'href': '/auth/OS-FEDERATION/saml2'}, _build_federation_rel(resource_name='ecp'): { @@ -368,6 +372,11 @@ V3_JSON_HOME_RESOURCES_INHERIT_DISABLED = { { 'href-template': '/OS-FEDERATION/identity_providers/{idp_id}', 'href-vars': {'idp_id': IDP_ID_PARAMETER_RELATION, }}, + _build_federation_rel(resource_name='identity_providers'): { + 'href-template': FEDERATED_IDP_SPECIFIC_WEBSSO, + 'href-vars': { + 'idp_id': IDP_ID_PARAMETER_RELATION, + 'protocol_id': PROTOCOL_ID_PARAM_RELATION, }}, _build_federation_rel(resource_name='service_provider'): { 'href-template': '/OS-FEDERATION/service_providers/{sp_id}', @@ -614,6 +623,36 @@ V3_JSON_HOME_RESOURCES_INHERIT_ENABLED.update( ) +class TestClient(object): + def __init__(self, app=None, token=None): + self.app = app + self.token = token + + def request(self, method, path, headers=None, body=None): + if headers is None: + headers = {} + + if self.token: + headers.setdefault('X-Auth-Token', self.token) + + req = webob.Request.blank(path) + req.method = method + for k, v in headers.items(): + req.headers[k] = v + if body: + req.body = body + return req.get_response(self.app) + + def get(self, path, headers=None): + return self.request('GET', path=path, headers=headers) + + def post(self, path, headers=None, body=None): + return self.request('POST', path=path, headers=headers, body=body) + + def put(self, path, headers=None, body=None): + return self.request('PUT', path=path, headers=headers, body=body) + + class _VersionsEqual(tt_matchers.MatchesListwise): def __init__(self, expected): super(_VersionsEqual, self).__init__([ @@ -632,7 +671,7 @@ class _VersionsEqual(tt_matchers.MatchesListwise): ]) -class VersionTestCase(tests.TestCase): +class VersionTestCase(unit.TestCase): def setUp(self): super(VersionTestCase, self).setUp() self.load_backends() @@ -657,7 +696,7 @@ class VersionTestCase(tests.TestCase): link['href'] = port def test_public_versions(self): - client = tests.TestClient(self.public_app) + client = TestClient(self.public_app) resp = client.get('/') self.assertEqual(300, resp.status_int) data = jsonutils.loads(resp.body) @@ -674,7 +713,7 @@ class VersionTestCase(tests.TestCase): self.assertThat(data, _VersionsEqual(expected)) def test_admin_versions(self): - client = tests.TestClient(self.admin_app) + client = TestClient(self.admin_app) resp = client.get('/') self.assertEqual(300, resp.status_int) data = jsonutils.loads(resp.body) @@ -694,7 +733,7 @@ class VersionTestCase(tests.TestCase): self.config_fixture.config(public_endpoint=None, admin_endpoint=None) for app in (self.public_app, self.admin_app): - client = tests.TestClient(app) + client = TestClient(app) resp = client.get('/') self.assertEqual(300, resp.status_int) data = jsonutils.loads(resp.body) @@ -710,9 +749,9 @@ class VersionTestCase(tests.TestCase): self.assertThat(data, _VersionsEqual(expected)) def test_public_version_v2(self): - client = tests.TestClient(self.public_app) + client = TestClient(self.public_app) resp = client.get('/v2.0/') - self.assertEqual(200, resp.status_int) + self.assertEqual(http_client.OK, resp.status_int) data = jsonutils.loads(resp.body) expected = v2_VERSION_RESPONSE self._paste_in_port(expected['version'], @@ -721,9 +760,9 @@ class VersionTestCase(tests.TestCase): self.assertEqual(expected, data) def test_admin_version_v2(self): - client = tests.TestClient(self.admin_app) + client = TestClient(self.admin_app) resp = client.get('/v2.0/') - self.assertEqual(200, resp.status_int) + self.assertEqual(http_client.OK, resp.status_int) data = jsonutils.loads(resp.body) expected = v2_VERSION_RESPONSE self._paste_in_port(expected['version'], @@ -734,18 +773,18 @@ class VersionTestCase(tests.TestCase): def test_use_site_url_if_endpoint_unset_v2(self): self.config_fixture.config(public_endpoint=None, admin_endpoint=None) for app in (self.public_app, self.admin_app): - client = tests.TestClient(app) + client = TestClient(app) resp = client.get('/v2.0/') - self.assertEqual(200, resp.status_int) + self.assertEqual(http_client.OK, resp.status_int) data = jsonutils.loads(resp.body) expected = v2_VERSION_RESPONSE self._paste_in_port(expected['version'], 'http://localhost/v2.0/') self.assertEqual(data, expected) def test_public_version_v3(self): - client = tests.TestClient(self.public_app) + client = TestClient(self.public_app) resp = client.get('/v3/') - self.assertEqual(200, resp.status_int) + self.assertEqual(http_client.OK, resp.status_int) data = jsonutils.loads(resp.body) expected = v3_VERSION_RESPONSE self._paste_in_port(expected['version'], @@ -755,9 +794,9 @@ class VersionTestCase(tests.TestCase): @utils.wip('waiting on bug #1381961') def test_admin_version_v3(self): - client = tests.TestClient(self.admin_app) + client = TestClient(self.admin_app) resp = client.get('/v3/') - self.assertEqual(200, resp.status_int) + self.assertEqual(http_client.OK, resp.status_int) data = jsonutils.loads(resp.body) expected = v3_VERSION_RESPONSE self._paste_in_port(expected['version'], @@ -768,9 +807,9 @@ class VersionTestCase(tests.TestCase): def test_use_site_url_if_endpoint_unset_v3(self): self.config_fixture.config(public_endpoint=None, admin_endpoint=None) for app in (self.public_app, self.admin_app): - client = tests.TestClient(app) + client = TestClient(app) resp = client.get('/v3/') - self.assertEqual(200, resp.status_int) + self.assertEqual(http_client.OK, resp.status_int) data = jsonutils.loads(resp.body) expected = v3_VERSION_RESPONSE self._paste_in_port(expected['version'], 'http://localhost/v3/') @@ -778,14 +817,14 @@ class VersionTestCase(tests.TestCase): @mock.patch.object(controllers, '_VERSIONS', ['v3']) def test_v2_disabled(self): - client = tests.TestClient(self.public_app) + client = TestClient(self.public_app) # request to /v2.0 should fail resp = client.get('/v2.0/') - self.assertEqual(404, resp.status_int) + self.assertEqual(http_client.NOT_FOUND, resp.status_int) # request to /v3 should pass resp = client.get('/v3/') - self.assertEqual(200, resp.status_int) + self.assertEqual(http_client.OK, resp.status_int) data = jsonutils.loads(resp.body) expected = v3_VERSION_RESPONSE self._paste_in_port(expected['version'], @@ -811,14 +850,14 @@ class VersionTestCase(tests.TestCase): @mock.patch.object(controllers, '_VERSIONS', ['v2.0']) def test_v3_disabled(self): - client = tests.TestClient(self.public_app) + client = TestClient(self.public_app) # request to /v3 should fail resp = client.get('/v3/') - self.assertEqual(404, resp.status_int) + self.assertEqual(http_client.NOT_FOUND, resp.status_int) # request to /v2.0 should pass resp = client.get('/v2.0/') - self.assertEqual(200, resp.status_int) + self.assertEqual(http_client.OK, resp.status_int) data = jsonutils.loads(resp.body) expected = v2_VERSION_RESPONSE self._paste_in_port(expected['version'], @@ -843,7 +882,7 @@ class VersionTestCase(tests.TestCase): self.assertEqual(v2_only_response, data) def _test_json_home(self, path, exp_json_home_data): - client = tests.TestClient(self.public_app) + client = TestClient(self.public_app) resp = client.get(path, headers={'Accept': 'application/json-home'}) self.assertThat(resp.status, tt_matchers.Equals('200 OK')) @@ -876,7 +915,7 @@ class VersionTestCase(tests.TestCase): # Accept headers with multiple types and qvalues are handled. def make_request(accept_types=None): - client = tests.TestClient(self.public_app) + client = TestClient(self.public_app) headers = None if accept_types: headers = {'Accept': accept_types} @@ -926,7 +965,7 @@ class VersionTestCase(tests.TestCase): self.assertIsNone(extensions_property) -class VersionSingleAppTestCase(tests.TestCase): +class VersionSingleAppTestCase(unit.TestCase): """Tests running with a single application loaded. These are important because when Keystone is running in Apache httpd @@ -962,7 +1001,7 @@ class VersionSingleAppTestCase(tests.TestCase): else: return CONF.eventlet_server.public_port app = self.loadapp('keystone', app_name) - client = tests.TestClient(app) + client = TestClient(app) resp = client.get('/') self.assertEqual(300, resp.status_int) data = jsonutils.loads(resp.body) @@ -983,7 +1022,7 @@ class VersionSingleAppTestCase(tests.TestCase): self._test_version('admin') -class VersionInheritEnabledTestCase(tests.TestCase): +class VersionInheritEnabledTestCase(unit.TestCase): def setUp(self): super(VersionInheritEnabledTestCase, self).setUp() self.load_backends() @@ -1008,7 +1047,7 @@ class VersionInheritEnabledTestCase(tests.TestCase): # If the request is /v3 and the Accept header is application/json-home # then the server responds with a JSON Home document. - client = tests.TestClient(self.public_app) + client = TestClient(self.public_app) resp = client.get('/v3/', headers={'Accept': 'application/json-home'}) self.assertThat(resp.status, tt_matchers.Equals('200 OK')) @@ -1022,7 +1061,7 @@ class VersionInheritEnabledTestCase(tests.TestCase): tt_matchers.Equals(exp_json_home_data)) -class VersionBehindSslTestCase(tests.TestCase): +class VersionBehindSslTestCase(unit.TestCase): def setUp(self): super(VersionBehindSslTestCase, self).setUp() self.load_backends() @@ -1048,7 +1087,7 @@ class VersionBehindSslTestCase(tests.TestCase): return expected def test_versions_without_headers(self): - client = tests.TestClient(self.public_app) + client = TestClient(self.public_app) host_name = 'host-%d' % random.randint(10, 30) host_port = random.randint(10000, 30000) host = 'http://%s:%s/' % (host_name, host_port) @@ -1059,7 +1098,7 @@ class VersionBehindSslTestCase(tests.TestCase): self.assertThat(data, _VersionsEqual(expected)) def test_versions_with_header(self): - client = tests.TestClient(self.public_app) + client = TestClient(self.public_app) host_name = 'host-%d' % random.randint(10, 30) host_port = random.randint(10000, 30000) resp = client.get('http://%s:%s/' % (host_name, host_port), diff --git a/keystone-moon/keystone/tests/unit/test_wsgi.py b/keystone-moon/keystone/tests/unit/test_wsgi.py index 62156bd5..2a5cb386 100644 --- a/keystone-moon/keystone/tests/unit/test_wsgi.py +++ b/keystone-moon/keystone/tests/unit/test_wsgi.py @@ -23,13 +23,14 @@ import mock import oslo_i18n from oslo_serialization import jsonutils import six +from six.moves import http_client from testtools import matchers import webob from keystone.common import environment from keystone.common import wsgi from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit class FakeApp(wsgi.Application): @@ -52,7 +53,7 @@ class FakeAttributeCheckerApp(wsgi.Application): self._require_attributes(ref, attr) -class RouterTest(tests.TestCase): +class RouterTest(unit.TestCase): def setUp(self): self.router = wsgi.RoutersBase() super(RouterTest, self).setUp() @@ -68,7 +69,7 @@ class RouterTest(tests.TestCase): status=uuid.uuid4().hex) -class BaseWSGITest(tests.TestCase): +class BaseWSGITest(unit.TestCase): def setUp(self): self.app = FakeApp() super(BaseWSGITest, self).setUp() @@ -111,15 +112,16 @@ class ApplicationTest(BaseWSGITest): resp = wsgi.render_response(body=data) self.assertEqual('200 OK', resp.status) - self.assertEqual(200, resp.status_int) + self.assertEqual(http_client.OK, resp.status_int) self.assertEqual(body, resp.body) self.assertEqual('X-Auth-Token', resp.headers.get('Vary')) self.assertEqual(str(len(body)), resp.headers.get('Content-Length')) def test_render_response_custom_status(self): - resp = wsgi.render_response(status=(501, 'Not Implemented')) + resp = wsgi.render_response( + status=(http_client.NOT_IMPLEMENTED, 'Not Implemented')) self.assertEqual('501 Not Implemented', resp.status) - self.assertEqual(501, resp.status_int) + self.assertEqual(http_client.NOT_IMPLEMENTED, resp.status_int) def test_successful_require_attribute(self): app = FakeAttributeCheckerApp() @@ -171,14 +173,14 @@ class ApplicationTest(BaseWSGITest): def test_render_response_no_body(self): resp = wsgi.render_response() self.assertEqual('204 No Content', resp.status) - self.assertEqual(204, resp.status_int) + self.assertEqual(http_client.NO_CONTENT, resp.status_int) self.assertEqual(b'', resp.body) self.assertEqual('0', resp.headers.get('Content-Length')) self.assertIsNone(resp.headers.get('Content-Type')) def test_render_response_head_with_body(self): resp = wsgi.render_response({'id': uuid.uuid4().hex}, method='HEAD') - self.assertEqual(200, resp.status_int) + self.assertEqual(http_client.OK, resp.status_int) self.assertEqual(b'', resp.body) self.assertNotEqual(resp.headers.get('Content-Length'), '0') self.assertEqual('application/json', resp.headers.get('Content-Type')) @@ -195,14 +197,14 @@ class ApplicationTest(BaseWSGITest): def test_render_exception(self): e = exception.Unauthorized(message=u'\u7f51\u7edc') resp = wsgi.render_exception(e) - self.assertEqual(401, resp.status_int) + self.assertEqual(http_client.UNAUTHORIZED, resp.status_int) def test_render_exception_host(self): e = exception.Unauthorized(message=u'\u7f51\u7edc') context = {'host_url': 'http://%s:5000' % uuid.uuid4().hex} resp = wsgi.render_exception(e, context=context) - self.assertEqual(401, resp.status_int) + self.assertEqual(http_client.UNAUTHORIZED, resp.status_int) def test_improperly_encoded_params(self): class FakeApp(wsgi.Application): @@ -311,7 +313,7 @@ class MiddlewareTest(BaseWSGITest): self.assertEqual("test", app.kwargs["testkey"]) -class LocalizedResponseTest(tests.TestCase): +class LocalizedResponseTest(unit.TestCase): def test_request_match_default(self): # The default language if no Accept-Language is provided is None req = webob.Request.blank('/') @@ -409,7 +411,7 @@ class LocalizedResponseTest(tests.TestCase): self.assertThat(xlation_mock.called, matchers.Equals(True)) -class ServerTest(tests.TestCase): +class ServerTest(unit.TestCase): def setUp(self): super(ServerTest, self).setUp() diff --git a/keystone-moon/keystone/tests/unit/tests/test_core.py b/keystone-moon/keystone/tests/unit/tests/test_core.py index 2de51c52..50f1309e 100644 --- a/keystone-moon/keystone/tests/unit/tests/test_core.py +++ b/keystone-moon/keystone/tests/unit/tests/test_core.py @@ -19,28 +19,28 @@ from oslo_log import log from sqlalchemy import exc from testtools import matchers -from keystone.tests import unit as tests +from keystone.tests import unit LOG = log.getLogger(__name__) -class BaseTestTestCase(tests.BaseTestCase): +class BaseTestTestCase(unit.BaseTestCase): def test_unexpected_exit(self): # if a test calls sys.exit it raises rather than exiting. self.assertThat(lambda: sys.exit(), - matchers.raises(tests.UnexpectedExit)) + matchers.raises(unit.UnexpectedExit)) -class TestTestCase(tests.TestCase): +class TestTestCase(unit.TestCase): def test_bad_log(self): # If the arguments are invalid for the string in a log it raises an # exception during testing. self.assertThat( lambda: LOG.warn('String %(p1)s %(p2)s', {'p1': 'something'}), - matchers.raises(tests.BadLog)) + matchers.raises(KeyError)) def test_sa_warning(self): self.assertThat( diff --git a/keystone-moon/keystone/tests/unit/token/test_fernet_provider.py b/keystone-moon/keystone/tests/unit/token/test_fernet_provider.py index 4101369c..5f74b430 100644 --- a/keystone-moon/keystone/tests/unit/token/test_fernet_provider.py +++ b/keystone-moon/keystone/tests/unit/token/test_fernet_provider.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +import base64 import datetime import hashlib import os @@ -20,7 +21,7 @@ from oslo_utils import timeutils from keystone.common import config from keystone.common import utils from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import ksfixtures from keystone.token import provider from keystone.token.providers import fernet @@ -31,7 +32,7 @@ from keystone.token.providers.fernet import utils as fernet_utils CONF = config.CONF -class TestFernetTokenProvider(tests.TestCase): +class TestFernetTokenProvider(unit.TestCase): def setUp(self): super(TestFernetTokenProvider, self).setUp() self.useFixture(ksfixtures.KeyRepository(self.config_fixture)) @@ -56,7 +57,24 @@ class TestFernetTokenProvider(tests.TestCase): uuid.uuid4().hex) -class TestPayloads(tests.TestCase): +class TestTokenFormatter(unit.TestCase): + def test_restore_padding(self): + # 'a' will result in '==' padding, 'aa' will result in '=' padding, and + # 'aaa' will result in no padding. + strings_to_test = ['a', 'aa', 'aaa'] + + for string in strings_to_test: + encoded_string = base64.urlsafe_b64encode(string) + encoded_str_without_padding = encoded_string.rstrip('=') + self.assertFalse(encoded_str_without_padding.endswith('=')) + encoded_str_with_padding_restored = ( + token_formatters.TokenFormatter.restore_padding( + encoded_str_without_padding) + ) + self.assertEqual(encoded_string, encoded_str_with_padding_restored) + + +class TestPayloads(unit.TestCase): def test_uuid_hex_to_byte_conversions(self): payload_cls = token_formatters.BasePayload @@ -387,7 +405,7 @@ class TestPayloads(tests.TestCase): self.assertDictEqual(exp_federated_info, federated_info) -class TestFernetKeyRotation(tests.TestCase): +class TestFernetKeyRotation(unit.TestCase): def setUp(self): super(TestFernetKeyRotation, self).setUp() @@ -512,7 +530,7 @@ class TestFernetKeyRotation(tests.TestCase): self.assertEqual(3, keys) -class TestLoadKeys(tests.TestCase): +class TestLoadKeys(unit.TestCase): def test_non_numeric_files(self): self.useFixture(ksfixtures.KeyRepository(self.config_fixture)) evil_file = os.path.join(CONF.fernet_tokens.key_repository, '~1') diff --git a/keystone-moon/keystone/tests/unit/token/test_pki_provider.py b/keystone-moon/keystone/tests/unit/token/test_pki_provider.py index dad31266..b3ad4c2b 100644 --- a/keystone-moon/keystone/tests/unit/token/test_pki_provider.py +++ b/keystone-moon/keystone/tests/unit/token/test_pki_provider.py @@ -10,11 +10,11 @@ # License for the specific language governing permissions and limitations # under the License. -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.token.providers import pki -class TestPkiTokenProvider(tests.TestCase): +class TestPkiTokenProvider(unit.TestCase): def setUp(self): super(TestPkiTokenProvider, self).setUp() self.provider = pki.Provider() diff --git a/keystone-moon/keystone/tests/unit/token/test_pkiz_provider.py b/keystone-moon/keystone/tests/unit/token/test_pkiz_provider.py index 4a492bc1..1ffe7cfc 100644 --- a/keystone-moon/keystone/tests/unit/token/test_pkiz_provider.py +++ b/keystone-moon/keystone/tests/unit/token/test_pkiz_provider.py @@ -10,11 +10,11 @@ # License for the specific language governing permissions and limitations # under the License. -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.token.providers import pkiz -class TestPkizTokenProvider(tests.TestCase): +class TestPkizTokenProvider(unit.TestCase): def setUp(self): super(TestPkizTokenProvider, self).setUp() self.provider = pkiz.Provider() diff --git a/keystone-moon/keystone/tests/unit/token/test_token_data_helper.py b/keystone-moon/keystone/tests/unit/token/test_token_data_helper.py index a12a22d4..6114b723 100644 --- a/keystone-moon/keystone/tests/unit/token/test_token_data_helper.py +++ b/keystone-moon/keystone/tests/unit/token/test_token_data_helper.py @@ -16,11 +16,11 @@ import uuid from testtools import matchers from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.token.providers import common -class TestTokenDataHelper(tests.TestCase): +class TestTokenDataHelper(unit.TestCase): def setUp(self): super(TestTokenDataHelper, self).setUp() self.load_backends() diff --git a/keystone-moon/keystone/tests/unit/token/test_token_model.py b/keystone-moon/keystone/tests/unit/token/test_token_model.py index 3959d901..f1398491 100644 --- a/keystone-moon/keystone/tests/unit/token/test_token_model.py +++ b/keystone-moon/keystone/tests/unit/token/test_token_model.py @@ -30,7 +30,6 @@ CONF = cfg.CONF class TestKeystoneTokenModel(core.TestCase): def setUp(self): super(TestKeystoneTokenModel, self).setUp() - self.load_backends() self.v2_sample_token = copy.deepcopy( test_token_provider.SAMPLE_V2_TOKEN) self.v3_sample_token = copy.deepcopy( diff --git a/keystone-moon/keystone/tests/unit/token/test_uuid_provider.py b/keystone-moon/keystone/tests/unit/token/test_uuid_provider.py index b49427f0..5c364490 100644 --- a/keystone-moon/keystone/tests/unit/token/test_uuid_provider.py +++ b/keystone-moon/keystone/tests/unit/token/test_uuid_provider.py @@ -10,11 +10,11 @@ # License for the specific language governing permissions and limitations # under the License. -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.token.providers import uuid -class TestUuidTokenProvider(tests.TestCase): +class TestUuidTokenProvider(unit.TestCase): def setUp(self): super(TestUuidTokenProvider, self).setUp() self.provider = uuid.Provider() diff --git a/keystone-moon/keystone/token/persistence/backends/kvs.py b/keystone-moon/keystone/token/persistence/backends/kvs.py index 1bd08a31..51931586 100644 --- a/keystone-moon/keystone/token/persistence/backends/kvs.py +++ b/keystone-moon/keystone/token/persistence/backends/kvs.py @@ -33,7 +33,7 @@ CONF = cfg.CONF LOG = log.getLogger(__name__) -class Token(token.persistence.Driver): +class Token(token.persistence.TokenDriverV8): """KeyValueStore backend for tokens. This is the base implementation for any/all key-value-stores (e.g. diff --git a/keystone-moon/keystone/token/persistence/backends/sql.py b/keystone-moon/keystone/token/persistence/backends/sql.py index 08c3a216..6fc1d223 100644 --- a/keystone-moon/keystone/token/persistence/backends/sql.py +++ b/keystone-moon/keystone/token/persistence/backends/sql.py @@ -83,7 +83,7 @@ def _expiry_range_all(session, upper_bound_func): yield upper_bound_func() -class Token(token.persistence.Driver): +class Token(token.persistence.TokenDriverV8): # Public interface def get_token(self, token_id): if token_id is None: diff --git a/keystone-moon/keystone/token/persistence/core.py b/keystone-moon/keystone/token/persistence/core.py index 15a58085..e68970ac 100644 --- a/keystone-moon/keystone/token/persistence/core.py +++ b/keystone-moon/keystone/token/persistence/core.py @@ -230,7 +230,7 @@ class Manager(object): @six.add_metaclass(abc.ABCMeta) -class Driver(object): +class TokenDriverV8(object): """Interface description for a Token driver.""" @abc.abstractmethod @@ -357,3 +357,6 @@ class Driver(object): """Archive or delete tokens that have expired. """ raise exception.NotImplemented() # pragma: no cover + + +Driver = manager.create_legacy_driver(TokenDriverV8) diff --git a/keystone-moon/keystone/token/providers/fernet/token_formatters.py b/keystone-moon/keystone/token/providers/fernet/token_formatters.py index d1dbb08c..f0b6271d 100644 --- a/keystone-moon/keystone/token/providers/fernet/token_formatters.py +++ b/keystone-moon/keystone/token/providers/fernet/token_formatters.py @@ -21,7 +21,7 @@ from oslo_config import cfg from oslo_log import log from oslo_utils import timeutils import six -from six.moves import map, urllib +from six.moves import map from keystone.auth import plugins as auth_plugins from keystone.common import utils as ks_utils @@ -67,11 +67,14 @@ class TokenFormatter(object): def pack(self, payload): """Pack a payload for transport as a token.""" # base64 padding (if any) is not URL-safe - return urllib.parse.quote(self.crypto.encrypt(payload)) + return self.crypto.encrypt(payload).rstrip('=') def unpack(self, token): """Unpack a token, and validate the payload.""" - token = urllib.parse.unquote(six.binary_type(token)) + token = six.binary_type(token) + + # Restore padding on token before decoding it + token = TokenFormatter.restore_padding(token) try: return self.crypto.decrypt(token) @@ -80,16 +83,31 @@ class TokenFormatter(object): _('This is not a recognized Fernet token')) @classmethod + def restore_padding(cls, token): + """Restore padding based on token size. + + :param token: token to restore padding on + :returns: token with correct padding + + """ + # Re-inflate the padding + mod_returned = len(token) % 4 + if mod_returned: + missing_padding = 4 - mod_returned + token += b'=' * missing_padding + return token + + @classmethod def creation_time(cls, fernet_token): """Returns the creation time of a valid Fernet token.""" # tokens may be transmitted as Unicode, but they're just ASCII # (pypi/cryptography will refuse to operate on Unicode input) fernet_token = six.binary_type(fernet_token) - # the base64 padding on fernet tokens is made URL-safe - fernet_token = urllib.parse.unquote(fernet_token) + # Restore padding on token before decoding it + fernet_token = TokenFormatter.restore_padding(fernet_token) - # fernet tokens are base64 encoded and the padding made URL-safe + # fernet tokens are base64 encoded, so we need to unpack them first token_bytes = base64.urlsafe_b64decode(fernet_token) # slice into the byte array to get just the timestamp @@ -571,9 +589,9 @@ class FederatedUnscopedPayload(BasePayload): """Validate a federated payload. :param token_string: a string representing the token - :return: a tuple containing the user_id, auth methods, audit_ids, and - a dictionary containing federated information such as the the - group IDs, the identity provider ID, the protocol ID, and the + :return: a tuple containing the user_id, auth methods, audit_ids, and a + dictionary containing federated information such as the group + IDs, the identity provider ID, the protocol ID, and the federated domain ID """ diff --git a/keystone-moon/keystone/trust/backends/sql.py b/keystone-moon/keystone/trust/backends/sql.py index 95b18d40..a017056b 100644 --- a/keystone-moon/keystone/trust/backends/sql.py +++ b/keystone-moon/keystone/trust/backends/sql.py @@ -54,7 +54,7 @@ class TrustRole(sql.ModelBase): role_id = sql.Column(sql.String(64), primary_key=True, nullable=False) -class Trust(trust.Driver): +class Trust(trust.TrustDriverV8): @sql.handle_conflicts(conflict_type='trust') def create_trust(self, trust_id, trust, roles): with sql.transaction() as session: diff --git a/keystone-moon/keystone/trust/core.py b/keystone-moon/keystone/trust/core.py index e091ff93..7838cb03 100644 --- a/keystone-moon/keystone/trust/core.py +++ b/keystone-moon/keystone/trust/core.py @@ -204,7 +204,7 @@ class Manager(manager.Manager): @six.add_metaclass(abc.ABCMeta) -class Driver(object): +class TrustDriverV8(object): @abc.abstractmethod def create_trust(self, trust_id, trust, roles): @@ -251,3 +251,6 @@ class Driver(object): keystone.exception.TrustNotFound """ raise exception.NotImplemented() # pragma: no cover + + +Driver = manager.create_legacy_driver(TrustDriverV8) diff --git a/keystone-moon/requirements.txt b/keystone-moon/requirements.txt index d83eed6a..555a148f 100644 --- a/keystone-moon/requirements.txt +++ b/keystone-moon/requirements.txt @@ -2,7 +2,7 @@ # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -pbr<2.0,>=1.4 +pbr>=1.6 WebOb>=1.2.3 eventlet>=0.17.4 greenlet>=0.3.2 @@ -10,28 +10,29 @@ PasteDeploy>=1.5.0 Paste Routes!=2.0,!=2.1,>=1.12.3;python_version=='2.7' Routes!=2.0,>=1.12.3;python_version!='2.7' -cryptography>=0.9.1 # Apache-2.0 +cryptography>=1.0 # Apache-2.0 six>=1.9.0 -SQLAlchemy<1.1.0,>=0.9.7 +SQLAlchemy<1.1.0,>=0.9.9 sqlalchemy-migrate>=0.9.6 stevedore>=1.5.0 # Apache-2.0 -passlib +passlib>=1.6 python-keystoneclient>=1.6.0 keystonemiddleware>=2.0.0 oslo.concurrency>=2.3.0 # Apache-2.0 -oslo.config>=2.1.0 # Apache-2.0 +oslo.config>=2.3.0 # Apache-2.0 +oslo.context>=0.2.0 # Apache-2.0 oslo.messaging!=1.17.0,!=1.17.1,>=1.16.0 # Apache-2.0 -oslo.db>=2.0 # Apache-2.0 +oslo.db>=2.4.1 # Apache-2.0 oslo.i18n>=1.5.0 # Apache-2.0 oslo.log>=1.8.0 # Apache-2.0 -oslo.middleware>=2.4.0 # Apache-2.0 +oslo.middleware>=2.8.0 # Apache-2.0 oslo.policy>=0.5.0 # Apache-2.0 oslo.serialization>=1.4.0 # Apache-2.0 -oslo.service>=0.6.0 # Apache-2.0 +oslo.service>=0.7.0 # Apache-2.0 oslo.utils>=2.0.0 # Apache-2.0 oauthlib>=0.6 pysaml2>=2.4.0 dogpile.cache>=0.5.4 jsonschema!=2.5.0,<3.0.0,>=2.0.0 -pycadf>=0.8.0 +pycadf>=1.1.0 msgpack-python>=0.4.0 diff --git a/keystone-moon/setup.cfg b/keystone-moon/setup.cfg index ad3ee53d..c40aa3b8 100644 --- a/keystone-moon/setup.cfg +++ b/keystone-moon/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = keystone -version = 8.0.0 +version = 9.0.0 summary = OpenStack Identity description-file = README.rst @@ -24,7 +24,7 @@ packages = [extras] ldap = python-ldap>=2.4:python_version=='2.7' - ldappool>=1.0 # MPL + ldappool>=1.0:python_version=='2.7' # MPL memcache = python-memcached>=1.56 mongodb = diff --git a/keystone-moon/setup.py b/keystone-moon/setup.py index d8080d05..782bb21f 100644 --- a/keystone-moon/setup.py +++ b/keystone-moon/setup.py @@ -25,5 +25,5 @@ except ImportError: pass setuptools.setup( - setup_requires=['pbr>=1.3'], + setup_requires=['pbr>=1.8'], pbr=True) diff --git a/keystone-moon/test-requirements.txt b/keystone-moon/test-requirements.txt index 326145c2..a027613f 100644 --- a/keystone-moon/test-requirements.txt +++ b/keystone-moon/test-requirements.txt @@ -30,7 +30,7 @@ testtools>=1.4.0 # For documentation oslosphinx>=2.5.0 # Apache-2.0 -tempest-lib>=0.6.1 +tempest-lib>=0.8.0 # Functional tests. requests>=2.5.2 diff --git a/keystone-moon/tox.ini b/keystone-moon/tox.ini index cea70b7b..0d249105 100644 --- a/keystone-moon/tox.ini +++ b/keystone-moon/tox.ini @@ -102,7 +102,7 @@ commands = oslo-config-generator --config-file=config-generator/keystone.conf [testenv:bandit] deps = .[bandit] -commands = bandit -c bandit.yaml -r keystone -n5 -p keystone_conservative +commands = bandit -c bandit.yaml -r keystone -n5 -p gate [hacking] import_exceptions = |