python-keystoneclient-0.7.1/0000775000175400017540000000000012315030547017234 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/HACKING.rst0000664000175400017540000000121712315030450021024 0ustar jenkinsjenkins00000000000000Keystone Style Commandments =========================== - Step 1: Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/ - Step 2: Read on Exceptions ---------- When dealing with exceptions from underlying libraries, translate those exceptions to an instance or subclass of ClientException. ======= Testing ======= python-keystoneclient uses testtools and testr for its unittest suite and its test runner. Basic workflow around our use of tox and testr can be found at http://wiki.openstack.org/testr. If you'd like to learn more in depth: https://testtools.readthedocs.org/ https://testrepository.readthedocs.org/ python-keystoneclient-0.7.1/.mailmap0000664000175400017540000000027612315030450020653 0ustar jenkinsjenkins00000000000000# Format is: # # python-keystoneclient-0.7.1/test-requirements.txt0000664000175400017540000000037112315030451023470 0ustar jenkinsjenkins00000000000000coverage>=3.6 discover fixtures>=0.3.14 hacking>=0.8.0,<0.9 httpretty>=0.8.0 keyring>=1.6.1,<2.0,>=2.1 mock>=1.0 mox3>=0.7.0 pycrypto>=2.6 sphinx>=1.1.2,<1.2 stevedore>=0.14 testrepository>=0.0.18 testresources>=0.2.4 testtools>=0.9.34 WebOb>=1.2.3 python-keystoneclient-0.7.1/.coveragerc0000664000175400017540000000020412315030450021342 0ustar jenkinsjenkins00000000000000[run] branch = True source = keystoneclient omit = keystoneclient/tests/*,keystoneclient/openstack/* [report] ignore-errors = True python-keystoneclient-0.7.1/ChangeLog0000664000175400017540000006165412315030547021022 0ustar jenkinsjenkins00000000000000CHANGES ======= 0.7.1 ----- * Adds to Keystone to convert V2 endpoints to V3 0.7.0 ----- * Improve language in update_password() validation error * Handle URLs via the session and auth_plugins * Add a method for changing a user's password in v3 * sanity check memcached availability before running tests against it * Start using positional decorator * Fix passing get_token kwargs to get_access * add functional test for cache pool * Add a positional decorator * add pooling for cache references * Don't use a connection pool unless provided * Docs link to middlewarearchitecture * Discover should support other services * Revert "Add request/access token and consumer..." * Revert "Authenticate via oauth" * Fix doc build errors * Generate module docs * document that --pass can be required * Authenticate via oauth * Add request/access token and consumer support for keystoneclient * Add 'methods' to all v3 test tokens * Handle Token/Endpoint authentication * Updated from global requirements * Fix retry logic * Provide more data to AuthMethod plugins * Fix state modifying catalog tests * Remove reference to non-existent shell doc * increase default revocation_cache_time * Improve help strings * Make keystoneclient not log auth tokens * improve configuration help text in auth_token * Log the command output on CertificateConfigError * Enforce scope mutual exclusion for trusts * Atomic write of certificate files and revocation list * Privatize auth construction parameters * Remove blank space after print * Set the right permissions for signing_dir in tests * Capitalize Client API title consistently * Remove dependent module py3kcompat * Remove http_handler config option in auth_token * Rely on OSLO.config * Use admin_prefix consistently * demonstrate auth_token behavior with a simple echo service * Remove redundant default value None for dict.get * correct typo of config option name in error message * remove extra indentation * refer to non-deprecated config option in help * Create V3 Auth Plugins * Create V2 Auth Plugins * Fix role_names call from V3 AccessInfo * Interactive prompt for create user * Add Python 3 classifiers * Replace assertEqual(None, *) with assertIsNone in tests * Ensure domains.list filtered results are correct * Test query-string for list actions with filter arguments * Use Resource class from Oslo * Fix keystone command man page * Add link to the v3 client api doc * Fix references to auth_token in middlewarearchitecture doc 0.6.0 ----- * Don't use private last_request variable * Python: Pass bytes to derive_keys() * Make sure to unset all variable starting with OS_ * Remove tox locale overrides * Python3: use six.moves.urllib.parse.quote instead of urllib.quote * Remove vim header * Python3: httpretty.last_request().body is now bytes * Python3: fix test_insecure * Sync openstack/common/memorycache.py with Oslo * cms: Use universal_newlines=True in subprocess.Popen() * HTTPretty: Bump to 0.8.0 * Check for any monkeypatching * Python3: webob.Response.body must be bytes * Python 3: call functions from memcache_crypt.py with bytes as input * Update my mailmap * Improve output of "keystone help discover" * Use requests library in S3 middleware * Python 3: make tests from v2_0/test_access.py pass * Sync apiclient from oslo * Create Authentication Plugins * Fix debug curl commands for included data * Add back --insecure option to CURL debug 0.5.1 ----- * Use HTTPretty in S3 test code * Provide a conversion function for creating session * Update reference to middlewarearchitecture doc * Update middlewarearchitecture config options docs * Remove support for old Swift memcache interface 0.5.0 ----- * refactor handling of extension list * Python 3: fix tests/test_utils.py * Python 3: Fix an str vs bytes issue in tempfile * Return role names by AccessInfo.role_names * Copy s3_token middleware from keystone * Fix discover command failed to discover keystone in ssl * Fix E12x warnings found by Pep8 1.4.6 * Fix typos in documents and comments * Consistently support kwargs across all v3 CRUD Manager ops * Using common method 'bool_from_string' from oslo strutils * Python 3: set webob.Response().body to a bytes value * Remove test_print_{dict,list}_unicode_without_encode * Tests use cleanUp rather than tearDown * Sort items in requirement related files * Respect region name when processing domain URL * Python 3: fix the _calc_signature_* functions * Adjust import items according to hacking import rule * Replace assertTrue with explicit assertIsInstance * Sync with global requirements * Fix discover command failed to read extension list issue * Clarify roles validation error messages * Fix incorrect assertTrue usage * Make assertQueryStringIs usage simpler 0.4.2 ----- * auth_token tests use assertIs/Not/None * Updated from global requirements * Python 3: Use HTTPMessage.get() rather than HTTPMessage.getheader() * auth_token tests close temp file descriptor * Tests cleanup temporary files * Use install_venv from oslo to fix no post_process issue * Removes use of timeutils.set_time_override * Saner debug log message generation * Controllable redirect handling * Revert "Whitelist external netaddr requirement" * Sync strutils from oslo * Verify token binding in auth_token middleware * Fix auth_token middleware test invalid cross-device link issue * Add unit tests for generic/shell.py * Rename using-api.rst to using-api-v2.rst * Debug env for tox * Whitelist external netaddr requirement * Prevent dictionary size from changing while iterating over its items * Do not try to call decode() on a text string * Documents keystone v3 API usage - part 1 * v3 test utils, don't modify input parameter * Fix error in v3 credentials create/update * Rename instead of writing directly to revoked file * Correctly handle auth_url/token authentication * Move redirect handling to session * Remove debug specific handling * Fix missed management_url setter in v3 client * Add service catalog to domain scoped token fixture * Update requirements * Change assertEquals to assertIsNone * Avoid meaningless comparison that leads to a TypeError * HTTPretty: update to 0.7.1 * Python3: replace urllib by six.moves.urllib * Remove the 'cmp' keyword from a call to 'sort()' * Make _get_utf8_value Python3 compliant * Don't install pre-release software with tox * Sync global requirements to pin sphinx to sphinx>=1.1.2,<1.2 * Allow commit title messages to end with a period * Sync with latest module from oslo * Fix --debug handling in the shell * Rename tokenauth to authtoken in the doc * use six.StringIO for compatibility with io.StringIO in python3 * Properly handle Regions in keystoneclient * Use testresources for example files * Discover supported APIs * Warn user about unsupported API version * Bump hacking to 0.8 * Add workaround for OSError raised by Popen.communicate() * Use assertIn where appropriate * Updates .gitignore * Updates .gitignore * Extract a base Session object * Reorganize Service Catalog * Do not format messages before they are logged * keystoneclient requires an email address when creating a user * Fix typo in keystoneclient * Encode the text before print it to console * Opt-out of service catalog * Fix trustee/trustor definitions * Revert "Merge "Avoid returning stale token via auth_token property"" * "publicurl" should be required on endpoint-create * Update the management url for every fetched token * Remove service type restriction from keystone client help text * Add testresources test requirement * Fix python3 incompatible use of urlparse * Update tox.ini to usedevelop * Make HACKING.rst DRYer and turn into rst file * Quote URL in curl output to handle query params * Fix curl debug output for requests with a body * Add --insecure to curl output if required * Convert revocation list file last modified to UTC * Improved error message on connection failure * PEP 8 Public and internal interfaces * python3: Work around httpretty issue * Remove unused simplejson requirement * Migrate the keystone.common.cms to keystoneclient * Avoid returning stale token via auth_token property * Remove SERVICE_TOKEN and SERVICE_ENDPOINT env vars * Apply six for metaclass * Make ROOTDIR determination more robust 0.4.1 ----- * Replace OpenStack LLC with OpenStack Foundation * Add AssertRequestHeaderEqual test helper and make use of it * Sync jsonutils from oslo * python3: Refactor dict for python2/python3 compat * Updated from global requirements * python3: Make iteritems py3k compat 0.4.0 ----- * Normalize datetimes to account for tz * assertEquals is deprecated, use assertEqual (H602) * Fix H202 assertRaises Exception * Refactor for testability of an upcoming change * Fixes print error for keystone action with non-English characters * Allow v2 client authentication with trust_id * Fix misused assertTrue in unit tests * Add auth_uri in conf to avoid unnecessary warning * Require oslo.config 1.2.0 final * Move tests in keystoneclient * Change Babel to a runtime requirement * Allow blank to email in user-update * Set example timestamps to 2038-01-18T21:14:07Z * Replace HttpConnection in auth_token with Requests * Convert tests to HTTPretty and reorganize * Support client generate literal ipv6 auth_uri base on auth_host * Log user info in auth_token middleware * Decode the non-english username str to unicode * Deprecation warning should be 'pending' * Deprecation warning for the CLI * Don't need to init testr explicitly * Allow Hacking 0.7.x or later * Remove testcase test_invalid_auth_version_request * Correct keyword args in test cases * python3: Use from future import unicode_literals * Fixing potential NameErrors * Modify keyring tests to test authentication * Fix and enable gating on F811 * Fix and enable gating on F841 * Remove duplicate method in AccessInfo * remove the UUID check for userids * Standardize base.py with novaclient * Fix and enable gating on H302: only import modules * Fix License Headers and Enable Gating on H102 * Replace auth_token middleware tests with httpretty * Add domain attributes to accessinfo * Support older token formats for projects in accessinfo * Use OSLO jsonutils instead of json module * python3: Transition to mox3 instead of mox * Sync py3kcompat from oslo-incubator * Update oslo.config * clearer error when authenticate called without auth_url 0.3.2 ----- * Add unittests for exceptions.EmptyCatalog * Allow configure the number of http retries * Restore client.py for backward compatibility * Initial Trusts support * Add importutils and strutils from oslo * Synchronize code from oslo * Add apiclient.exceptions hierarchy * Use hashed token for invalid PKI token cache key * Move flake8 option from run_tests.sh to tox.ini * Extract test token data from auth_token middleware * Make auth_token middleware fetching respect prefix * Fix and enable Gating on H404 * Move all opens in auth_token to be in context * Refactor verify signing dir logic * Fixes files with wrong bitmode * flake8: enable H201, H202, H802 * Adds support for passing extra tenant attributes to keystone * Add a get_data function to Service Catalog * Extract basic request call * Rename client.py to httpclient.py * Updated from global requirements * Don't cache tokens as invalid on network errors * Fix test_request_no_token_dummy cms dependency * Fix a typo in fetch_revocation_list * Make TestResponse properly inherit Response * auth_uri (public ep) should not default to auth_* values (admin ep) * Adds help in keystone_authtoken config opts * python3: Add basic compatibility support * Pass the default_project_id when managing User * Use flake8 in run_tests.sh and updated ignore flake8 rules with tox.ini * flake8: fix alphabetical imports and enable H306 * Ec2Signer : Allow signature verification for older boto versions * Correct mis-spell in comments * Reorganize url creation * Add -u option in run_tests.sh * Drop webob from auth_token.py * no logging on cms failure * Client V3 shouldn't inherit V2 * Add discover to test-requirements * rm improper assert syntax * Update openstack-common.conf format * Fix and enable gating on H403 * Fix and enable gating on H402 * Raise key length defaults * Use ServiceCatalog.factory, the object has no __init__ * Ec2Signer : Modify v4 signer to match latest boto * Sync install_venv_common from oslo * Flake8 should ignore build folder * Fix auth_token.py bad signing_dir log message * Add name arguments to keystone command 0.3.1 ----- * Fix and enable H401 * List groups by domain in keystoneclient * Unmock requests when testing complete 0.3.0 ----- * Use Python 3.x compatible print syntax * Fix the cache interface to use time= by default * Implements v3 auth client * Change memcache config entry name in Keystone to be consistent with Oslo * Fix memcache encryption middleware * Python-2.6 compatibility for tests/test_keyring.py * Remove endpoint.name attribute from v3 manager (bug 1191152) * Provide keystone CLI man page * Log cms_verify issues as warnings (not errors) * Cleanup shell's authentication check * Use AuthRef for some client fields * Fix optional keyring support, add basic keyring tests 0.2.5 ----- * Fix unused imports(flake8 F401, F999) * Fix line continuations (flake8 E125, E126) * Fix --version to output version * python3: Introduce py33 to tox.ini * Add find() method to CrudManager 0.2.4 ----- * Check Expiry * Enumerate ignored flake8 rules * Missing command descriptions for 'token-get' and 'endpoint-get' * Suggestion of a new arguments display in the help, to reflect required ones Fix bug 1182130 * Rename requires files to standard names * Default signing_dir to secure temp dir (bug 1181157) * Only add logging handlers if there currently aren't any * Pass memcache_servers as array * Allow secure user password update * Make ManagerWithFind abstract and fix TokenManager * Migrate to flake8 * Migrate to pbr * change "int(marker)" to "marker" on user list pagination * Use testr instead of nose * Perform oslo-incubator code sync * Securely create signing_dir (bug 1174608) * Added Conflict Exception to the exception code map * Refactor v3 API to support filtering * Revert "Use TokenManager to get token" * Restore compatibility with PrettyTable < 0.7.2 * Remove duplicate test definitions * Use TokenManager to get token * Pass json object when invoking exception handler * modify mistake in comment * Ec2Signer: Initial support for v4 signature verification * adding notes about dealing with exceptions in the client * Fix v3 with UUID and memcache expiring * Convert requests.ConnectionError to ClientException * Restrict prettytable to >=0.6,<0.8 * Allow keystoneclient to work with older keystone installs * Config value for revocation list timeout 0.2.3 ----- * Cache tokens using memorycache from oslo * Make keystone client handle the response code 300 * Make auth_token lazy load the auth_version * Doc info and other readability improvements * Retry http_request and json_request failure * Use v2.0 api by default in auth_token middleware * Switch to final 1.1.0 oslo.config release * Fix auth-token middleware to understand v3 tokens * help text improvements * Switch to oslo.config * update v3 domains - remove public/private namespace * Work better in server env with no keyrings * Remove test dep on name of dir (bug 1124283) * Sync memorycache and timeutils from oslo * Improve error message for missing endpoint * Update oslo-config version * Fix selef to self in class * Save test_auth_token_middleware from unlimited recursion * Use oslo-config-2013.1b3 * Added missing unit tests for shell.py * Allow configure auth_token http connect timeout * Fix debug with requests * Allow requests up to 0.8 and greater * sync README with "keystone help" * Use install_venv_common.py from oslo * Fix incomplete sentence in help * Update .coveragerc * Pin requests module more strictly * Treat HTTP code 400 and above as error * Update requests requirements * Mark password config options with secret * Implements token expiration handling * fix discrepancies seen in domain and credential, v3 - bug 1109349 * Fix how python 2.6 doesn't have assertDictEqual * If you specify the --debug argument, it doesn't show the body of a POST request. The body (string rep) is at 'data' in the kwargs dict. 'body' was deleted prior to this call * Fix STALE_TOKEN_DURATION usage * Factorize endpoint retrieval in access * Take region_name into account when calling url_for * Remove useless code * Fix thinko in self.middleware.cert_file_missing * Remove useless import * Restore Python 2.6 compatibility * Allow request timeout to be specified * Remove assertDictEqual for python 2.6 compatibility * Add name arguments to keystone command * Blueprint memcache-protection: enable memcache value encryption/integrity check * Make WebOb version specification more flexible * Warning message is not logged for valid token-less request 0.2.2 ----- * Use os.path to find ~/keystone-signing (bug 1078947) * Remove iso8601 dep in favor of openstack.common * Move iso8601 dependency from test- to pip-requires * Pin requests to >=0.8.8 * Use testtools instead of unittest for base classes * Add support for user groups 0.2.1 ----- * Make it possible to debug by running module * remove unused import * Bug 1052674: added support for Swift cache * Add file 'ChangeLog' to MANIFEST.in * Fix keystone *-list order by 'name' * Use requests module for HTTP/HTTPS * Print to stderr when keyring module is missing * Prevent an uncaught exception from being rasied * modify ca-certificate default value * Spelling: compatibile->compatible * URL-encode user-supplied tokens (bug 974319) * Fix middleware logging for swift * Fix keystoneclient user-list output order * Misspelling error in README.rst * Rename --no_cache to --os_cache * Make use_keyring False by default * bug-1040361: use keyring to store tokens * Don't try to split a list of memcache servers * Drop hashlib/hmac from pip-requires * Add --version CLI opt and __version__ module attr * Add Ec2Signer utility class to keystoneclient * Add command to allow users to change their own password * updating PEP8 to 1.3.3 * Correct a misspelled in comments * Remove Policy.endpoint_id reference * Fix scoped auth for non-admins (bug 1081192) 0.2.0 ----- * Throw validation response into the environment * fixes auth_ref initialization error * Update README and CLI help * Add auth-token code to keystoneclient, along with supporting files * Make initial structural changes to keystoneclient in preparation to moving auth_token here from keystone. No functional change should occur from this commit (even though it did refresh a newer copy of openstack.common.setup.py, none of the newer updates are in functions called from this client) * removing repeat attempt at authorization in client * Check for auth URL before password (bug 1076235) * check creds before token/endpoint (bug 1076233) * Warn about bypassing auth on CLI (bug 1076225) * fixes 1075376 * Fix keystoneclient so swift works against Rackspace Cloud Files * HACKING compliance: consistent usage of 'except' * Update --os-* error messages * Replace refs to 'Keystone API' with 'Identity API' * Don't log an exception for an expected empty catalog * Add OpenStack trove classifier for PyPI * add a new HTTPClient attr for setting the original IP * Fixes https connections to keystone when no CA certificates are specified * use mock context managers instead of decorators+functions * Ensure JSON isn't read on no HTTP response body * Added 'service_id' column to endpoint-list * Useful error msg when missing catalog (bug 949904) * bootstrap a keystone user (e.g. admin) in one cmd * Enable/disable services/endpoints (bug 1048662 * v3 Domain/Project role grants * Fixed httplib2 mocking (bug 1050091, bug 1050097) * v3 List projects for a user * v3 Credential CRUD * v3 User CRUD * v3 Project CRUD * v3 Role CRUD * v3 Domain CRUD * v3 Policy CRUD * v3 Endpoint CRUD * v3 Service CRUD * change default wrap for tokens from 78 characters to 0 * v3 Client & test utils * Manager for generic CRUD on v3 * virtualenv quite installation for zypper * updating base keystoneclient documentation * updating keystoneclient doc theme * enabling i18n with Babel * pep8 1.3.1 cleanup * Allow empty description for tenants * Add wrap option to keystone token-get for humans * switching options to match authentication paths * Fixes setup compatibility issue on Windows * Handle "503 Service Unavailable" exception * removing deprecated commandline options * Require httplib2 version 0.7 or higher * Fixed httplib2 mocking (bug 1050091, bug 1050097) * Allow serialization impl to be overridden * Add generic entity.delete() * Add support for HEAD and PATCH * Don't need to lazy load resources loaded from API 0.1.3 ----- * fixing pep8 formatting for 1.0.1+ pep8 * Fix PEP8 issues * splitting http req and resp logging also some pep8 cleanup in shell.py * Change underscores in new cert options to dashes * Add nosehtmloutput as a test dependency 0.1.2 ----- * Add '--insecure' commandline argument * If no password in env or command line, try prompting * Install test-requires in development venv * add keystone bash-completion * Replace obsolete option in README * Support 2-way SSL with Keystone server if it is configured to enforce 2-way SSL. See also https://review.openstack.org/#/c/7706/ for the corresponding review for the 2-way SSL addition to Keystone * Don't call PrettyTable add_row with a tuple * Change CLI options to use dashes 0.1.1 ----- * Add post-tag versioning * decoding json only on 2xx success response bug 1007661 * Do not display None in pretty tables for fields with no value 0.1.0 ----- * Drop support for legacy OS args * Skip argparse when injecting requirements * Move unittest2 dependency * Fix coverage job. Turns out you need coverage * Update to latest openstack.common setup code * Move docs to doc * fix bug lp:936168,format output * pep8 1.1 changes and updates * Updated Sphinx documentation folsom-1 -------- * Fix Tenant.update() for enabled=False * Change --user to --user_id and --role to --role_id in the keystone client for consistency * Remove printt * Auto generate AUTHORS for python-keystoneclient * Require service_id for endpoint-create (bug 987457) * Removed unused imports and variables * Include last missing files in tarball * fix parameter name error in exapmle * Drop support for OS --tenant_id (bug 960977) * Open Folsom essex-rc1 --------- * Useful messages for missing auth data (bug 946297) * Updated tox.ini to work properly with Jenkins * Implement user-get based on tenant-get (bug 940272) * Backslash continuations (python-keystoneclient) * Split user-role-list from user-list * Change CLIAuth arg names * enabled treated as string (bug 953678) * CLI shows help without args (bug 936398) * fix bug 950685,make update user password works * Add endpoint commands help text * List roles for user on CLI (bug 932282) * prevent keyerrors when accessing optional keys * Removed ?fresh=nonsense (bug 936405) * Make ec2-credentials-* commands work properly for non-admin user * Remove trailing whitespaces in regular file essex-4 ------- * Endpoints: Add create, delete, list support * Clean up EC2 CRUD * Fix --tenant_id corner case with ec2-create-creds command * Improve usability of CLI * Help output tweaks, Vol I * Move --version to --identity_api_version * Remove internal '-' from flag names * Fix inconsistient method names and add tests * Added condition requirement to argparse * Add tenant commands to cli * Display token and service catalog for user * Restores proper PUT method for user update now that KSL supports it * Add license file to the tarball * Fixes user update methods * Use unittest2 instead of unittest * Fix conflicts with shell args for subcommands * Allow --token and --endpoint to bypass catalog * Blueprint cli-auth: common cli args * Correct tenant update HTTP method * Added delete token * Updates client to work with keystone essex roles API routes * Enabling/disabling users should use OS-KSADM extension (bug 922394) * Add limit and marker to user_list and tenant_list essex-3 ------- * Support for version and extension discovery * Implementing a minimal, but useful CLI * Adjust version number to match other deliveries * update ec2 crud responses we test against * support ec2 crud calls * Install a good version of pip in the venv * Modify tox.ini file to do the standard thigns * Added in common test, venv and gitreview stuff * log when no service catalog * update comment to be tenant_name * should have had tenant_name * use full name for args in readme * finish removing project_id * update test env shell * Fix the tests * remove X-Auth-Project-Id, re-add auth by token support (most tests pass) * pep8 * set the management_url from the service_catalog * more work on standardizing project_id * typo in comments * remove print statements and uncomment exceptions * more work on standardization of cliauth * remove user_id as you shouldn't auth using it * initial pass to cliauth blueprint * Improved error message when unable to communicate with keystone * Improved logging/error messages * adding myself to authors * switching back per docs * fixing up the VerifyAll() bits * more pep8 cleanup * pep8 cleanup * Updated the docs a little bit * Project ID always treated as a string * Cleans up the data returned for a token a little * Fixed a typo... "API" should've been "CLI". Thanks termie. ;-) * Initial commit python-keystoneclient-0.7.1/doc/0000775000175400017540000000000012315030547020001 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/doc/.gitignore0000664000175400017540000000000712315030450021757 0ustar jenkinsjenkins00000000000000build/ python-keystoneclient-0.7.1/doc/ext/0000775000175400017540000000000012315030547020601 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/doc/ext/apidoc.py0000664000175400017540000000307112315030450022404 0ustar jenkinsjenkins00000000000000# Copyright 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # NOTE(blk-u): Uncomment the [pbr] section in setup.cfg and remove this # Sphinx extension when https://launchpad.net/bugs/1260495 is fixed. import os.path as path from sphinx import apidoc # NOTE(blk-u): pbr will run Sphinx multiple times when it generates # documentation. Once for each builder. To run this extension we use the # 'builder-inited' hook that fires at the beginning of a Sphinx build. # We use ``run_already`` to make sure apidocs are only generated once # even if Sphinx is run multiple times. run_already = False def run_apidoc(app): global run_already if run_already: return run_already = True package_dir = path.abspath(path.join(app.srcdir, '..', '..', 'keystoneclient')) source_dir = path.join(app.srcdir, 'api') apidoc.main(['apidoc', package_dir, '-f', '-H', 'keystoneclient Modules', '-o', source_dir]) def setup(app): app.connect('builder-inited', run_apidoc) python-keystoneclient-0.7.1/doc/ext/__init__.py0000664000175400017540000000000012315030450022671 0ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/doc/Makefile0000664000175400017540000000617012315030450021436 0ustar jenkinsjenkins00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build SPHINXSOURCE = source PAPER = BUILDDIR = build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SPHINXSOURCE) .PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/python-keystoneclient.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/python-keystoneclient.qhc" latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." python-keystoneclient-0.7.1/doc/source/0000775000175400017540000000000012315030547021301 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/doc/source/using-api-v2.rst0000664000175400017540000001044112315030450024245 0ustar jenkinsjenkins00000000000000================= The Client v2 API ================= Introduction ============ The main concepts in the Identity v2 API are: * tenants * users * roles * services * endpoints The client v2 API lets you query and make changes through managers. For example, to manipulate tenants, you interact with a ``keystoneclient.v2_0.tenants.TenantManager`` object. You obtain access to managers through via attributes of the ``keystoneclient.v2_0.client.Client`` object. For example, the ``tenants`` attribute of the ``Client`` class is a tenant manager:: >>> from keystoneclient.v2_0 import client >>> keystone = client.Client(...) >>> keystone.tenants.list() # List tenants You create a valid ``keystoneclient.v2_0.client.Client`` object by passing authentication data to the constructor. Authentication and examples of common tasks are provided below. You can generally expect that when the client needs to propagate an exception it will raise an instance of subclass of ``keystoneclient.exceptions.ClientException`` Authenticating ============== There are two ways to authenticate against Keystone: * against the admin endpoint with the admin token * against the public endpoint with a username and password If you are an administrator, you can authenticate by connecting to the admin endpoint and using the admin token (sometimes referred to as the service token). The token is specified as the ``admin_token`` configuration option in your keystone.conf config file, which is typically in /etc/keystone:: >>> from keystoneclient.v2_0 import client >>> token = '012345SECRET99TOKEN012345' >>> endpoint = 'http://192.168.206.130:35357/v2.0' >>> keystone = client.Client(token=token, endpoint=endpoint) If you have a username and password, authentication is done against the public endpoint. You must also specify a tenant that is associated with the user:: >>> from keystoneclient.v2_0 import client >>> username='adminUser' >>> password='secreetword' >>> tenant_name='openstackDemo' >>> auth_url='http://192.168.206.130:5000/v2.0' >>> keystone = client.Client(username=username, password=password, ... tenant_name=tenant_name, auth_url=auth_url) Creating tenants ================ This example will create a tenant named *openStackDemo*:: >>> from keystoneclient.v2_0 import client >>> keystone = client.Client(...) >>> keystone.tenants.create(tenant_name="openstackDemo", ... description="Default Tenant", enabled=True) Creating users ============== This example will create a user named *adminUser* with a password *secretword* in the opoenstackDemo tenant. We first need to retrieve the tenant:: >>> from keystoneclient.v2_0 import client >>> keystone = client.Client(...) >>> tenants = keystone.tenants.list() >>> my_tenant = [x for x in tenants if x.name=='openstackDemo'][0] >>> my_user = keystone.users.create(name="adminUser", ... password="secretword", ... tenant_id=my_tenant.id) Creating roles and adding users =============================== This example will create an admin role and add the *my_user* user to that role, but only for the *my_tenant* tenant: >>> from keystoneclient.v2_0 import client >>> keystone = client.Client(...) >>> role = keystone.roles.create('admin') >>> my_tenant = ... >>> my_user = ... >>> keystone.roles.add_user_role(my_user, role, my_tenant) Creating services and endpoints =============================== This example will create the service and corresponding endpoint for the Compute service:: >>> from keystoneclient.v2_0 import client >>> keystone = client.Client(...) >>> service = keystone.services.create(name="nova", service_type="compute", ... description="Nova Compute Service") >>> keystone.endpoints.create( ... region="RegionOne", service_id=service.id, ... publicurl="http://192.168.206.130:8774/v2/%(tenant_id)s", ... adminurl="http://192.168.206.130:8774/v2/%(tenant_id)s", ... internalurl="http://192.168.206.130:8774/v2/%(tenant_id)s") python-keystoneclient-0.7.1/doc/source/releases.rst0000664000175400017540000000214312315030450023627 0ustar jenkinsjenkins00000000000000============= Release notes ============= 0.1.3 (August 31, 2012) ======================= * changed logging to report request and response independently in --debug mode * changed options to use hyphens instead of underscores * added support for PKI signed tokens with Keystone 0.1.2 (July 9, 2012) ==================== * added support for two-way SSL and --insecure option to allow for self-signed certificates * added support for password prompting if not provided * added support for bash completion for keystone * updated CLI options to use dashes instead of underscores 0.1.1 (June 25, 2012) ===================== * corrected versioning 0.1.0 (March 29, 2012) ====================== * released with OpenStack Essex and Diablo compatibility * forked from http://github.com/rackspace/python-novaclient * refactored to support Identity API (auth, tokens, services, roles, tenants, users, etc.) * removed legacy arguments of --username, --password, etc in migration to support a cross-openstack unified CLI convention defined at http://wiki.openstack.org/UnifiedCLI * required service ID for listing endpoints python-keystoneclient-0.7.1/doc/source/static/0000775000175400017540000000000012315030547022570 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/doc/source/static/basic.css0000664000175400017540000001462512315030450024364 0ustar jenkinsjenkins00000000000000/** * Sphinx stylesheet -- basic theme * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /* -- main layout ----------------------------------------------------------- */ div.clearer { clear: both; } /* -- relbar ---------------------------------------------------------------- */ div.related { width: 100%; font-size: 90%; } div.related h3 { display: none; } div.related ul { margin: 0; padding: 0 0 0 10px; list-style: none; } div.related li { display: inline; } div.related li.right { float: right; margin-right: 5px; } /* -- sidebar --------------------------------------------------------------- */ div.sphinxsidebarwrapper { padding: 10px 5px 0 10px; } div.sphinxsidebar { float: left; width: 230px; margin-left: -100%; font-size: 90%; } div.sphinxsidebar ul { list-style: none; } div.sphinxsidebar ul ul, div.sphinxsidebar ul.want-points { margin-left: 20px; list-style: square; } div.sphinxsidebar ul ul { margin-top: 0; margin-bottom: 0; } div.sphinxsidebar form { margin-top: 10px; } div.sphinxsidebar input { border: 1px solid #98dbcc; font-family: sans-serif; font-size: 1em; } img { border: 0; } /* -- search page ----------------------------------------------------------- */ ul.search { margin: 10px 0 0 20px; padding: 0; } ul.search li { padding: 5px 0 5px 20px; background-image: url(file.png); background-repeat: no-repeat; background-position: 0 7px; } ul.search li a { font-weight: bold; } ul.search li div.context { color: #888; margin: 2px 0 0 30px; text-align: left; } ul.keywordmatches li.goodmatch a { font-weight: bold; } /* -- index page ------------------------------------------------------------ */ table.contentstable { width: 90%; } table.contentstable p.biglink { line-height: 150%; } a.biglink { font-size: 1.3em; } span.linkdescr { font-style: italic; padding-top: 5px; font-size: 90%; } /* -- general index --------------------------------------------------------- */ table.indextable td { text-align: left; vertical-align: top; } table.indextable dl, table.indextable dd { margin-top: 0; margin-bottom: 0; } table.indextable tr.pcap { height: 10px; } table.indextable tr.cap { margin-top: 10px; background-color: #f2f2f2; } img.toggler { margin-right: 3px; margin-top: 3px; cursor: pointer; } /* -- general body styles --------------------------------------------------- */ a.headerlink { visibility: hidden; } h1:hover > a.headerlink, h2:hover > a.headerlink, h3:hover > a.headerlink, h4:hover > a.headerlink, h5:hover > a.headerlink, h6:hover > a.headerlink, dt:hover > a.headerlink { visibility: visible; } div.body p.caption { text-align: inherit; } div.body td { text-align: left; } .field-list ul { padding-left: 1em; } .first { } p.rubric { margin-top: 30px; font-weight: bold; } /* -- sidebars -------------------------------------------------------------- */ div.sidebar { margin: 0 0 0.5em 1em; border: 1px solid #ddb; padding: 7px 7px 0 7px; background-color: #ffe; width: 40%; float: right; } p.sidebar-title { font-weight: bold; } /* -- topics ---------------------------------------------------------------- */ div.topic { border: 1px solid #ccc; padding: 7px 7px 0 7px; margin: 10px 0 10px 0; } p.topic-title { font-size: 1.1em; font-weight: bold; margin-top: 10px; } /* -- admonitions ----------------------------------------------------------- */ div.admonition { margin-top: 10px; margin-bottom: 10px; padding: 7px; } div.admonition dt { font-weight: bold; } div.admonition dl { margin-bottom: 0; } p.admonition-title { margin: 0px 10px 5px 0px; font-weight: bold; } div.body p.centered { text-align: center; margin-top: 25px; } /* -- tables ---------------------------------------------------------------- */ table.docutils { border: 0; border-collapse: collapse; } table.docutils td, table.docutils th { padding: 1px 8px 1px 0; border-top: 0; border-left: 0; border-right: 0; border-bottom: 1px solid #aaa; } table.field-list td, table.field-list th { border: 0 !important; } table.footnote td, table.footnote th { border: 0 !important; } th { text-align: left; padding-right: 5px; } /* -- other body styles ----------------------------------------------------- */ dl { margin-bottom: 15px; } dd p { margin-top: 0px; } dd ul, dd table { margin-bottom: 10px; } dd { margin-top: 3px; margin-bottom: 10px; margin-left: 30px; } dt:target, .highlight { background-color: #fbe54e; } dl.glossary dt { font-weight: bold; font-size: 1.1em; } .field-list ul { margin: 0; padding-left: 1em; } .field-list p { margin: 0; } .refcount { color: #060; } .optional { font-size: 1.3em; } .versionmodified { font-style: italic; } .system-message { background-color: #fda; padding: 5px; border: 3px solid red; } .footnote:target { background-color: #ffa } .line-block { display: block; margin-top: 1em; margin-bottom: 1em; } .line-block .line-block { margin-top: 0; margin-bottom: 0; margin-left: 1.5em; } /* -- code displays --------------------------------------------------------- */ pre { overflow: auto; } td.linenos pre { padding: 5px 0px; border: 0; background-color: transparent; color: #aaa; } table.highlighttable { margin-left: 0.5em; } table.highlighttable td { padding: 0 0.5em 0 0.5em; } tt.descname { background-color: transparent; font-weight: bold; font-size: 1.2em; } tt.descclassname { background-color: transparent; } tt.xref, a tt { background-color: transparent; font-weight: bold; } h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { background-color: transparent; } /* -- math display ---------------------------------------------------------- */ img.math { vertical-align: middle; } div.body div.math p { text-align: center; } span.eqno { float: right; } /* -- printout stylesheet --------------------------------------------------- */ @media print { div.document, div.documentwrapper, div.bodywrapper { margin: 0 !important; width: 100%; } div.sphinxsidebar, div.related, div.footer, #top-link { display: none; } } python-keystoneclient-0.7.1/doc/source/static/openstack_logo.png0000664000175400017540000000712612315030450026304 0ustar jenkinsjenkins00000000000000PNG  IHDR8&ztEXtSoftwareAdobe ImageReadyqe< IDATxOP߮VB 8ccčĞtڤ:= %Gc;1\z襝`39tz2LO4v;yo:qqq96c0+i~O<Rf>}vgޮ:s̱J4ٹUI݀=>p=l Bol鵵5?tgi>C1M-Gz/@8g;4,Uo@8 G=T/%&UD@lsIZc͸"^,IU>EEZCjEobü.,ʲyFXQ̭x2a6H'X_(dJ:&2U4Ԁɕ{kxr.i:zH PYY]߮pe)Z6:Wѱp XvP|5:?,#;D- ǣBFy6ȚX>ڡ0D JeH J^."4pXN,"s.'yy7עƍ!wvΞ={ jB` p󧛎˭ZWI`x⬆/xa!!n6cpLmq1ޢy/Rqlj3"0a j ; \E8ۅ<~cQCMiq*ł6+[z'B|㱒=D >@i!8Nӝ"e y\L`(02H#V*M4> u?϶ c}`*ca!]7,iHMkU4O7Ԃ0N.vRT(3R~X_r&Mn:D@_v f הYfʌӾ gDiA/XsDiLhlF% 4wAc lγ(>NjaAZJR ST,L9nUʩSCOc%N^ @iYՒSHÏy^"&KI}d+1VNB\iq)8+shqe y[< -86=^e2Ëh1qZrfJ*T埠%,jÍgFMW/zRD4G,b3/n{Z_5676R+ f{{aUhfdYxabEwOib+2MxyBhitI۝IopV 8ʹOjb'}>v[$\\0'j$t- #E&LLOu̪/oD6GLnAuRzʇ5Aűv#^tBuG*)Ct[(-Y(O/F^ʦvd :ҙcrLkT1@N;1_+ k3ZLY^b'uk)T\/{;aбxrF77/)ǓjFMekTHH fe?~`{7 h}.8 7?$&<%ݻTN/ѬUNîF6gjU5{~w37|UWD*gׅ?ڰHoq`Y8+K]8MW,LJ4iYv2cVL;$C1L߆MEZИg߭g vXM9B Ke76]jjnUpݲ\*{3._scnN';d LA=`ҙ*IѓcFkIEenou:.H#'a A_HNDA0*g_rpU,A qbkϰ? $39kW^^XO"׿GJcZ_W^8I=Q6TTS,XgP\lʻ0"< ;7~ϣƁcM0%tWc`;ݛB `9-TխܮRug\liW-x1qVZՅqXX&xz\;Rݺu\dmreV/J&ވcpyL+*98oJz /ֻS!k[-<t]S)W['&\ޏ~wݤQ3Zw.vr|mGg:#5xc祮 \Z"7 NT}n TD9!w aS . hfO57'swD7=WJB)$C 9ÍLPMѥc"fĶ:σb3f9D5 nSey13;;zeM?u&]LJ=H҆,Oݒ4GKa$V//:v Jӡd(g7\S3CJ:Z[ E{3#ݺ3)ՓTp©w9 MM}Ibq?qnW+++DupA?_9Suv8YISƖݡ'>7iq+A8.6ח.e(B(7\L7 ~ɩܭQg?G:YmPeEQ\X,L'Zjy\X; ` >dp>)y0<[wO9Vc9c/f΍0IENDB`python-keystoneclient-0.7.1/doc/source/static/default.css0000664000175400017540000000707712315030450024732 0ustar jenkinsjenkins00000000000000/** * Sphinx stylesheet -- default theme * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @import url("basic.css"); /* -- page layout ----------------------------------------------------------- */ body { font-family: sans-serif; font-size: 100%; background-color: #11303d; color: #000; margin: 0; padding: 0; } div.document { background-color: #1c4e63; } div.documentwrapper { float: left; width: 100%; } div.bodywrapper { margin: 0 0 0 230px; } div.body { background-color: #ffffff; color: #000000; padding: 0 20px 30px 20px; } div.footer { color: #ffffff; width: 100%; padding: 9px 0 9px 0; text-align: center; font-size: 75%; } div.footer a { color: #ffffff; text-decoration: underline; } div.related { background-color: #133f52; line-height: 30px; color: #ffffff; } div.related a { color: #ffffff; } div.sphinxsidebar { } div.sphinxsidebar h3 { font-family: 'Trebuchet MS', sans-serif; color: #ffffff; font-size: 1.4em; font-weight: normal; margin: 0; padding: 0; } div.sphinxsidebar h3 a { color: #ffffff; } div.sphinxsidebar h4 { font-family: 'Trebuchet MS', sans-serif; color: #ffffff; font-size: 1.3em; font-weight: normal; margin: 5px 0 0 0; padding: 0; } div.sphinxsidebar p { color: #ffffff; } div.sphinxsidebar p.topless { margin: 5px 10px 10px 10px; } div.sphinxsidebar ul { margin: 10px; padding: 0; color: #ffffff; } div.sphinxsidebar a { color: #98dbcc; } div.sphinxsidebar input { border: 1px solid #98dbcc; font-family: sans-serif; font-size: 1em; } /* -- body styles ----------------------------------------------------------- */ a { color: #355f7c; text-decoration: none; } a:hover { text-decoration: underline; } div.body p, div.body dd, div.body li { text-align: left; line-height: 130%; } div.body h1, div.body h2, div.body h3, div.body h4, div.body h5, div.body h6 { font-family: 'Trebuchet MS', sans-serif; background-color: #f2f2f2; font-weight: normal; color: #20435c; border-bottom: 1px solid #ccc; margin: 20px -20px 10px -20px; padding: 3px 0 3px 10px; } div.body h1 { margin-top: 0; font-size: 200%; } div.body h2 { font-size: 160%; } div.body h3 { font-size: 140%; } div.body h4 { font-size: 120%; } div.body h5 { font-size: 110%; } div.body h6 { font-size: 100%; } a.headerlink { color: #c60f0f; font-size: 0.8em; padding: 0 4px 0 4px; text-decoration: none; } a.headerlink:hover { background-color: #c60f0f; color: white; } div.body p, div.body dd, div.body li { text-align: left; line-height: 130%; } div.admonition p.admonition-title + p { display: inline; } div.admonition p { margin-bottom: 5px; } div.admonition pre { margin-bottom: 5px; } div.admonition ul, div.admonition ol { margin-bottom: 5px; } div.note { background-color: #eee; border: 1px solid #ccc; } div.seealso { background-color: #ffc; border: 1px solid #ff6; } div.topic { background-color: #eee; } div.warning { background-color: #ffe4e4; border: 1px solid #f66; } p.admonition-title { display: inline; } p.admonition-title:after { content: ":"; } pre { padding: 5px; background-color: #eeffcc; color: #333333; line-height: 120%; border: 1px solid #ac9; border-left: none; border-right: none; } tt { background-color: #ecf0f3; padding: 0 1px 0 1px; font-size: 0.95em; } .warning tt { background: #efc2c2; } .note tt { background: #d6d6d6; } python-keystoneclient-0.7.1/doc/source/static/tweaks.css0000664000175400017540000000310312315030450024566 0ustar jenkinsjenkins00000000000000body { background: #fff url(../_static/header_bg.jpg) top left no-repeat; } #header { width: 950px; margin: 0 auto; height: 102px; } #header h1#logo { background: url(../_static/openstack_logo.png) top left no-repeat; display: block; float: left; text-indent: -9999px; width: 175px; height: 55px; } #navigation { background: url(../_static/header-line.gif) repeat-x 0 bottom; display: block; float: left; margin: 27px 0 0 25px; padding: 0; } #navigation li{ float: left; display: block; margin-right: 25px; } #navigation li a { display: block; font-weight: normal; text-decoration: none; background-position: 50% 0; padding: 20px 0 5px; color: #353535; font-size: 14px; } #navigation li a.current, #navigation li a.section { border-bottom: 3px solid #cf2f19; color: #cf2f19; } div.related { background-color: #cde2f8; border: 1px solid #b0d3f8; } div.related a { color: #4078ba; text-shadow: none; } div.sphinxsidebarwrapper { padding-top: 0; } pre { color: #555; } div.documentwrapper h1, div.documentwrapper h2, div.documentwrapper h3, div.documentwrapper h4, div.documentwrapper h5, div.documentwrapper h6 { font-family: 'PT Sans', sans-serif !important; color: #264D69; border-bottom: 1px dotted #C5E2EA; padding: 0; background: none; padding-bottom: 5px; } div.documentwrapper h3 { color: #CF2F19; } a.headerlink { color: #fff !important; margin-left: 5px; background: #CF2F19 !important; } div.body { margin-top: -25px; margin-left: 230px; } div.document { width: 960px; margin: 0 auto; }python-keystoneclient-0.7.1/doc/source/static/nature.css0000664000175400017540000001015312315030450024571 0ustar jenkinsjenkins00000000000000/* * nature.css_t * ~~~~~~~~~~~~ * * Sphinx stylesheet -- nature theme. * * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ @import url("basic.css"); /* -- page layout ----------------------------------------------------------- */ body { font-family: Arial, sans-serif; font-size: 100%; background-color: #111; color: #555; margin: 0; padding: 0; } div.documentwrapper { float: left; width: 100%; } div.bodywrapper { margin: 0 0 0 {{ theme_sidebarwidth|toint }}px; } hr { border: 1px solid #B1B4B6; } div.document { background-color: #eee; } div.body { background-color: #ffffff; color: #3E4349; padding: 0 30px 30px 30px; font-size: 0.9em; } div.footer { color: #555; width: 100%; padding: 13px 0; text-align: center; font-size: 75%; } div.footer a { color: #444; text-decoration: underline; } div.related { background-color: #6BA81E; line-height: 32px; color: #fff; text-shadow: 0px 1px 0 #444; font-size: 0.9em; } div.related a { color: #E2F3CC; } div.sphinxsidebar { font-size: 0.75em; line-height: 1.5em; } div.sphinxsidebarwrapper{ padding: 20px 0; } div.sphinxsidebar h3, div.sphinxsidebar h4 { font-family: Arial, sans-serif; color: #222; font-size: 1.2em; font-weight: normal; margin: 0; padding: 5px 10px; background-color: #ddd; text-shadow: 1px 1px 0 white } div.sphinxsidebar h4{ font-size: 1.1em; } div.sphinxsidebar h3 a { color: #444; } div.sphinxsidebar p { color: #888; padding: 5px 20px; } div.sphinxsidebar p.topless { } div.sphinxsidebar ul { margin: 10px 20px; padding: 0; color: #000; } div.sphinxsidebar a { color: #444; } div.sphinxsidebar input { border: 1px solid #ccc; font-family: sans-serif; font-size: 1em; } div.sphinxsidebar input[type=text]{ margin-left: 20px; } /* -- body styles ----------------------------------------------------------- */ a { color: #005B81; text-decoration: none; } a:hover { color: #E32E00; text-decoration: underline; } div.body h1, div.body h2, div.body h3, div.body h4, div.body h5, div.body h6 { font-family: Arial, sans-serif; background-color: #BED4EB; font-weight: normal; color: #212224; margin: 30px 0px 10px 0px; padding: 5px 0 5px 10px; text-shadow: 0px 1px 0 white } div.body h1 { border-top: 20px solid white; margin-top: 0; font-size: 200%; } div.body h2 { font-size: 150%; background-color: #C8D5E3; } div.body h3 { font-size: 120%; background-color: #D8DEE3; } div.body h4 { font-size: 110%; background-color: #D8DEE3; } div.body h5 { font-size: 100%; background-color: #D8DEE3; } div.body h6 { font-size: 100%; background-color: #D8DEE3; } a.headerlink { color: #c60f0f; font-size: 0.8em; padding: 0 4px 0 4px; text-decoration: none; } a.headerlink:hover { background-color: #c60f0f; color: white; } div.body p, div.body dd, div.body li { line-height: 1.5em; } div.admonition p.admonition-title + p { display: inline; } div.highlight{ background-color: white; } div.note { background-color: #eee; border: 1px solid #ccc; } div.seealso { background-color: #ffc; border: 1px solid #ff6; } div.topic { background-color: #eee; } div.warning { background-color: #ffe4e4; border: 1px solid #f66; } p.admonition-title { display: inline; } p.admonition-title:after { content: ":"; } pre { padding: 10px; background-color: White; color: #222; line-height: 1.2em; border: 1px solid #C6C9CB; font-size: 1.1em; margin: 1.5em 0 1.5em 0; -webkit-box-shadow: 1px 1px 1px #d8d8d8; -moz-box-shadow: 1px 1px 1px #d8d8d8; } tt { background-color: #ecf0f3; color: #222; /* padding: 1px 2px; */ font-size: 1.1em; font-family: monospace; } .viewcode-back { font-family: Arial, sans-serif; } div.viewcode-block:target { background-color: #f4debf; border-top: 1px solid #ac9; border-bottom: 1px solid #ac9; } python-keystoneclient-0.7.1/doc/source/static/header_bg.jpg0000664000175400017540000000723212315030450025167 0ustar jenkinsjenkins00000000000000JFIFddDuckyPAdobed      u !1AQa"q2BRb!1 ?ezFUO8H+{|mfu7uڛk$+N7n. aUBn\jgZ˒i8XJ5.i?RwmR|+5띟:΅.V^=PW 'fˉ^|{rTsa7&3!}5kt1w7ZZMZG?85oQ:F_fF;A^>ΚR˷ZF6λ[Ӯ? BhآIUƩwzW\寱38˖VG % ZY :&dWu׼Ko$iߏ$=|^Jh蕖+I$6#x_ @H "M6(P?׭VQSS.%/R;Vo*nQSU۩54bUm7\<1L&L]pM#S \/Wbx64Ye{)~ٟ+~ej.&T^ 9߭6>#_ufZfCXVWTǃ;flΚ^/;cմMb-:鮃tnYZH6:+=5EµoHD_W)-4 @7WV)o@ ٸg}M:rM?r54Y|Ez'A>#NaVֳ};q4m" U4H/@mimP_.8ΐ%: ֕Nn)Vի㼒bx򋚚ԗfM\}VbR&+e鉬jk]tRڋl?LIM|R9Vg |l J%$z+җ`u{z,s:cQdZyzƒX|RXήFc_uh܊dXJi aJ+Ejդ8޶5SkiVߨx")0Um F=}AMWJ77GkQ5\s0=8#yBK[YInA 6l*@˩f;u_rJF:Z"kBR--G\3eճ_],Vnnخ'V17 (Z#/l ĿO[i7F"W,R5ݿ1ܴ@W[aksHuEu,uQiqVoV}IR8W oϊ_V* uX(ź^{)JjQXêEE}I"WRQ 'TJAtO Ge;dǦQf[ZR"i ZǦxs*tH"i[ A"ڭ:Z@E_(+Jk/ȣەdA&YSkOM:5Īύg3QƵ#R9޼l$\WU^*3ڔt|k::qv|ָ( UZ3Z^[0I;>FR%#VJ/IBR"i'‘uk0E#]y%H&cS61j>Se6yܔ1cKz/f6WX*;/GU_@,?UKPEi( m 4 ,@++$]Yrj[WD]WUj&?Nr^^+AcЕS%W>ml03ZWSݶwOrQLDE[k/M5ī֣+AHU9 @G5Zj9#PVA 4ګ07Ӊxi 7" X`^$ǫťGR\UWТO}ˢ  Đx8@}ZBU H4E(UQaP>7EI +Q@@q:@YUB}^, @R@^( S|FlkX@} ,B$0j@@,0 ,` (48//,h@@XdR! @J" $1")); }); return $(returning); }, linkUser: function() { var returning = []; var regexp = /[\@]+([A-Za-z0-9-_]+)/gi; this.each(function() { returning.push(this.replace(regexp,"@$1")); }); return $(returning); }, linkHash: function() { var returning = []; var regexp = / [\#]+([A-Za-z0-9-_]+)/gi; this.each(function() { returning.push(this.replace(regexp, ' #$1')); }); return $(returning); }, capAwesome: function() { var returning = []; this.each(function() { returning.push(this.replace(/\b(awesome)\b/gi, '$1')); }); return $(returning); }, capEpic: function() { var returning = []; this.each(function() { returning.push(this.replace(/\b(epic)\b/gi, '$1')); }); return $(returning); }, makeHeart: function() { var returning = []; this.each(function() { returning.push(this.replace(/(<)+[3]/gi, "")); }); return $(returning); } }); function relative_time(time_value) { var parsed_date = Date.parse(time_value); var relative_to = (arguments.length > 1) ? arguments[1] : new Date(); var delta = parseInt((relative_to.getTime() - parsed_date) / 1000); var pluralize = function (singular, n) { return '' + n + ' ' + singular + (n == 1 ? '' : 's'); }; if(delta < 60) { return 'less than a minute ago'; } else if(delta < (45*60)) { return 'about ' + pluralize("minute", parseInt(delta / 60)) + ' ago'; } else if(delta < (24*60*60)) { return 'about ' + pluralize("hour", parseInt(delta / 3600)) + ' ago'; } else { return 'about ' + pluralize("day", parseInt(delta / 86400)) + ' ago'; } } function build_url() { var proto = ('https:' == document.location.protocol ? 'https:' : 'http:'); if (s.list) { return proto+"//api.twitter.com/1/"+s.username[0]+"/lists/"+s.list+"/statuses.json?per_page="+s.count+"&callback=?"; } else if (s.query == null && s.username.length == 1) { return proto+'//twitter.com/status/user_timeline/'+s.username[0]+'.json?count='+s.count+'&callback=?'; } else { var query = (s.query || 'from:'+s.username.join('%20OR%20from:')); return proto+'//search.twitter.com/search.json?&q='+query+'&rpp='+s.count+'&callback=?'; } } return this.each(function(){ var list = $('
    ').appendTo(this); var intro = '

    '+s.intro_text+'

    '; var outro = '

    '+s.outro_text+'

    '; var loading = $('

    '+s.loading_text+'

    '); if(typeof(s.username) == "string"){ s.username = [s.username]; } if (s.loading_text) $(this).append(loading); $.getJSON(build_url(), function(data){ if (s.loading_text) loading.remove(); if (s.intro_text) list.before(intro); $.each((data.results || data), function(i,item){ // auto join text based on verb tense and content if (s.join_text == "auto") { if (item.text.match(/^(@([A-Za-z0-9-_]+)) .*/i)) { var join_text = s.auto_join_text_reply; } else if (item.text.match(/(^\w+:\/\/[A-Za-z0-9-_]+\.[A-Za-z0-9-_:%&\?\/.=]+) .*/i)) { var join_text = s.auto_join_text_url; } else if (item.text.match(/^((\w+ed)|just) .*/im)) { var join_text = s.auto_join_text_ed; } else if (item.text.match(/^(\w*ing) .*/i)) { var join_text = s.auto_join_text_ing; } else { var join_text = s.auto_join_text_default; } } else { var join_text = s.join_text; }; var from_user = item.from_user || item.user.screen_name; var profile_image_url = item.profile_image_url || item.user.profile_image_url; var join_template = ' '+join_text+' '; var join = ((s.join_text) ? join_template : ' '); var avatar_template = ''+from_user+'\'s avatar'; var avatar = (s.avatar_size ? avatar_template : ''); var date = ''+relative_time(item.created_at)+''; var text = '' +$([item.text]).linkUrl().linkUser().linkHash().makeHeart().capAwesome().capEpic()[0]+ ''; // until we create a template option, arrange the items below to alter a tweet's display. list.append('
  • ' + avatar + date + join + text + '
  • '); list.children('li:first').addClass('tweet_first'); list.children('li:odd').addClass('tweet_even'); list.children('li:even').addClass('tweet_odd'); }); if (s.outro_text) list.after(outro); }); }); }; })(jQuery);python-keystoneclient-0.7.1/doc/source/static/header-line.gif0000664000175400017540000000006012315030450025421 0ustar jenkinsjenkins00000000000000GIF89a!,Q;python-keystoneclient-0.7.1/doc/source/middlewarearchitecture.rst0000664000175400017540000003504412315030450026552 0ustar jenkinsjenkins00000000000000.. Copyright 2011-2013 OpenStack Foundation All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ======================= Middleware Architecture ======================= Abstract ======== The Keystone middleware architecture supports a common authentication protocol in use between the OpenStack projects. By using keystone as a common authentication and authorization mechanism, the OpenStack project can plug in to existing authentication and authorization systems in use by existing environments. In this document, we describe the architecture and responsibilities of the authentication middleware which acts as the internal API mechanism for OpenStack projects based on the WSGI standard. This documentation describes the implementation in :class:`keystoneclient.middleware.auth_token` Specification Overview ====================== 'Authentication' is the process of determining that users are who they say they are. Typically, 'authentication protocols' such as HTTP Basic Auth, Digest Access, public key, token, etc, are used to verify a user's identity. In this document, we define an ''authentication component'' as a software module that implements an authentication protocol for an OpenStack service. OpenStack is using a token based mechanism to represent authentication and authorization. At a high level, an authentication middleware component is a proxy that intercepts HTTP calls from clients and populates HTTP headers in the request context for other WSGI middleware or applications to use. The general flow of the middleware processing is: * clear any existing authorization headers to prevent forgery * collect the token from the existing HTTP request headers * validate the token * if valid, populate additional headers representing the identity that has been authenticated and authorized * if invalid, or no token present, reject the request (HTTPUnauthorized) or pass along a header indicating the request is unauthorized (configurable in the middleware) * if the keystone service is unavailable to validate the token, reject the request with HTTPServiceUnavailable. .. _authComponent: Authentication Component ------------------------ Figure 1. Authentication Component .. image:: images/graphs_authComp.svg :width: 100% :height: 180 :alt: An Authentication Component The middleware may also be configured to operate in a 'delegated mode'. In this mode, the decision to reject an unauthenticated client is delegated to the OpenStack service, as illustrated in :ref:`authComponentDelegated`. Here, requests are forwarded to the OpenStack service with an identity status message that indicates whether the client's identity has been confirmed or is indeterminate. It is the OpenStack service that decides whether or not a reject message should be sent to the client. .. _authComponentDelegated: Authentication Component (Delegated Mode) ----------------------------------------- Figure 2. Authentication Component (Delegated Mode) .. image:: images/graphs_authCompDelegate.svg :width: 100% :height: 180 :alt: An Authentication Component (Delegated Mode) .. _deployStrategies: Deployment Strategy =================== The middleware is intended to be used inline with OpenStack wsgi components, based on the Oslo WSGI middleware class. It is typically deployed as a configuration element in a paste configuration pipeline of other middleware components, with the pipeline terminating in the service application. The middleware conforms to the python WSGI standard [PEP-333]_. In initializing the middleware, a configuration item (which acts like a python dictionary) is passed to the middleware with relevant configuration options. Configuration ------------- The middleware is configured within the config file of the main application as a WSGI component. Example for the auth_token middleware:: [app:myService] paste.app_factory = myService:app_factory [pipeline:main] pipeline = authtoken myService [filter:authtoken] paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory auth_host = 127.0.0.1 auth_port = 35357 auth_protocol = http admin_token = Super999Sekret888Password777 admin_user = admin admin_password = SuperSekretPassword admin_tenant_name = service ;Uncomment next line to use Swift MemcacheRing ;cache = swift.cache ;Uncomment next line and check ip:port to use memcached to cache tokens ;memcached_servers = 127.0.0.1:11211 ;Uncomment next 2 lines to turn on memcache protection ;memcache_security_strategy = ENCRYPT ;memcache_secret_key = change_me ;Uncomment next 2 lines if Keystone server is validating client cert ;certfile = ;keyfile = ;Uncomment next line to opt-out of service catalog ;include_service_catalog = False For services which have a separate paste-deploy ini file, auth_token middleware can be alternatively configured in [keystone_authtoken] section in the main config file. For example in Nova, all middleware parameters can be removed from api-paste.ini:: [filter:authtoken] paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory and set in nova.conf:: [DEFAULT] ... auth_strategy=keystone [keystone_authtoken] auth_host = 127.0.0.1 auth_port = 35357 auth_protocol = http admin_user = admin admin_password = SuperSekretPassword admin_tenant_name = service Note that middleware parameters in paste config take priority, they must be removed to use values in [keystone_authtoken] section. Configuration Options --------------------- * ``auth_admin_prefix``: Prefix to prepend at the beginning of the path * ``auth_host``: (required) the host providing the keystone service API endpoint for validating and requesting tokens * ``auth_port``: (optional, default `35357`) the port used to validate tokens * ``auth_protocol``: (optional, default `https`) * ``auth_uri``: (optional, defaults to `auth_protocol`://`auth_host`:`auth_port`) * ``auth_version``: API version of the admin Identity API endpoint * ``delay_auth_decision``: (optional, default `0`) (off). If on, the middleware will not reject invalid auth requests, but will delegate that decision to downstream WSGI components. * ``http_connect_timeout``: (optional) Request timeout value for communicating with Identity API server. * ``http_request_max_retries``: (default 3) How many times are we trying to reconnect when communicating with Identity API Server. * ``http_handler``: (optional) Allows to pass in the name of a fake http_handler callback function used instead of `httplib.HTTPConnection` or `httplib.HTTPSConnection`. Useful for unit testing where network is not available. * ``admin_token``: either this or the following three options are required. If set, this is a single shared secret with the keystone configuration used to validate tokens. * ``admin_user``, ``admin_password``, ``admin_tenant_name``: if ``admin_token`` is not set, or invalid, then admin_user, admin_password, and admin_tenant_name are defined as a service account which is expected to have been previously configured in Keystone to validate user tokens. * ``cache``: (optional) Env key for the swift cache * ``certfile``: (required, if Keystone server requires client cert) * ``keyfile``: (required, if Keystone server requires client cert) This can be the same as the certfile if the certfile includes the private key. * ``cafile``: (optional, defaults to use system CA bundle) the path to a PEM encoded CA file/bundle that will be used to verify HTTPS connections. * ``insecure``: (optional, default `False`) Don't verify HTTPS connections (overrides `cafile`). * ``signing_dir``: (optional) Directory used to cache files related to PKI tokens * ``memcached_servers``: (optional) If defined, the memcache server(s) to use for caching * ``token_cache_time``: (default 300) In order to prevent excessive requests and validations, the middleware uses an in-memory cache for the tokens the Keystone API returns. This is only valid if memcache_servers s defined. Set to -1 to disable caching completely. * ``memcache_security_strategy``: (optional) if defined, indicate whether token data should be authenticated or authenticated and encrypted. Acceptable values are MAC or ENCRYPT. If MAC, token data is authenticated (with HMAC) in the cache. If ENCRYPT, token data is encrypted and authenticated in the cache. If the value is not one of these options or empty, auth_token will raise an exception on initialization. * ``memcache_secret_key``: (mandatory if memcache_security_strategy is defined) this string is used for key derivation. * ``include_service_catalog``: (optional, default `True`) Indicate whether to set the X-Service-Catalog header. If False, middleware will not ask for service catalog on token validation and will not set the X-Service-Catalog header. * ``enforce_token_bind``: (default ``permissive``) Used to control the use and type of token binding. Can be set to: "disabled" to not check token binding. "permissive" (default) to validate binding information if the bind type is of a form known to the server and ignore it if not. "strict" like "permissive" but if the bind type is unknown the token will be rejected. "required" any form of token binding is needed to be allowed. Finally the name of a binding method that must be present in tokens. Caching for improved response ----------------------------- In order to prevent excessive requests and validations, the middleware uses an in-memory cache for the tokens the keystone API returns. Keep in mind that invalidated tokens may continue to work if they are still in the token cache, so token_cache_time is configurable. For larger deployments, the middleware also supports memcache based caching. * ``memcached_servers``: (optonal) if defined, the memcache server(s) to use for cacheing. It will be ignored if Swift MemcacheRing is used instead. * ``token_cache_time``: (optional, default 300 seconds) Set to -1 to disable caching completely. When deploying auth_token middleware with Swift, user may elect to use Swift MemcacheRing instead of the local Keystone memcache. The Swift MemcacheRing object is passed in from the request environment and it defaults to 'swift.cache'. However it could be different, depending on deployment. To use Swift MemcacheRing, you must provide the ``cache`` option. * ``cache``: (optional) if defined, the environment key where the Swift MemcacheRing object is stored. Memcached and System Time ========================= When using `memcached`_ with ``auth_token`` middleware, ensure that the system time of memcached hosts is set to UTC. Memcached uses the host's system time in determining whether a key has expired, whereas Keystone sets key expiry in UTC. The timezone used by Keystone and memcached must match if key expiry is to behave as expected. .. _`memcached`: http://memcached.org/ Memcache Protection =================== When using memcached, we are storing user tokens and token validation information into the cache as raw data. Which means that anyone who has access to the memcache servers can read and modify data stored there. To mitigate this risk, ``auth_token`` middleware provides an option to authenticate and optionally encrypt the token data stored in the cache. * ``memcache_security_strategy``: (optional) if defined, indicate whether token data should be authenticated or authenticated and encrypted. Acceptable values are ``MAC`` or ``ENCRYPT``. If ``MAC``, token data is authenticated (with HMAC) in the cache. If ``ENCRYPT``, token data is encrypted and authenticated in the cache. If the value is not one of these options or empty, ``auth_token`` will raise an exception on initialization. * ``memcache_secret_key``: (optional, mandatory if ``memcache_security_strategy`` is defined) this string is used for key derivation. If ``memcache_security_strategy`` is defined and ``memcache_secret_key`` is absent, ``auth_token`` will raise an exception on initialization. Exchanging User Information =========================== The middleware expects to find a token representing the user with the header ``X-Auth-Token`` or ``X-Storage-Token``. `X-Storage-Token` is supported for swift/cloud files and for legacy Rackspace use. If the token isn't present and the middleware is configured to not delegate auth responsibility, it will respond to the HTTP request with HTTPUnauthorized, returning the header ``WWW-Authenticate`` with the value `Keystone uri='...'` to indicate where to request a token. The auth_uri returned is configured with the middleware. The authentication middleware extends the HTTP request with the header ``X-Identity-Status``. If a request is successfully authenticated, the value is set to `Confirmed`. If the middleware is delegating the auth decision to the service, then the status is set to `Invalid` if the auth request was unsuccessful. Extended the request with additional User Information ----------------------------------------------------- :py:class:`keystoneclient.middleware.auth_token.AuthProtocol` extends the request with additional information if the user has been authenticated. X-Identity-Status Provides information on whether the request was authenticated or not. X-Tenant-Id The unique, immutable tenant Id X-Tenant-Name The unique, but mutable (it can change) tenant name. X-User-Id The user id of the user used to log in X-User-Name The username used to log in X-Roles The roles associated with that user Deprecated additions -------------------- X-Tenant Provides the tenant name. This is to support any legacy implementations before Keystone switched to an ID/Name schema for tenants. X-User The username used to log in. This is to support any legacy implementations before Keystone switched to an ID/Name schema for tenants. X-Role The roles associated with that user References ========== .. [PEP-333] pep0333 Phillip J Eby. 'Python Web Server Gateway Interface v1.0.'' http://www.python.org/dev/peps/pep-0333/. python-keystoneclient-0.7.1/doc/source/conf.py0000664000175400017540000001750012315030450022574 0ustar jenkinsjenkins00000000000000# -*- coding: utf-8 -*- # # python-keystoneclient documentation build configuration file, created by # sphinx-quickstart on Sun Dec 6 14:19:25 2009. # # This file is execfile()d with the current directory set to its containing # dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. from __future__ import unicode_literals import os import sys import pbr.version sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))) # NOTE(blk-u): Path for our Sphinx extension, remove when # https://launchpad.net/bugs/1260495 is fixed. sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.append(os.path.abspath('.')) # -- General configuration ---------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.intersphinx', # NOTE(blk-u): Uncomment the [pbr] section in setup.cfg and # remove this Sphinx extension when # https://launchpad.net/bugs/1260495 is fixed. 'ext.apidoc', ] todo_include_todos = True # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8' # The master toctree document. master_doc = 'index' # General information about the project. project = 'python-keystoneclient' copyright = 'OpenStack Contributors' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. version_info = pbr.version.VersionInfo('python-keystoneclient') # The short X.Y version. version = version_info.version_string() # The full version, including alpha/beta/rc tags. release = version_info.release_string() # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = [] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # Grouping the document tree for man pages. # List of tuples 'sourcefile', 'target', 'title', 'Authors name', 'manual' man_pages = [ ('man/keystone', 'keystone', 'Client for OpenStack Identity API', ['OpenStack Contributors'], 1), ] # -- Options for HTML output -------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. html_theme_path = ["."] html_theme = '_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. git_cmd = "git log --pretty=format:'%ad, commit %h' --date=local -n1" html_last_updated_fmt = os.popen(git_cmd).read() # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'python-keystoneclientdoc' # -- Options for LaTeX output ------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]) # . latex_documents = [ ('index', 'python-keystoneclient.tex', 'python-keystoneclient Documentation', 'Nebula Inc, based on work by Rackspace and Jacob Kaplan-Moss', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'python': ('http://docs.python.org/', None), 'nova': ('http://nova.openstack.org', None), 'swift': ('http://swift.openstack.org', None), 'glance': ('http://glance.openstack.org', None)} python-keystoneclient-0.7.1/doc/source/_theme/0000775000175400017540000000000012315030547022542 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/doc/source/_theme/theme.conf0000664000175400017540000000010712315030450024502 0ustar jenkinsjenkins00000000000000[theme] inherit = basic stylesheet = nature.css pygments_style = tango python-keystoneclient-0.7.1/doc/source/_theme/layout.html0000664000175400017540000000730512315030450024743 0ustar jenkinsjenkins00000000000000{% extends "basic/layout.html" %} {% set css_files = css_files + ['_static/tweaks.css'] %} {% set script_files = script_files + ['_static/jquery.tweet.js'] %} {%- macro sidebar() %} {%- if not embedded %}{% if not theme_nosidebar|tobool %}
    {%- block sidebarlogo %} {%- if logo %} {%- endif %} {%- endblock %} {%- block sidebartoc %} {%- if display_toc %}

    {{ _('Table Of Contents') }}

    {{ toc }} {%- endif %} {%- endblock %} {%- block sidebarrel %} {%- if prev %}

    {{ _('Previous topic') }}

    {{ prev.title }}

    {%- endif %} {%- if next %}

    {{ _('Next topic') }}

    {{ next.title }}

    {%- endif %} {%- endblock %} {%- block sidebarsourcelink %} {%- if show_source and has_source and sourcename %}

    {{ _('This Page') }}

    {%- endif %} {%- endblock %} {%- if customsidebar %} {% include customsidebar %} {%- endif %} {%- block sidebarsearch %} {%- if pagename != "search" %} {%- endif %} {%- endblock %}
    {%- endif %}{% endif %} {%- endmacro %} {% block relbar1 %}{% endblock relbar1 %} {% block header %} {% endblock %}python-keystoneclient-0.7.1/doc/source/index.rst0000664000175400017540000000176312315030450023142 0ustar jenkinsjenkins00000000000000Python bindings to the OpenStack Identity API (Keystone) ======================================================== This is a client for OpenStack Identity API. There's a Python API for :doc:`Identity API v3 ` and :doc:`v2 ` (the :mod:`keystoneclient` modules), and a command-line script (installed as :doc:`keystone `). Contents: .. toctree:: :maxdepth: 1 releases man/keystone using-api-v2 using-api-v3 middlewarearchitecture api/modules Contributing ============ Code is hosted `on GitHub`_. Submit bugs to the Keystone project on `Launchpad`_. Submit code to the ``openstack/python-keystoneclient`` project using `Gerrit`_. .. _on GitHub: https://github.com/openstack/python-keystoneclient .. _Launchpad: https://launchpad.net/python-keystoneclient .. _Gerrit: http://wiki.openstack.org/GerritWorkflow Run tests with ``python setup.py test``. Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` python-keystoneclient-0.7.1/doc/source/images/0000775000175400017540000000000012315030547022546 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/doc/source/images/graphs_authComp.svg0000664000175400017540000000570612315030450026414 0ustar jenkinsjenkins00000000000000 AuthComp AuthComp Auth Component AuthComp->Reject Reject Unauthenticated Requests Service OpenStack Service AuthComp->Service Forward Authenticated Requests Start->AuthComp python-keystoneclient-0.7.1/doc/source/images/graphs_authCompDelegate.svg0000664000175400017540000000702012315030450030036 0ustar jenkinsjenkins00000000000000 AuthCompDelegate AuthComp Auth Component AuthComp->Reject Reject Requests Indicated by the Service Service OpenStack Service AuthComp->Service Forward Requests with Identiy Status Service->AuthComp Send Response OR Reject Message Start->AuthComp python-keystoneclient-0.7.1/doc/source/using-api-v3.rst0000664000175400017540000000752012315030450024252 0ustar jenkinsjenkins00000000000000================= The Client v3 API ================= Introduction ============ The main concepts in the Identity v3 API are: * credentials * domains * endpoints * groups * policies * projects * roles * services * trusts * users The ``keystoneclient.v3.client`` API lets you query and make changes through ``managers``. For example, to manipulate a project (formerly called tenant), you interact with a ``keystoneclient.v3.projects.ProjectManager`` object. You obtain access to managers through attributes of a ``keystoneclient.v3.client.Client`` object. For example, the ``projects`` attribute of a ``Client`` object is a projects manager:: >>> from keystoneclient.v3 import client >>> keystone = client.Client(...) >>> keystone.projects.list() # List projects While it is possible to instantiate a ``keystoneclient.v3.client.Client`` object (as done above for clarity), the recommended approach is to use the discovery mechanism provided by the ``keystone.client.Client`` class. The appropriate class will be instantiated depending on the API versions available:: >>> from keystoneclient import client >>> keystone = ... client.Client(auth_url='http://localhost:5000', ...) >>> type(keystone) One can force the use of a specific version of the API, either by using the ``version`` keyword argument:: >>> from keystoneclient import client >>> keystone = client.Client(auth_url='http://localhost:5000', version=(2,), ...) >>> type(keystone) >>> keystone = client.Client(auth_url='http://localhost:5000', version=(3,), ...) >>> type(keystone) Or by specifying directly the specific API version authentication URL as the auth_url keyword argument:: >>> from keystoneclient import client >>> keystone = ... client.Client(auth_url='http://localhost:5000/v2.0', ...) >>> type(keystone) >>> keystone = ... client.Client(auth_url='http://localhost:5000/v3', ...) >>> type(keystone) Upon successful authentication, a ``keystoneclient.v3.client.Client`` object is returned (when using the Identity v3 API). Authentication and examples of common tasks are provided below. You can generally expect that when the client needs to propagate an exception it will raise an instance of subclass of ``keystoneclient.exceptions.ClientException`` Authenticating ============== You can authenticate against Keystone using a username, a user domain name (which will default to 'Default' if it is not specified) and a password:: >>> from keystoneclient import client >>> auth_url = 'http://localhost:5000' >>> username = 'adminUser' >>> user_domain_name = 'Default' >>> password = 'secreetword' >>> keystone = client.Client(auth_url=auth_url, version=(3,), ... username=username, password=password, ... user_domain_name=user_domain_name) You may optionally specify a domain or project (along with its project domain name), to obtain a scoped token:: >>> from keystoneclient import client >>> auth_url = 'http://localhost:5000' >>> username = 'adminUser' >>> user_domain_name = 'Default' >>> project_name = 'demo' >>> project_domain_name = 'Default' >>> password = 'secreetword' >>> keystone = client.Client(auth_url=auth_url, version=(3,), ... username=username, password=password, ... user_domain_name=user_domain_name, ... project_name=project_name, ... project_domain_name=project_domain_name) python-keystoneclient-0.7.1/doc/source/man/0000775000175400017540000000000012315030547022054 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/doc/source/man/keystone.rst0000664000175400017540000000704312315030450024444 0ustar jenkinsjenkins00000000000000======================================== :program:`keystone` command line utility ======================================== .. program:: keystone .. highlight:: bash SYNOPSIS ======== :program:`keystone` [options] [command-options] :program:`keystone help` :program:`keystone help` DESCRIPTION =========== The :program:`keystone` command line utility interacts with services providing OpenStack Identity API (e.g. Keystone). To communicate with the API, you will need to be authenticated - and the :program:`keystone` provides multiple options for this. While bootstrapping keystone the authentication is accomplished with a shared secret token and the location of the Identity API endpoint. The shared secret token is configured in keystone.conf as "admin_token". You can specify those values on the command line with :option:`--os-token` and :option:`--os-endpoint`, or set them in environment variables: .. envvar:: OS_SERVICE_TOKEN Your keystone administrative token .. envvar:: OS_SERVICE_ENDPOINT Your Identity API endpoint The command line options will override any environment variables set. If you already have accounts, you can use your OpenStack username and password. You can do this with the :option:`--os-username`, :option:`--os-password`. Keystone allows a user to be associated with one or more projects which are historically called tenants. To specify the project for which you want to authorize against, you may optionally specify a :option:`--os-tenant-id` or :option:`--os-tenant-name`. Instead of using options, it is easier to just set them as environment variables: .. envvar:: OS_USERNAME Your Keystone username. .. envvar:: OS_PASSWORD Your Keystone password. .. envvar:: OS_TENANT_NAME Name of Keystone project. .. envvar:: OS_TENANT_ID ID of Keystone Tenant. .. envvar:: OS_AUTH_URL The OpenStack API server URL. .. envvar:: OS_IDENTITY_API_VERSION The OpenStack Identity API version. .. envvar:: OS_CACERT The location for the CA truststore (PEM formatted) for this client. .. envvar:: OS_CERT The location for the keystore (PEM formatted) containing the public key of this client. This keystore can also optionally contain the private key of this client. .. envvar:: OS_KEY The location for the keystore (PEM formatted) containing the private key of this client. This value can be empty if the private key is included in the OS_CERT file. For example, in Bash you'd use:: export OS_USERNAME=yourname export OS_PASSWORD=yadayadayada export OS_TENANT_NAME=myproject export OS_AUTH_URL=http(s)://example.com:5000/v2.0/ export OS_IDENTITY_API_VERSION=2.0 export OS_CACERT=/etc/keystone/yourca.pem export OS_CERT=/etc/keystone/yourpublickey.pem export OS_KEY=/etc/keystone/yourprivatekey.pem OPTIONS ======= To get a list of available commands and options run:: keystone help To get usage and options of a command:: keystone help EXAMPLES ======== Get information about endpoint-create command:: keystone help endpoint-create View endpoints of OpenStack services:: keystone catalog Create a 'service' project:: keystone tenant-create --name=service Create service user for nova:: keystone user-create --name=nova \ --tenant_id= \ --email=nova@nothing.com View roles:: keystone role-list BUGS ==== Keystone client is hosted in Launchpad so you can view current bugs at https://bugs.launchpad.net/python-keystoneclient/. python-keystoneclient-0.7.1/doc/source/_templates/0000775000175400017540000000000012315030547023436 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/doc/source/_templates/.placeholder0000664000175400017540000000000012315030450025700 0ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/setup.cfg0000664000175400017540000000251612315030547021061 0ustar jenkinsjenkins00000000000000[metadata] name = python-keystoneclient summary = Client Library for OpenStack Identity description-file = README.rst author = OpenStack author-email = openstack-dev@lists.openstack.org home-page = http://www.openstack.org/ classifier = Environment :: OpenStack Intended Audience :: Information Technology Intended Audience :: System Administrators License :: OSI Approved :: Apache Software License Operating System :: POSIX :: Linux Programming Language :: Python Programming Language :: Python :: 2 Programming Language :: Python :: 2.7 Programming Language :: Python :: 2.6 Programming Language :: Python :: 3 Programming Language :: Python :: 3.3 [files] packages = keystoneclient [global] setup-hooks = pbr.hooks.setup_hook [entry_points] console_scripts = keystone = keystoneclient.shell:main [build_sphinx] source-dir = doc/source build-dir = doc/build all_files = 1 [upload_sphinx] upload-dir = doc/build/html [compile_catalog] directory = keystoneclient/locale domain = keystoneclient [update_catalog] domain = keystoneclient output_dir = keystoneclient/locale input_file = keystoneclient/locale/keystoneclient.pot [extract_messages] keywords = _ gettext ngettext l_ lazy_gettext mapping_file = babel.cfg output_file = keystoneclient/locale/keystoneclient.pot [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 python-keystoneclient-0.7.1/setup.py0000664000175400017540000000141512315030451020741 0ustar jenkinsjenkins00000000000000#!/usr/bin/env python # Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. # THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT import setuptools setuptools.setup( setup_requires=['pbr'], pbr=True) python-keystoneclient-0.7.1/tools/0000775000175400017540000000000012315030547020374 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/tools/install_venv.py0000664000175400017540000000455512315030451023455 0ustar jenkinsjenkins00000000000000# Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. # All Rights Reserved. # # Copyright 2010 OpenStack Foundation # Copyright 2013 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import sys import install_venv_common as install_venv # noqa def print_help(venv, root): help = """ Openstack development environment setup is complete. Openstack development uses virtualenv to track and manage Python dependencies while in development and testing. To activate the Openstack virtualenv for the extent of your current shell session you can run: $ source %s/bin/activate Or, if you prefer, you can run commands in the virtualenv on a case by case basis by running: $ %s/tools/with_venv.sh Also, make test will automatically use the virtualenv. """ print(help % (venv, root)) def main(argv): root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) if os.environ.get('tools_path'): root = os.environ['tools_path'] venv = os.path.join(root, '.venv') if os.environ.get('venv'): venv = os.environ['venv'] pip_requires = os.path.join(root, 'requirements.txt') test_requires = os.path.join(root, 'test-requirements.txt') py_version = "python%s.%s" % (sys.version_info[0], sys.version_info[1]) project = 'python-keystoneclient' install = install_venv.InstallVenv(root, venv, pip_requires, test_requires, py_version, project) options = install.parse_args(argv) install.check_python_version() install.check_dependencies() install.create_virtualenv(no_site_packages=options.no_site_packages) install.install_dependencies() print_help(venv, root) if __name__ == '__main__': main(sys.argv) python-keystoneclient-0.7.1/tools/with_venv.sh0000775000175400017540000000012412315030451022733 0ustar jenkinsjenkins00000000000000#!/bin/bash TOOLS=`dirname $0` VENV=$TOOLS/../.venv source $VENV/bin/activate && $@ python-keystoneclient-0.7.1/tools/install_venv_common.py0000664000175400017540000001350612315030451025021 0ustar jenkinsjenkins00000000000000# Copyright 2013 OpenStack Foundation # Copyright 2013 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """Provides methods needed by installation script for OpenStack development virtual environments. Since this script is used to bootstrap a virtualenv from the system's Python environment, it should be kept strictly compatible with Python 2.6. Synced in from openstack-common """ from __future__ import print_function import optparse import os import subprocess import sys class InstallVenv(object): def __init__(self, root, venv, requirements, test_requirements, py_version, project): self.root = root self.venv = venv self.requirements = requirements self.test_requirements = test_requirements self.py_version = py_version self.project = project def die(self, message, *args): print(message % args, file=sys.stderr) sys.exit(1) def check_python_version(self): if sys.version_info < (2, 6): self.die("Need Python Version >= 2.6") def run_command_with_code(self, cmd, redirect_output=True, check_exit_code=True): """Runs a command in an out-of-process shell. Returns the output of that command. Working directory is self.root. """ if redirect_output: stdout = subprocess.PIPE else: stdout = None proc = subprocess.Popen(cmd, cwd=self.root, stdout=stdout) output = proc.communicate()[0] if check_exit_code and proc.returncode != 0: self.die('Command "%s" failed.\n%s', ' '.join(cmd), output) return (output, proc.returncode) def run_command(self, cmd, redirect_output=True, check_exit_code=True): return self.run_command_with_code(cmd, redirect_output, check_exit_code)[0] def get_distro(self): if (os.path.exists('/etc/fedora-release') or os.path.exists('/etc/redhat-release')): return Fedora( self.root, self.venv, self.requirements, self.test_requirements, self.py_version, self.project) else: return Distro( self.root, self.venv, self.requirements, self.test_requirements, self.py_version, self.project) def check_dependencies(self): self.get_distro().install_virtualenv() def create_virtualenv(self, no_site_packages=True): """Creates the virtual environment and installs PIP. Creates the virtual environment and installs PIP only into the virtual environment. """ if not os.path.isdir(self.venv): print('Creating venv...', end=' ') if no_site_packages: self.run_command(['virtualenv', '-q', '--no-site-packages', self.venv]) else: self.run_command(['virtualenv', '-q', self.venv]) print('done.') else: print("venv already exists...") pass def pip_install(self, *args): self.run_command(['tools/with_venv.sh', 'pip', 'install', '--upgrade'] + list(args), redirect_output=False) def install_dependencies(self): print('Installing dependencies with pip (this can take a while)...') # First things first, make sure our venv has the latest pip and # setuptools and pbr self.pip_install('pip>=1.4') self.pip_install('setuptools') self.pip_install('pbr') self.pip_install('-r', self.requirements, '-r', self.test_requirements) def parse_args(self, argv): """Parses command-line arguments.""" parser = optparse.OptionParser() parser.add_option('-n', '--no-site-packages', action='store_true', help="Do not inherit packages from global Python " "install") return parser.parse_args(argv[1:])[0] class Distro(InstallVenv): def check_cmd(self, cmd): return bool(self.run_command(['which', cmd], check_exit_code=False).strip()) def install_virtualenv(self): if self.check_cmd('virtualenv'): return if self.check_cmd('easy_install'): print('Installing virtualenv via easy_install...', end=' ') if self.run_command(['easy_install', 'virtualenv']): print('Succeeded') return else: print('Failed') self.die('ERROR: virtualenv not found.\n\n%s development' ' requires virtualenv, please install it using your' ' favorite package management tool' % self.project) class Fedora(Distro): """This covers all Fedora-based distributions. Includes: Fedora, RHEL, CentOS, Scientific Linux """ def check_pkg(self, pkg): return self.run_command_with_code(['rpm', '-q', pkg], check_exit_code=False)[1] == 0 def install_virtualenv(self): if self.check_cmd('virtualenv'): return if not self.check_pkg('python-virtualenv'): self.die("Please install 'python-virtualenv'.") super(Fedora, self).install_virtualenv() python-keystoneclient-0.7.1/tools/keystone.bash_completion0000664000175400017540000000153112315030451025317 0ustar jenkinsjenkins00000000000000# bash completion for openstack keystone _keystone_opts="" # lazy init _keystone_flags="" # lazy init _keystone_opts_exp="" # lazy init _keystone() { local cur prev kbc COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" if [ "x$_keystone_opts" == "x" ] ; then kbc="`keystone bash-completion | sed -e "s/ -h / /"`" _keystone_opts="`echo "$kbc" | sed -e "s/--[a-z0-9_-]*//g" -e "s/[ ][ ]*/ /g"`" _keystone_flags="`echo " $kbc" | sed -e "s/ [^-][^-][a-z0-9_-]*//g" -e "s/[ ][ ]*/ /g"`" _keystone_opts_exp="`echo $_keystone_opts | sed -e "s/[ ]/|/g"`" fi if [[ " ${COMP_WORDS[@]} " =~ " "($_keystone_opts_exp)" " && "$prev" != "help" ]] ; then COMPREPLY=($(compgen -W "${_keystone_flags}" -- ${cur})) else COMPREPLY=($(compgen -W "${_keystone_opts}" -- ${cur})) fi return 0 } complete -F _keystone keystone python-keystoneclient-0.7.1/tools/debug_helper.sh0000775000175400017540000000057212315030451023356 0ustar jenkinsjenkins00000000000000#!/bin/bash TMP_DIR=`mktemp -d` || exit 1 trap "rm -rf $TMP_DIR" EXIT ALL_TESTS=$TMP_DIR/all_tests TESTS_TO_RUN=$TMP_DIR/ksc_to_run python -m testtools.run discover -t ./ ./keystoneclient/tests --list > $ALL_TESTS if [ "$1" ] then grep "$1" < $ALL_TESTS > $TESTS_TO_RUN else mv $ALL_TESTS $TESTS_TO_RUN fi python -m testtools.run discover --load-list $TESTS_TO_RUN python-keystoneclient-0.7.1/examples/0000775000175400017540000000000012315030547021052 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/examples/pki/0000775000175400017540000000000012315030547021635 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/examples/pki/certs/0000775000175400017540000000000012315030547022755 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/examples/pki/certs/signing_cert.pem0000664000175400017540000000245612315030450026133 0ustar jenkinsjenkins00000000000000-----BEGIN CERTIFICATE----- MIIDpTCCAo0CAREwDQYJKoZIhvcNAQEFBQAwgZ4xCjAIBgNVBAUTATUxCzAJBgNV BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRIwEAYDVQQK EwlPcGVuU3RhY2sxETAPBgNVBAsTCEtleXN0b25lMSUwIwYJKoZIhvcNAQkBFhZr ZXlzdG9uZUBvcGVuc3RhY2sub3JnMRQwEgYDVQQDEwtTZWxmIFNpZ25lZDAgFw0x MzA5MTMxNjI1NDNaGA8yMDcyMDMwNzE2MjU0M1owgY8xCzAJBgNVBAYTAlVTMQsw CQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRIwEAYDVQQKEwlPcGVuU3Rh Y2sxETAPBgNVBAsTCEtleXN0b25lMSUwIwYJKoZIhvcNAQkBFhZrZXlzdG9uZUBv cGVuc3RhY2sub3JnMREwDwYDVQQDEwhLZXlzdG9uZTCCASIwDQYJKoZIhvcNAQEB BQADggEPADCCAQoCggEBAMz5WsgsuX3rZUdLwQpZXN2Ro7LQ6jEZnreBqMztVObw BuC1WdiJsg6dVlC7PVdt+0gY1c8WFg1TKmsucxesQSyfGAPg+9T/hsRMb6y12uJx fp3Wgqqw0U1HsXvMiaJH87MaGnt043BxzF+R9fhAcDk6Cyj5cx9J0LvZJEOzN4J4 ZRyO6j/DZZItb3lK5W9xkuoT+mTdDZOQJnXyG818uiWfjdCkLjr1ruytRcBOo4na Y828voT/A7I95+YCgKgbjiUWhHeTaNmMEQiGy0nGYfteC+oSsHOlxZ3b12azzHPk 83Bh2ez0Ih9vcZoe9DqvlFOXfv9q8OsYc5Yo6gPTXEsCAwEAATANBgkqhkiG9w0B AQUFAAOCAQEAmaYE98kOQWu6DV84ZcZP/OdT8eeu3vdB247nRj+6+GYItN/Gzqt4 HVvz7c+FVTolCcAQQ+z3XGswI9fIJ78Hb0p9CgnLprc3L7Xtk60Im59Xlf3tcurn r/ZnSDcjRBXKiEDrSM0VrhAnc0GoSeb6aDWopec+1hWOWfBVAg9R8yJgU9sUgO3O 0gimGyrw8eubmNhckSQLJTunUTsrkcBjuSg63wAD9OqCiX6c2eoQr+0YBp2eV2/n aOiJXWNLbeueMKSYiJNyyvM/dlON7/56cdwDTzKzgD34TImouM5VKipUwCX1ovLu ITLzALzpqFFzc8ugV9pMgUKtDbZoPp9EEA== -----END CERTIFICATE----- python-keystoneclient-0.7.1/examples/pki/certs/cacert.pem0000664000175400017540000000255712315030450024723 0ustar jenkinsjenkins00000000000000-----BEGIN CERTIFICATE----- MIID1jCCAr6gAwIBAgIJAJOtRP2+wrM/MA0GCSqGSIb3DQEBBQUAMIGeMQowCAYD VQQFEwE1MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVN1bm55 dmFsZTESMBAGA1UEChMJT3BlblN0YWNrMREwDwYDVQQLEwhLZXlzdG9uZTElMCMG CSqGSIb3DQEJARYWa2V5c3RvbmVAb3BlbnN0YWNrLm9yZzEUMBIGA1UEAxMLU2Vs ZiBTaWduZWQwIBcNMTMwOTEzMTYyNTQyWhgPMjA3MjAzMDcxNjI1NDJaMIGeMQow CAYDVQQFEwE1MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVN1 bm55dmFsZTESMBAGA1UEChMJT3BlblN0YWNrMREwDwYDVQQLEwhLZXlzdG9uZTEl MCMGCSqGSIb3DQEJARYWa2V5c3RvbmVAb3BlbnN0YWNrLm9yZzEUMBIGA1UEAxML U2VsZiBTaWduZWQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCl8906 EaRpibQFcCBWfxzLi5x/XpZ9iL6UX92NrSJxcDbaGws7s+GtjgDy8UOEonesRWTe qQEZtHpC3/UHHOnsA8F6ha/pq9LioqT7RehCnZCLBJwh5Ct+lclpWs15SkjJD2LT Dkjox0eA9nOBx+XDlWyU/GAyqx5Wsvg/Kxr0iod9/4IcJdnSdUjq4v0Cxg/zNk08 XPJX+F0bUDhgdUf7JrAmmS5LA8wphRnbIgtVsf6VN9HrbqtHAJDxh8gEfuwdhEW1 df1fBtZ+6WMIF3IRSbIsZELFB6sqcyRj7HhMoWMkdEyPb2f8mq61MzTgE6lJGIyT RvEoFie7qtGADIofAgMBAAGjEzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcN AQEFBQADggEBAJRMdEwAdN+crqI9dBLYlbBbnQ8xr9mk+REMdz9+SKhDCNdVisWU iLEZvK/aozrsRsDi81JjS4Tz0wXo8zsPPoDnXgDYEicNPTKifbPKgHdDIGFOwBKn y2cF6fHEn8n3KIBrDCNY6rHcYGZ7lbq/8eF0GoYQboPiuYesvVpynPmIK5/Mmire EuuZALAe1IFqqFt+l6tiJU2JWUFjLkFARMOD14qFZm+SInl64toi08j6gdou+NMW 7GEMbVHwNTafM/TgFN5j0yP9SAnYubckLSyH6hwR+rM8dztP5769joxQfnc9O/Bn TBD9KFpeQv6VJWLAxiIKcQCRTTDJLZZ0MQI= -----END CERTIFICATE----- python-keystoneclient-0.7.1/examples/pki/certs/middleware.pem0000664000175400017540000000572612315030450025600 0ustar jenkinsjenkins00000000000000-----BEGIN CERTIFICATE----- MIIDpjCCAo4CARAwDQYJKoZIhvcNAQEFBQAwgZ4xCjAIBgNVBAUTATUxCzAJBgNV BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRIwEAYDVQQK EwlPcGVuU3RhY2sxETAPBgNVBAsTCEtleXN0b25lMSUwIwYJKoZIhvcNAQkBFhZr ZXlzdG9uZUBvcGVuc3RhY2sub3JnMRQwEgYDVQQDEwtTZWxmIFNpZ25lZDAgFw0x MzA5MTMxNjI1NDNaGA8yMDcyMDMwNzE2MjU0M1owgZAxCzAJBgNVBAYTAlVTMQsw CQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRIwEAYDVQQKEwlPcGVuU3Rh Y2sxETAPBgNVBAsTCEtleXN0b25lMSUwIwYJKoZIhvcNAQkBFhZrZXlzdG9uZUBv cGVuc3RhY2sub3JnMRIwEAYDVQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEB AQUAA4IBDwAwggEKAoIBAQDL06AaJROwHPgJ9tcySSBepzJ81jYars2sMvLjyuvd iIBbhWvbS/a9Tw3WgL8H6OALkHiOU/f0A6Rpv8dGDIDsxZQVjT/4SLaQUOeDM+9b fkKHpSd9G3CsdSSZgOH08n+MyZ7slPHfUHLYWso0SJD0vAi1gmGDlSM/mmhhHTpC DGo6Wbwqare6JNeTCGJTJYwrxtoMCh/W1ZrslPC5lFvlHD7KBBf6IU2A8Xh/dUa3 p5pmQeHPW8Em90DzIB1qH0DRXl3KANc24xYRR45pPCVkk6vFsy6P0JwwpnkszB+L cK6CEsJhLsOYvQFsiQfSZ8m7YGhgrMLxtop4YEPirGGrAgMBAAEwDQYJKoZIhvcN AQEFBQADggEBAAjU7YomUx/U56p1KWHvr1B7oczHF8fPHYbuk5c/N81WOJeSRy+P 5ZGZ2UPjvqqXByv+78YWMKGY1BZ/2doeWuydr0sdSxEwmIUBYxFpujuYY+0AjS/n mMr1ZijK7TJssteKM7/MClzghUhPweDZrAg3ff1hbhK5QSy+9UPxUqLH44tfYSVC /BzM6se0p5ToM0bwdsa8TofaBRE1L1IW/Hg4VIGOoKs0R0uLm7+Oot2me2cEuZ6h Wls6MED8ND1Nz8EAKwndkeDu2iMM+qx/YFp6K8BQ5E5nXd2rbUZUlQMp1WbUlZ87 KvC98aT0UYIq6uo1Lx/dQvJs7faAkYd4lmE= -----END CERTIFICATE----- -----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDL06AaJROwHPgJ 9tcySSBepzJ81jYars2sMvLjyuvdiIBbhWvbS/a9Tw3WgL8H6OALkHiOU/f0A6Rp v8dGDIDsxZQVjT/4SLaQUOeDM+9bfkKHpSd9G3CsdSSZgOH08n+MyZ7slPHfUHLY Wso0SJD0vAi1gmGDlSM/mmhhHTpCDGo6Wbwqare6JNeTCGJTJYwrxtoMCh/W1Zrs lPC5lFvlHD7KBBf6IU2A8Xh/dUa3p5pmQeHPW8Em90DzIB1qH0DRXl3KANc24xYR R45pPCVkk6vFsy6P0JwwpnkszB+LcK6CEsJhLsOYvQFsiQfSZ8m7YGhgrMLxtop4 YEPirGGrAgMBAAECggEATwvbY0hNwlb5uqOIAXBqpUqiQdexU9fG26lGmSDxKBDv 9o5frcRgBDrMWwvDCgY+HT4CAvB9kJx4/qnpVjkzJp/ZNiJ5VIiehIlbv348rXbh xkk+bz5dDATCFOXuu1fwL2FhyM5anwhMAav0DyK1VLQ3jGzr9GO6L8hqAn+bQFFu 6ngiODwfhBMl5aRoL9UOBEhccK07znrH0JGRz+3+5Cdz59Xw91Bv210LhNNDL58+ 0JD0N+YztVOQd2bgwo0bQbOEijzmYq+0mjoqAnJh1/++y7PlIPs0AnPgqSnFPx9+ 6FsQEVRgk5Uq3kvPLaP4nT2y6MDZSp+ujYldvJhyQQKBgQDuX2pZIJMZ4aFnkG+K TmJ5wsLa/u9an0TmvAL9RLtBpVpQNKD8cQ+y8PUZavXDbAIt5NWqZVnTbCR79Dnd mZKblwcHhtsyA5f89el5KcxY2BREWdHdTnJpNd7XRlUECmzvX1zGj77lA982PhII yflRBRV3vqLkgC8vfoYgRyRElwKBgQDa5jnLdx/RahfYMOgn1HE5o4hMzLR4Y0Dd +gELshcUbPqouoP5zOb8WOagVJIgZVOSN+/VqbilVYrqRiNTn2rnoxs+HHRdaJNN 3eXllD4J2HfC2BIj1xSpIdyh2XewAJqw9IToHNB29QUhxOtgwseHciPG6JaKH2ik kqGKH/EKDQKBgFFAftygiOPCkCTgC9UmANUmOQsy6N2H+pF3tsEj43xt44oBVnqW A1boYXNnjRwuvdNs9BPf9i1l6E3EItFRXrLgWQoMwryakv0ryYh+YeRKyyW9RBbe fYs1TJ8unx4Ae79gTxxztQsVNcmkgLs0NWKTjAzEE3w14V+cDhYEie1DAoGBAJdI V5cLrBzBstsB6eBlDR9lqrRRIUS2a8U9m+1mVlcSfiWQSdehSd4K3tDdwePLw3ch W4qR8n+pYAlLEe0gFvUhn5lMdwt7U5qUCeehjUKmrRYm2FqWsbu2IFJnBjXIJSC4 zQXRrC0aZ0KQYpAL7XPpaVp1slyhGmPqxuO78Y0dAoGBAMHo3EIMwu9rfuGwFodr GFsOZhfJqgo5GDNxxf89Q9WWpMDTCdX+wdBTrN/wsMbBuwIDHrUuRnk6D5CWRjSk /ikCgHN3kOtrbL8zzqRomGAIIWKYGFEIGe1GHVGo5r//HXHdPxFXygvruQ/xbOA4 RGvmDiji8vVDq7Shho8I6KuT -----END PRIVATE KEY----- python-keystoneclient-0.7.1/examples/pki/certs/ssl_cert.pem0000664000175400017540000000245612315030450025276 0ustar jenkinsjenkins00000000000000-----BEGIN CERTIFICATE----- MIIDpjCCAo4CARAwDQYJKoZIhvcNAQEFBQAwgZ4xCjAIBgNVBAUTATUxCzAJBgNV BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRIwEAYDVQQK EwlPcGVuU3RhY2sxETAPBgNVBAsTCEtleXN0b25lMSUwIwYJKoZIhvcNAQkBFhZr ZXlzdG9uZUBvcGVuc3RhY2sub3JnMRQwEgYDVQQDEwtTZWxmIFNpZ25lZDAgFw0x MzA5MTMxNjI1NDNaGA8yMDcyMDMwNzE2MjU0M1owgZAxCzAJBgNVBAYTAlVTMQsw CQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRIwEAYDVQQKEwlPcGVuU3Rh Y2sxETAPBgNVBAsTCEtleXN0b25lMSUwIwYJKoZIhvcNAQkBFhZrZXlzdG9uZUBv cGVuc3RhY2sub3JnMRIwEAYDVQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEB AQUAA4IBDwAwggEKAoIBAQDL06AaJROwHPgJ9tcySSBepzJ81jYars2sMvLjyuvd iIBbhWvbS/a9Tw3WgL8H6OALkHiOU/f0A6Rpv8dGDIDsxZQVjT/4SLaQUOeDM+9b fkKHpSd9G3CsdSSZgOH08n+MyZ7slPHfUHLYWso0SJD0vAi1gmGDlSM/mmhhHTpC DGo6Wbwqare6JNeTCGJTJYwrxtoMCh/W1ZrslPC5lFvlHD7KBBf6IU2A8Xh/dUa3 p5pmQeHPW8Em90DzIB1qH0DRXl3KANc24xYRR45pPCVkk6vFsy6P0JwwpnkszB+L cK6CEsJhLsOYvQFsiQfSZ8m7YGhgrMLxtop4YEPirGGrAgMBAAEwDQYJKoZIhvcN AQEFBQADggEBAAjU7YomUx/U56p1KWHvr1B7oczHF8fPHYbuk5c/N81WOJeSRy+P 5ZGZ2UPjvqqXByv+78YWMKGY1BZ/2doeWuydr0sdSxEwmIUBYxFpujuYY+0AjS/n mMr1ZijK7TJssteKM7/MClzghUhPweDZrAg3ff1hbhK5QSy+9UPxUqLH44tfYSVC /BzM6se0p5ToM0bwdsa8TofaBRE1L1IW/Hg4VIGOoKs0R0uLm7+Oot2me2cEuZ6h Wls6MED8ND1Nz8EAKwndkeDu2iMM+qx/YFp6K8BQ5E5nXd2rbUZUlQMp1WbUlZ87 KvC98aT0UYIq6uo1Lx/dQvJs7faAkYd4lmE= -----END CERTIFICATE----- python-keystoneclient-0.7.1/examples/pki/cms/0000775000175400017540000000000012315030547022417 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/examples/pki/cms/auth_token_revoked.json0000664000175400017540000000272012315030450027164 0ustar jenkinsjenkins00000000000000{"access": {"serviceCatalog": [{"endpoints": [{"adminURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a", "region": "regionOne", "internalURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a", "publicURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a"}], "endpoints_links": [], "type": "volume", "name": "volume"}, {"endpoints": [{"adminURL": "http://127.0.0.1:9292/v1", "region": "regionOne", "internalURL": "http://127.0.0.1:9292/v1", "publicURL": "http://127.0.0.1:9292/v1"}], "endpoints_links": [], "type": "image", "name": "glance"}, {"endpoints": [{"adminURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a", "region": "regionOne", "internalURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a", "publicURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a"}], "endpoints_links": [], "type": "compute", "name": "nova"}, {"endpoints": [{"adminURL": "http://127.0.0.1:35357/v2.0", "region": "RegionOne", "internalURL": "http://127.0.0.1:35357/v2.0", "publicURL": "http://127.0.0.1:5000/v2.0"}], "endpoints_links": [], "type": "identity", "name": "keystone"}],"token": {"expires": "2038-01-18T21:14:07Z", "id": "placeholder", "tenant": {"enabled": true, "description": null, "name": "tenant_name1", "id": "tenant_id1"}}, "user": {"username": "revoked_username1", "roles_links": ["role1","role2"], "id": "revoked_user_id1", "roles": [{"name": "role1"}, {"name": "role2"}], "name": "revoked_username1"}}} python-keystoneclient-0.7.1/examples/pki/cms/auth_token_scoped_expired.pem0000664000175400017540000000525412315030450030337 0ustar jenkinsjenkins00000000000000-----BEGIN CMS----- MIIHwQYJKoZIhvcNAQcCoIIHsjCCB64CAQExCTAHBgUrDgMCGjCCBc4GCSqGSIb3 DQEHAaCCBb8EggW7eyJhY2Nlc3MiOiB7InNlcnZpY2VDYXRhbG9nIjogW3siZW5k cG9pbnRzIjogW3siYWRtaW5VUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo4Nzc2L3Yx LzY0YjZmM2ZiY2M1MzQzNWU4YTYwZmNmODliYjY2MTdhIiwgInJlZ2lvbiI6ICJy ZWdpb25PbmUiLCAiaW50ZXJuYWxVUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo4Nzc2 L3YxLzY0YjZmM2ZiY2M1MzQzNWU4YTYwZmNmODliYjY2MTdhIiwgInB1YmxpY1VS TCI6ICJodHRwOi8vMTI3LjAuMC4xOjg3NzYvdjEvNjRiNmYzZmJjYzUzNDM1ZThh NjBmY2Y4OWJiNjYxN2EifV0sICJlbmRwb2ludHNfbGlua3MiOiBbXSwgInR5cGUi OiAidm9sdW1lIiwgIm5hbWUiOiAidm9sdW1lIn0sIHsiZW5kcG9pbnRzIjogW3si YWRtaW5VUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo5MjkyL3YxIiwgInJlZ2lvbiI6 ICJyZWdpb25PbmUiLCAiaW50ZXJuYWxVUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo5 MjkyL3YxIiwgInB1YmxpY1VSTCI6ICJodHRwOi8vMTI3LjAuMC4xOjkyOTIvdjEi fV0sICJlbmRwb2ludHNfbGlua3MiOiBbXSwgInR5cGUiOiAiaW1hZ2UiLCAibmFt ZSI6ICJnbGFuY2UifSwgeyJlbmRwb2ludHMiOiBbeyJhZG1pblVSTCI6ICJodHRw Oi8vMTI3LjAuMC4xOjg3NzQvdjEuMS82NGI2ZjNmYmNjNTM0MzVlOGE2MGZjZjg5 YmI2NjE3YSIsICJyZWdpb24iOiAicmVnaW9uT25lIiwgImludGVybmFsVVJMIjog Imh0dHA6Ly8xMjcuMC4wLjE6ODc3NC92MS4xLzY0YjZmM2ZiY2M1MzQzNWU4YTYw ZmNmODliYjY2MTdhIiwgInB1YmxpY1VSTCI6ICJodHRwOi8vMTI3LjAuMC4xOjg3 NzQvdjEuMS82NGI2ZjNmYmNjNTM0MzVlOGE2MGZjZjg5YmI2NjE3YSJ9XSwgImVu ZHBvaW50c19saW5rcyI6IFtdLCAidHlwZSI6ICJjb21wdXRlIiwgIm5hbWUiOiAi bm92YSJ9LCB7ImVuZHBvaW50cyI6IFt7ImFkbWluVVJMIjogImh0dHA6Ly8xMjcu MC4wLjE6MzUzNTcvdjIuMCIsICJyZWdpb24iOiAiUmVnaW9uT25lIiwgImludGVy bmFsVVJMIjogImh0dHA6Ly8xMjcuMC4wLjE6MzUzNTcvdjIuMCIsICJwdWJsaWNV UkwiOiAiaHR0cDovLzEyNy4wLjAuMTo1MDAwL3YyLjAifV0sICJlbmRwb2ludHNf bGlua3MiOiBbXSwgInR5cGUiOiAiaWRlbnRpdHkiLCAibmFtZSI6ICJrZXlzdG9u ZSJ9XSwidG9rZW4iOiB7ImV4cGlyZXMiOiAiMjAxMC0wNi0wMlQxNDo0NzozNFoi LCAiaWQiOiAicGxhY2Vob2xkZXIiLCAidGVuYW50IjogeyJlbmFibGVkIjogdHJ1 ZSwgImRlc2NyaXB0aW9uIjogbnVsbCwgIm5hbWUiOiAidGVuYW50X25hbWUxIiwg ImlkIjogInRlbmFudF9pZDEifX0sICJ1c2VyIjogeyJ1c2VybmFtZSI6ICJ1c2Vy X25hbWUxIiwgInJvbGVzX2xpbmtzIjogWyJyb2xlMSIsInJvbGUyIl0sICJpZCI6 ICJ1c2VyX2lkMSIsICJyb2xlcyI6IFt7Im5hbWUiOiAicm9sZTEifSwgeyJuYW1l IjogInJvbGUyIn1dLCAibmFtZSI6ICJ1c2VyX25hbWUxIn19fQ0KMYIByjCCAcYC AQEwgaQwgZ4xCjAIBgNVBAUTATUxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTES MBAGA1UEBxMJU3Vubnl2YWxlMRIwEAYDVQQKEwlPcGVuU3RhY2sxETAPBgNVBAsT CEtleXN0b25lMSUwIwYJKoZIhvcNAQkBFhZrZXlzdG9uZUBvcGVuc3RhY2sub3Jn MRQwEgYDVQQDEwtTZWxmIFNpZ25lZAIBETAHBgUrDgMCGjANBgkqhkiG9w0BAQEF AASCAQAWJo+0pfhjJjmZ9fE4z53iJyWuOU4wkqb8aiTXnWDTSZPyF3iixLCJD+ZB P82scsieQyqZ+7qgzDGaEQhMOwEz8xGactXIDk1SObKbzggqC+7tLnw9dSzGe5g6 vrmcCJc2HsXNf5AjbKXuTmH7p7DpthXUKDiz0kIgbDZ1PuZ3NRz/A0zk6zFhkQ6P UaE4wrfQ9p1QpKhepOs/aKQfMfzFtIhkqnegg6d6lss73eOzra1Vysd6CrTzOXyK ldn5RkaClnJHogcFW6lwxJS9SBv+qSo4iqU08U+A5pd/AazlvxdX8QiDfNoTqV2T c/HF1FT8GOwnsHanVKcENL0bRumc -----END CMS----- python-keystoneclient-0.7.1/examples/pki/cms/auth_v3_token_revoked.json0000664000175400017540000000310412315030450027571 0ustar jenkinsjenkins00000000000000{"token": {"catalog": [{"endpoints": [{"adminURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a", "region": "regionOne", "internalURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a", "publicURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a"}], "endpoints_links": [], "type": "volume", "name": "volume"}, {"endpoints": [{"adminURL": "http://127.0.0.1:9292/v1", "region": "regionOne", "internalURL": "http://127.0.0.1:9292/v1", "publicURL": "http://127.0.0.1:9292/v1"}], "endpoints_links": [], "type": "image", "name": "glance"}, {"endpoints": [{"adminURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a", "region": "regionOne", "internalURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a", "publicURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a"}], "endpoints_links": [], "type": "compute", "name": "nova"}, {"endpoints": [{"adminURL": "http://127.0.0.1:35357/v3", "region": "RegionOne", "internalURL": "http://127.0.0.1:35357/v3", "publicURL": "http://127.0.0.1:5000/v3"}], "endpoints_links": [], "type": "identity", "name": "keystone"}], "expires_at": "2038-01-18T21:14:07Z", "project": {"enabled": true, "description": null, "name": "tenant_name1", "id": "tenant_id1", "domain": {"id": "domain_id1", "name": "domain_name1"}}, "user": {"name": "revoked_username1", "id": "revoked_user_id1", "domain": {"id": "domain_id1", "name": "domain_name1"}}, "roles": [{"name": "role1"}, {"name": "role2"}], "methods": ["password"], } } python-keystoneclient-0.7.1/examples/pki/cms/revocation_list.pem0000664000175400017540000000153312315030450026321 0ustar jenkinsjenkins00000000000000-----BEGIN CMS----- MIICWgYJKoZIhvcNAQcCoIICSzCCAkcCAQExCTAHBgUrDgMCGjBpBgkqhkiG9w0B BwGgXARaeyJyZXZva2VkIjpbeyJpZCI6IjdhY2ZjZmRhZjZhMTRhZWJlOTdjNjFj NTk0N2JjNGQzIiwiZXhwaXJlcyI6IjIwMTItMDgtMTRUMTc6NTg6NDhaIn1dfQ0K MYIByjCCAcYCAQEwgaQwgZ4xCjAIBgNVBAUTATUxCzAJBgNVBAYTAlVTMQswCQYD VQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRIwEAYDVQQKEwlPcGVuU3RhY2sx ETAPBgNVBAsTCEtleXN0b25lMSUwIwYJKoZIhvcNAQkBFhZrZXlzdG9uZUBvcGVu c3RhY2sub3JnMRQwEgYDVQQDEwtTZWxmIFNpZ25lZAIBETAHBgUrDgMCGjANBgkq hkiG9w0BAQEFAASCAQB8CHeQzIDFNy4RcWb+RRwwZ3JZ8Vw7OnFZhWXdbQ7a5KZr CrB/cGpkvMgnOtCNfpwSv7H97UEmOhQEarFJ6YFNNjA2FdXgt+2MCPmodG6tGyXl F+sh/g7qE8ONTiIJn2634kCj7fAyrVUlRxDLu6Llk9KWv7pAxtJKjqirHr8i8tYM /QV8pX/QP0u+vJvzZBXb/UlViQpaJnY9SWKF0ETXGypFTtExfiXG9qz96JWvC/jK gOF68N0FsoCNhzRys86rrAk7jZugajzjk3y/buInZiv4npQcEMC3lHKBXtA2uiRf ILgnF0qOttONx8GNE72eJqWBkiJf52239PxgxWNg -----END CMS----- python-keystoneclient-0.7.1/examples/pki/cms/revocation_list.json0000664000175400017540000000013112315030450026502 0ustar jenkinsjenkins00000000000000{"revoked":[{"id":"7acfcfdaf6a14aebe97c61c5947bc4d3","expires":"2012-08-14T17:58:48Z"}]} python-keystoneclient-0.7.1/examples/pki/cms/auth_token_scoped.pem0000664000175400017540000000525412315030450026617 0ustar jenkinsjenkins00000000000000-----BEGIN CMS----- MIIHwQYJKoZIhvcNAQcCoIIHsjCCB64CAQExCTAHBgUrDgMCGjCCBc4GCSqGSIb3 DQEHAaCCBb8EggW7eyJhY2Nlc3MiOiB7InNlcnZpY2VDYXRhbG9nIjogW3siZW5k cG9pbnRzIjogW3siYWRtaW5VUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo4Nzc2L3Yx LzY0YjZmM2ZiY2M1MzQzNWU4YTYwZmNmODliYjY2MTdhIiwgInJlZ2lvbiI6ICJy ZWdpb25PbmUiLCAiaW50ZXJuYWxVUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo4Nzc2 L3YxLzY0YjZmM2ZiY2M1MzQzNWU4YTYwZmNmODliYjY2MTdhIiwgInB1YmxpY1VS TCI6ICJodHRwOi8vMTI3LjAuMC4xOjg3NzYvdjEvNjRiNmYzZmJjYzUzNDM1ZThh NjBmY2Y4OWJiNjYxN2EifV0sICJlbmRwb2ludHNfbGlua3MiOiBbXSwgInR5cGUi OiAidm9sdW1lIiwgIm5hbWUiOiAidm9sdW1lIn0sIHsiZW5kcG9pbnRzIjogW3si YWRtaW5VUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo5MjkyL3YxIiwgInJlZ2lvbiI6 ICJyZWdpb25PbmUiLCAiaW50ZXJuYWxVUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo5 MjkyL3YxIiwgInB1YmxpY1VSTCI6ICJodHRwOi8vMTI3LjAuMC4xOjkyOTIvdjEi fV0sICJlbmRwb2ludHNfbGlua3MiOiBbXSwgInR5cGUiOiAiaW1hZ2UiLCAibmFt ZSI6ICJnbGFuY2UifSwgeyJlbmRwb2ludHMiOiBbeyJhZG1pblVSTCI6ICJodHRw Oi8vMTI3LjAuMC4xOjg3NzQvdjEuMS82NGI2ZjNmYmNjNTM0MzVlOGE2MGZjZjg5 YmI2NjE3YSIsICJyZWdpb24iOiAicmVnaW9uT25lIiwgImludGVybmFsVVJMIjog Imh0dHA6Ly8xMjcuMC4wLjE6ODc3NC92MS4xLzY0YjZmM2ZiY2M1MzQzNWU4YTYw ZmNmODliYjY2MTdhIiwgInB1YmxpY1VSTCI6ICJodHRwOi8vMTI3LjAuMC4xOjg3 NzQvdjEuMS82NGI2ZjNmYmNjNTM0MzVlOGE2MGZjZjg5YmI2NjE3YSJ9XSwgImVu ZHBvaW50c19saW5rcyI6IFtdLCAidHlwZSI6ICJjb21wdXRlIiwgIm5hbWUiOiAi bm92YSJ9LCB7ImVuZHBvaW50cyI6IFt7ImFkbWluVVJMIjogImh0dHA6Ly8xMjcu MC4wLjE6MzUzNTcvdjIuMCIsICJyZWdpb24iOiAiUmVnaW9uT25lIiwgImludGVy bmFsVVJMIjogImh0dHA6Ly8xMjcuMC4wLjE6MzUzNTcvdjIuMCIsICJwdWJsaWNV UkwiOiAiaHR0cDovLzEyNy4wLjAuMTo1MDAwL3YyLjAifV0sICJlbmRwb2ludHNf bGlua3MiOiBbXSwgInR5cGUiOiAiaWRlbnRpdHkiLCAibmFtZSI6ICJrZXlzdG9u ZSJ9XSwidG9rZW4iOiB7ImV4cGlyZXMiOiAiMjAzOC0wMS0xOFQyMToxNDowN1oi LCAiaWQiOiAicGxhY2Vob2xkZXIiLCAidGVuYW50IjogeyJlbmFibGVkIjogdHJ1 ZSwgImRlc2NyaXB0aW9uIjogbnVsbCwgIm5hbWUiOiAidGVuYW50X25hbWUxIiwg ImlkIjogInRlbmFudF9pZDEifX0sICJ1c2VyIjogeyJ1c2VybmFtZSI6ICJ1c2Vy X25hbWUxIiwgInJvbGVzX2xpbmtzIjogWyJyb2xlMSIsInJvbGUyIl0sICJpZCI6 ICJ1c2VyX2lkMSIsICJyb2xlcyI6IFt7Im5hbWUiOiAicm9sZTEifSwgeyJuYW1l IjogInJvbGUyIn1dLCAibmFtZSI6ICJ1c2VyX25hbWUxIn19fQ0KMYIByjCCAcYC AQEwgaQwgZ4xCjAIBgNVBAUTATUxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTES MBAGA1UEBxMJU3Vubnl2YWxlMRIwEAYDVQQKEwlPcGVuU3RhY2sxETAPBgNVBAsT CEtleXN0b25lMSUwIwYJKoZIhvcNAQkBFhZrZXlzdG9uZUBvcGVuc3RhY2sub3Jn MRQwEgYDVQQDEwtTZWxmIFNpZ25lZAIBETAHBgUrDgMCGjANBgkqhkiG9w0BAQEF AASCAQAMxN2Yj0hy7M5Pt1jjsDaonf6Z/LxrmdenVgobbJ1CiVtJYfoXyA7xhOlq fs0vvnftjbEKl2X2GsXKyDKGwLtFzD1LrSKqcUFmAQqwEf5b9MwbA2156z40y/QO BbXppeDfC/2ym44Iptc9wjK3ywo0ycosk+aTR6oJFp0QLooXtkjQ+tTjhaw/q/aP m5h2mXDVYdfKMZultYDZFg/y2X9NMvz8A77llTqgPoCdzSyz2Z3TPsSs+lgwfAqI TkLMwtmKBZqparZe5PG/mVhRfk6Gfe69k9cwW+V4Qy0S6JqqBB96tBKHuawZpL77 7DjtHl33UOYXEHK/pudNqp7W1kI6 -----END CMS----- python-keystoneclient-0.7.1/examples/pki/cms/auth_token_unscoped.pem0000664000175400017540000000216312315030450027156 0ustar jenkinsjenkins00000000000000-----BEGIN CMS----- MIIDKAYJKoZIhvcNAQcCoIIDGTCCAxUCAQExCTAHBgUrDgMCGjCCATUGCSqGSIb3 DQEHAaCCASYEggEieyJhY2Nlc3MiOiB7InRva2VuIjogeyJleHBpcmVzIjogIjIx MTItMDgtMTdUMTU6MzU6MzRaIiwgImlkIjogIjAxZTAzMmM5OTZlZjQ0MDZiMTQ0 MzM1OTE1YTQxZTc5In0sICJzZXJ2aWNlQ2F0YWxvZyI6IHt9LCAidXNlciI6IHsi dXNlcm5hbWUiOiAidXNlcl9uYW1lMSIsICJyb2xlc19saW5rcyI6IFtdLCAiaWQi OiAiYzljODllM2JlM2VlNDUzZmJmMDBjNzk2NmY2ZDNmYmQiLCAicm9sZXMiOiBb eyduYW1lJzogJ3JvbGUxJ30seyduYW1lJzogJ3JvbGUyJ30sXSwgIm5hbWUiOiAi dXNlcl9uYW1lMSJ9fX0xggHKMIIBxgIBATCBpDCBnjEKMAgGA1UEBRMBNTELMAkG A1UEBhMCVVMxCzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlTdW5ueXZhbGUxEjAQBgNV BAoTCU9wZW5TdGFjazERMA8GA1UECxMIS2V5c3RvbmUxJTAjBgkqhkiG9w0BCQEW FmtleXN0b25lQG9wZW5zdGFjay5vcmcxFDASBgNVBAMTC1NlbGYgU2lnbmVkAgER MAcGBSsOAwIaMA0GCSqGSIb3DQEBAQUABIIBAFq4JvODBIaoHiYG6KMCnBEhDjWS CuW0gq3kbi3j8kOzb4Mr7Muq0XvGMRwDrZlkfSpzIyuri/Fzf2pW58hnjWfDHQ1S laAWLs6csh2u80hgWpMngCN5ZVFtIIbWlE0ZuLZh8p7E0IJZnNvYmlOVrmIkRo+J 1vMr71HZr5/kFcJzFVgi8QI4XU5iBPsUWOdJJV+0jXkMHVqOX3H297CYCePaotLD azuquE74N8KMyl8j8jE9wi9O1gVBqO4L66ePjt5zI/TrjbjKwdseqoZR1dDGlp5V awRwRYCjsKF+asAbuASOwdSgP8V6VgTOUrZh2D8KHtclwS+URoTdVl4ypQA= -----END CMS----- python-keystoneclient-0.7.1/examples/pki/cms/auth_token_revoked.pem0000664000175400017540000000531412315030450026776 0ustar jenkinsjenkins00000000000000-----BEGIN CMS----- MIIH1wYJKoZIhvcNAQcCoIIHyDCCB8QCAQExCTAHBgUrDgMCGjCCBeQGCSqGSIb3 DQEHAaCCBdUEggXReyJhY2Nlc3MiOiB7InNlcnZpY2VDYXRhbG9nIjogW3siZW5k cG9pbnRzIjogW3siYWRtaW5VUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo4Nzc2L3Yx LzY0YjZmM2ZiY2M1MzQzNWU4YTYwZmNmODliYjY2MTdhIiwgInJlZ2lvbiI6ICJy ZWdpb25PbmUiLCAiaW50ZXJuYWxVUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo4Nzc2 L3YxLzY0YjZmM2ZiY2M1MzQzNWU4YTYwZmNmODliYjY2MTdhIiwgInB1YmxpY1VS TCI6ICJodHRwOi8vMTI3LjAuMC4xOjg3NzYvdjEvNjRiNmYzZmJjYzUzNDM1ZThh NjBmY2Y4OWJiNjYxN2EifV0sICJlbmRwb2ludHNfbGlua3MiOiBbXSwgInR5cGUi OiAidm9sdW1lIiwgIm5hbWUiOiAidm9sdW1lIn0sIHsiZW5kcG9pbnRzIjogW3si YWRtaW5VUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo5MjkyL3YxIiwgInJlZ2lvbiI6 ICJyZWdpb25PbmUiLCAiaW50ZXJuYWxVUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo5 MjkyL3YxIiwgInB1YmxpY1VSTCI6ICJodHRwOi8vMTI3LjAuMC4xOjkyOTIvdjEi fV0sICJlbmRwb2ludHNfbGlua3MiOiBbXSwgInR5cGUiOiAiaW1hZ2UiLCAibmFt ZSI6ICJnbGFuY2UifSwgeyJlbmRwb2ludHMiOiBbeyJhZG1pblVSTCI6ICJodHRw Oi8vMTI3LjAuMC4xOjg3NzQvdjEuMS82NGI2ZjNmYmNjNTM0MzVlOGE2MGZjZjg5 YmI2NjE3YSIsICJyZWdpb24iOiAicmVnaW9uT25lIiwgImludGVybmFsVVJMIjog Imh0dHA6Ly8xMjcuMC4wLjE6ODc3NC92MS4xLzY0YjZmM2ZiY2M1MzQzNWU4YTYw ZmNmODliYjY2MTdhIiwgInB1YmxpY1VSTCI6ICJodHRwOi8vMTI3LjAuMC4xOjg3 NzQvdjEuMS82NGI2ZjNmYmNjNTM0MzVlOGE2MGZjZjg5YmI2NjE3YSJ9XSwgImVu ZHBvaW50c19saW5rcyI6IFtdLCAidHlwZSI6ICJjb21wdXRlIiwgIm5hbWUiOiAi bm92YSJ9LCB7ImVuZHBvaW50cyI6IFt7ImFkbWluVVJMIjogImh0dHA6Ly8xMjcu MC4wLjE6MzUzNTcvdjIuMCIsICJyZWdpb24iOiAiUmVnaW9uT25lIiwgImludGVy bmFsVVJMIjogImh0dHA6Ly8xMjcuMC4wLjE6MzUzNTcvdjIuMCIsICJwdWJsaWNV UkwiOiAiaHR0cDovLzEyNy4wLjAuMTo1MDAwL3YyLjAifV0sICJlbmRwb2ludHNf bGlua3MiOiBbXSwgInR5cGUiOiAiaWRlbnRpdHkiLCAibmFtZSI6ICJrZXlzdG9u ZSJ9XSwidG9rZW4iOiB7ImV4cGlyZXMiOiAiMjAzOC0wMS0xOFQyMToxNDowN1oi LCAiaWQiOiAicGxhY2Vob2xkZXIiLCAidGVuYW50IjogeyJlbmFibGVkIjogdHJ1 ZSwgImRlc2NyaXB0aW9uIjogbnVsbCwgIm5hbWUiOiAidGVuYW50X25hbWUxIiwg ImlkIjogInRlbmFudF9pZDEifX0sICJ1c2VyIjogeyJ1c2VybmFtZSI6ICJyZXZv a2VkX3VzZXJuYW1lMSIsICJyb2xlc19saW5rcyI6IFsicm9sZTEiLCJyb2xlMiJd LCAiaWQiOiAicmV2b2tlZF91c2VyX2lkMSIsICJyb2xlcyI6IFt7Im5hbWUiOiAi cm9sZTEifSwgeyJuYW1lIjogInJvbGUyIn1dLCAibmFtZSI6ICJyZXZva2VkX3Vz ZXJuYW1lMSJ9fX0NCjGCAcowggHGAgEBMIGkMIGeMQowCAYDVQQFEwE1MQswCQYD VQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVN1bm55dmFsZTESMBAGA1UE ChMJT3BlblN0YWNrMREwDwYDVQQLEwhLZXlzdG9uZTElMCMGCSqGSIb3DQEJARYW a2V5c3RvbmVAb3BlbnN0YWNrLm9yZzEUMBIGA1UEAxMLU2VsZiBTaWduZWQCAREw BwYFKw4DAhowDQYJKoZIhvcNAQEBBQAEggEAY4y1smThXZF8g3jkJESg+xJSUriO +FCN249mG1NMLlyC8AMSUPW3Hfeng16jsBUKTcrCsNyE0vMeuEVq1a+ntCacEOr/ NQVqKgflZ8x6JWOqK4AU0oXjyJN+XleGmjxYN+B8MS3j7OvTc7RkfYw0kr85tYz4 BFvVaOL/mxEIjTndDQnPdQypfExfzg5bgqMGTn6AXa7or77FkGQ5fMEjjcQykvUB CmW8rZC0jM/SamIcGNSFuVlvAvdE+j16WEVM8fcvJ1u2klmKdTYwSOCApevko36z SxMyPmYktCwTd1xzSkPnPKOqCpuMYzt/a3OoCsrF+u+sGgrWEARDM1gIkQ== -----END CMS----- python-keystoneclient-0.7.1/examples/pki/cms/auth_v3_token_scoped.pem0000664000175400017540000000545212315030450027227 0ustar jenkinsjenkins00000000000000-----BEGIN CMS----- MIIIHgYJKoZIhvcNAQcCoIIIDzCCCAsCAQExCTAHBgUrDgMCGjCCBisGCSqGSIb3 DQEHAaCCBhwEggYYeyJ0b2tlbiI6DQoJeyJjYXRhbG9nIjogW3siZW5kcG9pbnRz IjogW3siYWRtaW5VUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo4Nzc2L3YxLzY0YjZm M2ZiY2M1MzQzNWU4YTYwZmNmODliYjY2MTdhIiwgInJlZ2lvbiI6ICJyZWdpb25P bmUiLCAiaW50ZXJuYWxVUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo4Nzc2L3YxLzY0 YjZmM2ZiY2M1MzQzNWU4YTYwZmNmODliYjY2MTdhIiwgInB1YmxpY1VSTCI6ICJo dHRwOi8vMTI3LjAuMC4xOjg3NzYvdjEvNjRiNmYzZmJjYzUzNDM1ZThhNjBmY2Y4 OWJiNjYxN2EifV0sICJlbmRwb2ludHNfbGlua3MiOiBbXSwgInR5cGUiOiAidm9s dW1lIiwgIm5hbWUiOiAidm9sdW1lIn0sDQoJCQkgICAgIHsiZW5kcG9pbnRzIjog W3siYWRtaW5VUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo5MjkyL3YxIiwgInJlZ2lv biI6ICJyZWdpb25PbmUiLCAiaW50ZXJuYWxVUkwiOiAiaHR0cDovLzEyNy4wLjAu MTo5MjkyL3YxIiwgInB1YmxpY1VSTCI6ICJodHRwOi8vMTI3LjAuMC4xOjkyOTIv djEifV0sICJlbmRwb2ludHNfbGlua3MiOiBbXSwgInR5cGUiOiAiaW1hZ2UiLCAi bmFtZSI6ICJnbGFuY2UifSwNCgkJCSAgICAgeyJlbmRwb2ludHMiOiBbeyJhZG1p blVSTCI6ICJodHRwOi8vMTI3LjAuMC4xOjg3NzQvdjEuMS82NGI2ZjNmYmNjNTM0 MzVlOGE2MGZjZjg5YmI2NjE3YSIsICJyZWdpb24iOiAicmVnaW9uT25lIiwgImlu dGVybmFsVVJMIjogImh0dHA6Ly8xMjcuMC4wLjE6ODc3NC92MS4xLzY0YjZmM2Zi Y2M1MzQzNWU4YTYwZmNmODliYjY2MTdhIiwgInB1YmxpY1VSTCI6ICJodHRwOi8v MTI3LjAuMC4xOjg3NzQvdjEuMS82NGI2ZjNmYmNjNTM0MzVlOGE2MGZjZjg5YmI2 NjE3YSJ9XSwgImVuZHBvaW50c19saW5rcyI6IFtdLCAidHlwZSI6ICJjb21wdXRl IiwgIm5hbWUiOiAibm92YSJ9LA0KCQkJICAgICB7ImVuZHBvaW50cyI6IFt7ImFk bWluVVJMIjogImh0dHA6Ly8xMjcuMC4wLjE6MzUzNTcvdjMiLCAicmVnaW9uIjog IlJlZ2lvbk9uZSIsICJpbnRlcm5hbFVSTCI6ICJodHRwOi8vMTI3LjAuMC4xOjM1 MzU3L3YzIiwgInB1YmxpY1VSTCI6ICJodHRwOi8vMTI3LjAuMC4xOjUwMDAvdjMi fV0sICJlbmRwb2ludHNfbGlua3MiOiBbXSwgInR5cGUiOiAiaWRlbnRpdHkiLCAi bmFtZSI6ICJrZXlzdG9uZSJ9XSwNCgkgImV4cGlyZXNfYXQiOiAiMjAzOC0wMS0x OFQyMToxNDowN1oiLA0KCSAicHJvamVjdCI6IHsiZW5hYmxlZCI6IHRydWUsICJk ZXNjcmlwdGlvbiI6IG51bGwsICJuYW1lIjogInRlbmFudF9uYW1lMSIsICJpZCI6 ICJ0ZW5hbnRfaWQxIiwgImRvbWFpbiI6IHsiaWQiOiAiZG9tYWluX2lkMSIsICJu YW1lIjogImRvbWFpbl9uYW1lMSJ9fSwNCgkgInVzZXIiOiB7Im5hbWUiOiAidXNl cl9uYW1lMSIsICJpZCI6ICJ1c2VyX2lkMSIsICJkb21haW4iOiB7ImlkIjogImRv bWFpbl9pZDEiLCAibmFtZSI6ICJkb21haW5fbmFtZTEifX0sDQoJICJyb2xlcyI6 IFt7Im5hbWUiOiAicm9sZTEifSwgeyJuYW1lIjogInJvbGUyIn1dLA0KICAgICAg ICAgIm1ldGhvZHMiOiBbInBhc3N3b3JkIl0NCgkgfQ0KfQ0KMYIByjCCAcYCAQEw gaQwgZ4xCjAIBgNVBAUTATUxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTESMBAG A1UEBxMJU3Vubnl2YWxlMRIwEAYDVQQKEwlPcGVuU3RhY2sxETAPBgNVBAsTCEtl eXN0b25lMSUwIwYJKoZIhvcNAQkBFhZrZXlzdG9uZUBvcGVuc3RhY2sub3JnMRQw EgYDVQQDEwtTZWxmIFNpZ25lZAIBETAHBgUrDgMCGjANBgkqhkiG9w0BAQEFAASC AQCjMu6pnPu6uCaCrbELvNmuRd5d4WbsI4N2eh6JlD6oTF4vOFo5JmZIsHTUW+oi b5Tly7C4ivm549NomTOUvNugI9G2M25rh3EXVzv59Jrj1NlC1uBlgR5W3/x7yPSr JyJumkRy3R/mVIbd+CV0f8qJ4tCia43QhGrLT3jPUl5LSQOfWOunDNL7wZF0OfBq wNkwGLpN2i9oMlcXwOmEQw9Kh3Th0p54QogY06LmCjXsneyjl0J3hucLevdMHuXV f3bonuowe8OEB/0GDkJFIZChKgtN6GRG5hIpb6zDjP9almmJk2Is1HdJPI1+94nj coCr6q/P3e4rE48NZicsf1sM -----END CMS----- python-keystoneclient-0.7.1/examples/pki/cms/auth_v3_token_revoked.pem0000664000175400017540000000556712315030450027420 0ustar jenkinsjenkins00000000000000-----BEGIN CMS----- MIIIVgYJKoZIhvcNAQcCoIIIRzCCCEMCAQExCTAHBgUrDgMCGjCCBmMGCSqGSIb3 DQEHAaCCBlQEggZQeyJ0b2tlbiI6DQogICAgeyJjYXRhbG9nIjogW3siZW5kcG9p bnRzIjogW3siYWRtaW5VUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo4Nzc2L3YxLzY0 YjZmM2ZiY2M1MzQzNWU4YTYwZmNmODliYjY2MTdhIiwgInJlZ2lvbiI6ICJyZWdp b25PbmUiLCAiaW50ZXJuYWxVUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo4Nzc2L3Yx LzY0YjZmM2ZiY2M1MzQzNWU4YTYwZmNmODliYjY2MTdhIiwgInB1YmxpY1VSTCI6 ICJodHRwOi8vMTI3LjAuMC4xOjg3NzYvdjEvNjRiNmYzZmJjYzUzNDM1ZThhNjBm Y2Y4OWJiNjYxN2EifV0sICJlbmRwb2ludHNfbGlua3MiOiBbXSwgInR5cGUiOiAi dm9sdW1lIiwgIm5hbWUiOiAidm9sdW1lIn0sDQogICAgICAgICAgICAgICAgIHsi ZW5kcG9pbnRzIjogW3siYWRtaW5VUkwiOiAiaHR0cDovLzEyNy4wLjAuMTo5Mjky L3YxIiwgInJlZ2lvbiI6ICJyZWdpb25PbmUiLCAiaW50ZXJuYWxVUkwiOiAiaHR0 cDovLzEyNy4wLjAuMTo5MjkyL3YxIiwgInB1YmxpY1VSTCI6ICJodHRwOi8vMTI3 LjAuMC4xOjkyOTIvdjEifV0sICJlbmRwb2ludHNfbGlua3MiOiBbXSwgInR5cGUi OiAiaW1hZ2UiLCAibmFtZSI6ICJnbGFuY2UifSwNCiAgICAgICAgICAgICAgICAg eyJlbmRwb2ludHMiOiBbeyJhZG1pblVSTCI6ICJodHRwOi8vMTI3LjAuMC4xOjg3 NzQvdjEuMS82NGI2ZjNmYmNjNTM0MzVlOGE2MGZjZjg5YmI2NjE3YSIsICJyZWdp b24iOiAicmVnaW9uT25lIiwgImludGVybmFsVVJMIjogImh0dHA6Ly8xMjcuMC4w LjE6ODc3NC92MS4xLzY0YjZmM2ZiY2M1MzQzNWU4YTYwZmNmODliYjY2MTdhIiwg InB1YmxpY1VSTCI6ICJodHRwOi8vMTI3LjAuMC4xOjg3NzQvdjEuMS82NGI2ZjNm YmNjNTM0MzVlOGE2MGZjZjg5YmI2NjE3YSJ9XSwgImVuZHBvaW50c19saW5rcyI6 IFtdLCAidHlwZSI6ICJjb21wdXRlIiwgIm5hbWUiOiAibm92YSJ9LA0KICAgICAg ICAgICAgICAgICB7ImVuZHBvaW50cyI6IFt7ImFkbWluVVJMIjogImh0dHA6Ly8x MjcuMC4wLjE6MzUzNTcvdjMiLCAicmVnaW9uIjogIlJlZ2lvbk9uZSIsICJpbnRl cm5hbFVSTCI6ICJodHRwOi8vMTI3LjAuMC4xOjM1MzU3L3YzIiwgInB1YmxpY1VS TCI6ICJodHRwOi8vMTI3LjAuMC4xOjUwMDAvdjMifV0sICJlbmRwb2ludHNfbGlu a3MiOiBbXSwgInR5cGUiOiAiaWRlbnRpdHkiLCAibmFtZSI6ICJrZXlzdG9uZSJ9 XSwNCiAgICAgImV4cGlyZXNfYXQiOiAiMjAzOC0wMS0xOFQyMToxNDowN1oiLA0K ICAgICAicHJvamVjdCI6IHsiZW5hYmxlZCI6IHRydWUsICJkZXNjcmlwdGlvbiI6 IG51bGwsICJuYW1lIjogInRlbmFudF9uYW1lMSIsICJpZCI6ICJ0ZW5hbnRfaWQx IiwgImRvbWFpbiI6IHsiaWQiOiAiZG9tYWluX2lkMSIsICJuYW1lIjogImRvbWFp bl9uYW1lMSJ9fSwNCiAgICAgInVzZXIiOiB7Im5hbWUiOiAicmV2b2tlZF91c2Vy bmFtZTEiLCAiaWQiOiAicmV2b2tlZF91c2VyX2lkMSIsICJkb21haW4iOiB7Imlk IjogImRvbWFpbl9pZDEiLCAibmFtZSI6ICJkb21haW5fbmFtZTEifX0sDQogICAg ICJyb2xlcyI6IFt7Im5hbWUiOiAicm9sZTEifSwgeyJuYW1lIjogInJvbGUyIn1d LA0KICAgICAibWV0aG9kcyI6IFsicGFzc3dvcmQiXSwNCiAgICB9DQp9DQoxggHK MIIBxgIBATCBpDCBnjEKMAgGA1UEBRMBNTELMAkGA1UEBhMCVVMxCzAJBgNVBAgT AkNBMRIwEAYDVQQHEwlTdW5ueXZhbGUxEjAQBgNVBAoTCU9wZW5TdGFjazERMA8G A1UECxMIS2V5c3RvbmUxJTAjBgkqhkiG9w0BCQEWFmtleXN0b25lQG9wZW5zdGFj ay5vcmcxFDASBgNVBAMTC1NlbGYgU2lnbmVkAgERMAcGBSsOAwIaMA0GCSqGSIb3 DQEBAQUABIIBAI9qvSGYlIHoI0gWTZ55POWwj5Gjyr/SQ4q2e13m5bSnmvawGw6Z Xa500UvvnUPHe/Nf4ExS0/hO2zn80hjLC+uEDwESlMMDTc29+TbafHTQOp+Zz742 KdO+2Zv3UevStJK+wg+D0cKUmhmghyzzIsD44OmzLaGe+3mCqLkWFv8i7KX9rrb5 Jqv8kbLm9bY7wlWqmYcaJyXy5SdJMKi1/aWm9nn5AOaujHTfV/bygQd8ZY9t4+6k OwKPZlt5x1KA3IxGdJUFvIHj7am6j0auQ2TMx8x2vzPzy5+mBzB0u0XaENJsb9Jz z+aVgXOQ81PbwwZmStONrwEVpjQeyEXY4pM= -----END CMS----- python-keystoneclient-0.7.1/examples/pki/cms/auth_token_unscoped.json0000664000175400017540000000044212315030450027344 0ustar jenkinsjenkins00000000000000{"access": {"token": {"expires": "2112-08-17T15:35:34Z", "id": "01e032c996ef4406b144335915a41e79"}, "serviceCatalog": {}, "user": {"username": "user_name1", "roles_links": [], "id": "c9c89e3be3ee453fbf00c7966f6d3fbd", "roles": [{'name': 'role1'},{'name': 'role2'},], "name": "user_name1"}}}python-keystoneclient-0.7.1/examples/pki/cms/auth_token_scoped_expired.json0000664000175400017540000000267212315030450030530 0ustar jenkinsjenkins00000000000000{"access": {"serviceCatalog": [{"endpoints": [{"adminURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a", "region": "regionOne", "internalURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a", "publicURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a"}], "endpoints_links": [], "type": "volume", "name": "volume"}, {"endpoints": [{"adminURL": "http://127.0.0.1:9292/v1", "region": "regionOne", "internalURL": "http://127.0.0.1:9292/v1", "publicURL": "http://127.0.0.1:9292/v1"}], "endpoints_links": [], "type": "image", "name": "glance"}, {"endpoints": [{"adminURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a", "region": "regionOne", "internalURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a", "publicURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a"}], "endpoints_links": [], "type": "compute", "name": "nova"}, {"endpoints": [{"adminURL": "http://127.0.0.1:35357/v2.0", "region": "RegionOne", "internalURL": "http://127.0.0.1:35357/v2.0", "publicURL": "http://127.0.0.1:5000/v2.0"}], "endpoints_links": [], "type": "identity", "name": "keystone"}],"token": {"expires": "2010-06-02T14:47:34Z", "id": "placeholder", "tenant": {"enabled": true, "description": null, "name": "tenant_name1", "id": "tenant_id1"}}, "user": {"username": "user_name1", "roles_links": ["role1","role2"], "id": "user_id1", "roles": [{"name": "role1"}, {"name": "role2"}], "name": "user_name1"}}} python-keystoneclient-0.7.1/examples/pki/cms/auth_v3_token_scoped.json0000664000175400017540000000301412315030450027407 0ustar jenkinsjenkins00000000000000{"token": {"catalog": [{"endpoints": [{"adminURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a", "region": "regionOne", "internalURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a", "publicURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a"}], "endpoints_links": [], "type": "volume", "name": "volume"}, {"endpoints": [{"adminURL": "http://127.0.0.1:9292/v1", "region": "regionOne", "internalURL": "http://127.0.0.1:9292/v1", "publicURL": "http://127.0.0.1:9292/v1"}], "endpoints_links": [], "type": "image", "name": "glance"}, {"endpoints": [{"adminURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a", "region": "regionOne", "internalURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a", "publicURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a"}], "endpoints_links": [], "type": "compute", "name": "nova"}, {"endpoints": [{"adminURL": "http://127.0.0.1:35357/v3", "region": "RegionOne", "internalURL": "http://127.0.0.1:35357/v3", "publicURL": "http://127.0.0.1:5000/v3"}], "endpoints_links": [], "type": "identity", "name": "keystone"}], "expires_at": "2038-01-18T21:14:07Z", "project": {"enabled": true, "description": null, "name": "tenant_name1", "id": "tenant_id1", "domain": {"id": "domain_id1", "name": "domain_name1"}}, "user": {"name": "user_name1", "id": "user_id1", "domain": {"id": "domain_id1", "name": "domain_name1"}}, "roles": [{"name": "role1"}, {"name": "role2"}], "methods": ["password"] } } python-keystoneclient-0.7.1/examples/pki/cms/auth_token_scoped.json0000664000175400017540000000267212315030450027010 0ustar jenkinsjenkins00000000000000{"access": {"serviceCatalog": [{"endpoints": [{"adminURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a", "region": "regionOne", "internalURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a", "publicURL": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a"}], "endpoints_links": [], "type": "volume", "name": "volume"}, {"endpoints": [{"adminURL": "http://127.0.0.1:9292/v1", "region": "regionOne", "internalURL": "http://127.0.0.1:9292/v1", "publicURL": "http://127.0.0.1:9292/v1"}], "endpoints_links": [], "type": "image", "name": "glance"}, {"endpoints": [{"adminURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a", "region": "regionOne", "internalURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a", "publicURL": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a"}], "endpoints_links": [], "type": "compute", "name": "nova"}, {"endpoints": [{"adminURL": "http://127.0.0.1:35357/v2.0", "region": "RegionOne", "internalURL": "http://127.0.0.1:35357/v2.0", "publicURL": "http://127.0.0.1:5000/v2.0"}], "endpoints_links": [], "type": "identity", "name": "keystone"}],"token": {"expires": "2038-01-18T21:14:07Z", "id": "placeholder", "tenant": {"enabled": true, "description": null, "name": "tenant_name1", "id": "tenant_id1"}}, "user": {"username": "user_name1", "roles_links": ["role1","role2"], "id": "user_id1", "roles": [{"name": "role1"}, {"name": "role2"}], "name": "user_name1"}}} python-keystoneclient-0.7.1/examples/pki/private/0000775000175400017540000000000012315030547023307 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/examples/pki/private/ssl_key.pem0000664000175400017540000000325012315030450025454 0ustar jenkinsjenkins00000000000000-----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDL06AaJROwHPgJ 9tcySSBepzJ81jYars2sMvLjyuvdiIBbhWvbS/a9Tw3WgL8H6OALkHiOU/f0A6Rp v8dGDIDsxZQVjT/4SLaQUOeDM+9bfkKHpSd9G3CsdSSZgOH08n+MyZ7slPHfUHLY Wso0SJD0vAi1gmGDlSM/mmhhHTpCDGo6Wbwqare6JNeTCGJTJYwrxtoMCh/W1Zrs lPC5lFvlHD7KBBf6IU2A8Xh/dUa3p5pmQeHPW8Em90DzIB1qH0DRXl3KANc24xYR R45pPCVkk6vFsy6P0JwwpnkszB+LcK6CEsJhLsOYvQFsiQfSZ8m7YGhgrMLxtop4 YEPirGGrAgMBAAECggEATwvbY0hNwlb5uqOIAXBqpUqiQdexU9fG26lGmSDxKBDv 9o5frcRgBDrMWwvDCgY+HT4CAvB9kJx4/qnpVjkzJp/ZNiJ5VIiehIlbv348rXbh xkk+bz5dDATCFOXuu1fwL2FhyM5anwhMAav0DyK1VLQ3jGzr9GO6L8hqAn+bQFFu 6ngiODwfhBMl5aRoL9UOBEhccK07znrH0JGRz+3+5Cdz59Xw91Bv210LhNNDL58+ 0JD0N+YztVOQd2bgwo0bQbOEijzmYq+0mjoqAnJh1/++y7PlIPs0AnPgqSnFPx9+ 6FsQEVRgk5Uq3kvPLaP4nT2y6MDZSp+ujYldvJhyQQKBgQDuX2pZIJMZ4aFnkG+K TmJ5wsLa/u9an0TmvAL9RLtBpVpQNKD8cQ+y8PUZavXDbAIt5NWqZVnTbCR79Dnd mZKblwcHhtsyA5f89el5KcxY2BREWdHdTnJpNd7XRlUECmzvX1zGj77lA982PhII yflRBRV3vqLkgC8vfoYgRyRElwKBgQDa5jnLdx/RahfYMOgn1HE5o4hMzLR4Y0Dd +gELshcUbPqouoP5zOb8WOagVJIgZVOSN+/VqbilVYrqRiNTn2rnoxs+HHRdaJNN 3eXllD4J2HfC2BIj1xSpIdyh2XewAJqw9IToHNB29QUhxOtgwseHciPG6JaKH2ik kqGKH/EKDQKBgFFAftygiOPCkCTgC9UmANUmOQsy6N2H+pF3tsEj43xt44oBVnqW A1boYXNnjRwuvdNs9BPf9i1l6E3EItFRXrLgWQoMwryakv0ryYh+YeRKyyW9RBbe fYs1TJ8unx4Ae79gTxxztQsVNcmkgLs0NWKTjAzEE3w14V+cDhYEie1DAoGBAJdI V5cLrBzBstsB6eBlDR9lqrRRIUS2a8U9m+1mVlcSfiWQSdehSd4K3tDdwePLw3ch W4qR8n+pYAlLEe0gFvUhn5lMdwt7U5qUCeehjUKmrRYm2FqWsbu2IFJnBjXIJSC4 zQXRrC0aZ0KQYpAL7XPpaVp1slyhGmPqxuO78Y0dAoGBAMHo3EIMwu9rfuGwFodr GFsOZhfJqgo5GDNxxf89Q9WWpMDTCdX+wdBTrN/wsMbBuwIDHrUuRnk6D5CWRjSk /ikCgHN3kOtrbL8zzqRomGAIIWKYGFEIGe1GHVGo5r//HXHdPxFXygvruQ/xbOA4 RGvmDiji8vVDq7Shho8I6KuT -----END PRIVATE KEY----- python-keystoneclient-0.7.1/examples/pki/private/signing_key.pem0000664000175400017540000000325412315030450026315 0ustar jenkinsjenkins00000000000000-----BEGIN PRIVATE KEY----- MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDM+VrILLl962VH S8EKWVzdkaOy0OoxGZ63gajM7VTm8AbgtVnYibIOnVZQuz1XbftIGNXPFhYNUypr LnMXrEEsnxgD4PvU/4bETG+stdricX6d1oKqsNFNR7F7zImiR/OzGhp7dONwccxf kfX4QHA5Ogso+XMfSdC72SRDszeCeGUcjuo/w2WSLW95SuVvcZLqE/pk3Q2TkCZ1 8hvNfLoln43QpC469a7srUXATqOJ2mPNvL6E/wOyPefmAoCoG44lFoR3k2jZjBEI hstJxmH7XgvqErBzpcWd29dms8xz5PNwYdns9CIfb3GaHvQ6r5RTl37/avDrGHOW KOoD01xLAgMBAAECggEAaIi22qWsh+JYCW9B6NRAPyN6V8Sh2x6UykOO4cwb45b/ +vOh+YPn0fo9vfhvxTnq0A8SY4WBA5SpanYK7kTEDEyqw7em1y7l/RB6V5t7IMb+ 6uIuS3zXkVEB3AApJSEK0Ql7/gBTydHPh+H5jnzWfujyLhhhtNBBarvH+drZcWio lWx8RERN4cH+3DZD/xxjH2Ff+X1XMvb8Xcup7MlWi2FtREg7LttLNWNK25iWjciP QwfWQIrURRJrD2IrOr9V2nuIEvRqRRBoO+pxJT2sC48NJ3hiKV2GtSQe2nRpQJ47 f9MEsF5KVQOOn+aQ60EKOI0MpNPmpiCZ5hFvBrNuOQKBgQD6vueEdI9eJgz5YN+t XWdpNippv35RTD8R4bQcE6GqIUXOmtQFS2wPJLn7nisZUsGMNEs36Yl0T9iow63r 5GNAfgzpqN1XZqaSMwAdxKmlBNYpAkVXHhv+1jN+9diDYmoj9T+3Q6Zvk5e/Liyp 6i+TsDppwmmr2utWajhyJ7owFwKBgQDRROncTztGDYLfRcrIoYsPo79KQ8tqwd2a 07Usch2kplTqojCUmmhMMFgV2eZPPiCjnEy2bAYh9I/oj7xG6EwApXTshZdCpivC rbUV64MakRTUP8IvM6PdI+apkJRsRUi/bSyIbcRlvEoCMNZhfj/5VY6w/jlwrPJj oBOCXBlB7QKBgQDGEbEeX1i03UfYYh6uep7qbEAaooqsu5cCkBDPMO6+TmQvLPyY Zhio6bEEQs/2w/lhwBk+xHqw5zXVMiWbtiB03F1k4eBeXxbrW+AWo7gCQ4zMfh+6 Dm284wVwn9D1D/OaDevT31uEvcjb2ySq3/PPLSEnU8xXVaoa6/NEsX8Q5wKBgQCm 2smULWBXZKJ6n00mVxdnqun0rsVcI6Mrta14+KwGAdEnG5achdivFsTE924YtLKV gSPxN4RUQokTprc52jHvOf1WMNYAADpYCOSfy55G6nKvIP8VX5lB00Qw4uRUx5FP gB7H0K2NaGmiAYqNRXqAtOUG3kyyOFMzeAjWIdTJqQKBgQCHzY1c7sS1vv7mPEkr 6CpwoaEbZeFnWoHBA8Rd82psqfYsVJIRwk5Id8zgDSEmoEi8hQ9UrYbrFpLK77xq EYSxLQHTNlM0G3lyEsv/gJhwYYhdTYiW3Cx3F6Y++jyn9O/+hFMyQvuesAL7DUYE ptEfvzFprpQUpByXkIpuJub6fg== -----END PRIVATE KEY----- python-keystoneclient-0.7.1/examples/pki/private/cakey.pem0000664000175400017540000000325012315030450025077 0ustar jenkinsjenkins00000000000000-----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCl8906EaRpibQF cCBWfxzLi5x/XpZ9iL6UX92NrSJxcDbaGws7s+GtjgDy8UOEonesRWTeqQEZtHpC 3/UHHOnsA8F6ha/pq9LioqT7RehCnZCLBJwh5Ct+lclpWs15SkjJD2LTDkjox0eA 9nOBx+XDlWyU/GAyqx5Wsvg/Kxr0iod9/4IcJdnSdUjq4v0Cxg/zNk08XPJX+F0b UDhgdUf7JrAmmS5LA8wphRnbIgtVsf6VN9HrbqtHAJDxh8gEfuwdhEW1df1fBtZ+ 6WMIF3IRSbIsZELFB6sqcyRj7HhMoWMkdEyPb2f8mq61MzTgE6lJGIyTRvEoFie7 qtGADIofAgMBAAECggEBAJ47X3y2xaU7f0KQHsVafgI2JAnuDl+zusOOhJlJs8Wl 0Sc1EgjjAxOQiqcaE96rap//qqYDTuFLjCenkuItV32KNzizr3+GLZWaruRHS6X4 xpFG2/gUrsQL3fdudOxpP+01lmzW+f25xRvZ4VilWRabquSDntWxA0R3cOwKFbGD uuwbTw3pBrRfCk/2IdpQtRrvvkVIFiYT6b/zeCQzhp4RETbC0oxqcEEOIUGmimAV 9cbwafinxCo54cOfX4JAh3j7Mp3eQUymoFk5gnmIeVe0QmpH2VkN7eItrhEvHKOk On7a5xvQ8s3wqPV5ZawHQcqar/p3QnGkiT6a+8LkIMECgYEA2iJ2DprTGZFRN0M7 Yj4WLsSC3/GKK8eYsKG3TvMrmPqUDaiWLIvBoc1Le59x9eoF7Mha+WX+cAFL+GTg 1sB+PUZZStpf1R1tGvMldvpQ+5GplUBpuQe4J0n5rCG6+5jkvSr7xO+G1B+C3GFq KR3iltiW5WJRVwh2k8yGvx3agyUCgYEAwsKFX82F7O+9IVud1JSQWmZMiyEK+DEX JRnwx4HBuWr+AZqbb0grRRb6x8JTUOD4T7DZGxTaAdfzzRjKU2sBAO8VCgaj2Auv 5nsbvfXvrmDDCqwoaD2PMy+kgFvE0QTh65tzuGXl1IgpIYSC1JwnP6kOeUDbqE+k UXzfVZzDdvMCgYByk9dfJIPt0h7O4Em4+NO+DQqRhtYE2PqjDM60cZZc7IIICp2X GHHFA4i6jq3Vde9WyIbAqYpUWtoExzgylTm6BdGxN7NOxf4hQcZUEHepLIHfG85s mlloibrTZ4RH06+SjZlhgE9Z7JNYHvMcVc5HXc0k/9ep15AxYiUFDjFQ4QKBgG7i k089U4/X2wWgBNdgkmN1tQTNllJCmNvdzhG41dQ8j0vYe8C7BS+76qJLCGaW/6lX lfRuRcUg78UI5UDjPloKxR7FMwmxdb+yvdPEr2bH3qQ36nWW/u30pSMTnJYownwD MLp/AYCk2U4lBNwJ3+rF1ODCRY2pcnOWtg0nSL5zAoGAWRoOinogEnOodJzO7eB3 TmL6M9QMyrAPBDsCnduJ8yW5mMUNod139YbSDxZPYwTLhK/GiHP/7OvLV5hg0s4s QKnNaMeEowX7dyEO4ehnbfzysxXPKLRVhWhN6MCUc71NMxqr7QkuCXAjJS6/G21+ Im3+Xb3Scq+UZghR+jiEZF0= -----END PRIVATE KEY----- python-keystoneclient-0.7.1/examples/pki/gen_pki.sh0000775000175400017540000001360012315030450023601 0ustar jenkinsjenkins00000000000000#!/bin/bash # Copyright 2012 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # This script generates the crypto necessary for the SSL tests. DIR=`dirname "$0"` CURRENT_DIR=`cd "$DIR" && pwd` CERTS_DIR=$CURRENT_DIR/certs PRIVATE_DIR=$CURRENT_DIR/private CMS_DIR=$CURRENT_DIR/cms function rm_old { rm -rf $CERTS_DIR/*.pem rm -rf $PRIVATE_DIR/*.pem } function cleanup { rm -rf *.conf > /dev/null 2>&1 rm -rf index* > /dev/null 2>&1 rm -rf *.crt > /dev/null 2>&1 rm -rf newcerts > /dev/null 2>&1 rm -rf *.pem > /dev/null 2>&1 rm -rf serial* > /dev/null 2>&1 } function generate_ca_conf { echo ' [ req ] default_bits = 2048 default_keyfile = cakey.pem default_md = default prompt = no distinguished_name = ca_distinguished_name x509_extensions = ca_extensions [ ca_distinguished_name ] serialNumber = 5 countryName = US stateOrProvinceName = CA localityName = Sunnyvale organizationName = OpenStack organizationalUnitName = Keystone emailAddress = keystone@openstack.org commonName = Self Signed [ ca_extensions ] basicConstraints = critical,CA:true ' > ca.conf } function generate_ssl_req_conf { echo ' [ req ] default_bits = 2048 default_keyfile = keystonekey.pem default_md = default prompt = no distinguished_name = distinguished_name [ distinguished_name ] countryName = US stateOrProvinceName = CA localityName = Sunnyvale organizationName = OpenStack organizationalUnitName = Keystone commonName = localhost emailAddress = keystone@openstack.org ' > ssl_req.conf } function generate_cms_signing_req_conf { echo ' [ req ] default_bits = 2048 default_keyfile = keystonekey.pem default_md = default prompt = no distinguished_name = distinguished_name [ distinguished_name ] countryName = US stateOrProvinceName = CA localityName = Sunnyvale organizationName = OpenStack organizationalUnitName = Keystone commonName = Keystone emailAddress = keystone@openstack.org ' > cms_signing_req.conf } function generate_signing_conf { echo ' [ ca ] default_ca = signing_ca [ signing_ca ] dir = . database = $dir/index.txt new_certs_dir = $dir/newcerts certificate = $dir/certs/cacert.pem serial = $dir/serial private_key = $dir/private/cakey.pem default_days = 21360 default_crl_days = 30 default_md = default policy = policy_any [ policy_any ] countryName = supplied stateOrProvinceName = supplied localityName = optional organizationName = supplied organizationalUnitName = supplied emailAddress = supplied commonName = supplied ' > signing.conf } function setup { touch index.txt echo '10' > serial generate_ca_conf mkdir newcerts } function check_error { if [ $1 != 0 ] ; then echo "Failed! rc=${1}" echo 'Bailing ...' cleanup exit $1 else echo 'Done' fi } function generate_ca { echo 'Generating New CA Certificate ...' openssl req -x509 -newkey rsa:2048 -days 21360 -out $CERTS_DIR/cacert.pem -keyout $PRIVATE_DIR/cakey.pem -outform PEM -config ca.conf -nodes check_error $? } function ssl_cert_req { echo 'Generating SSL Certificate Request ...' generate_ssl_req_conf openssl req -newkey rsa:2048 -keyout $PRIVATE_DIR/ssl_key.pem -keyform PEM -out ssl_req.pem -outform PEM -config ssl_req.conf -nodes check_error $? #openssl req -in req.pem -text -noout } function cms_signing_cert_req { echo 'Generating CMS Signing Certificate Request ...' generate_cms_signing_req_conf openssl req -newkey rsa:2048 -keyout $PRIVATE_DIR/signing_key.pem -keyform PEM -out cms_signing_req.pem -outform PEM -config cms_signing_req.conf -nodes check_error $? #openssl req -in req.pem -text -noout } function issue_certs { generate_signing_conf echo 'Issuing SSL Certificate ...' openssl ca -in ssl_req.pem -config signing.conf -batch check_error $? openssl x509 -in $CURRENT_DIR/newcerts/10.pem -out $CERTS_DIR/ssl_cert.pem check_error $? echo 'Issuing CMS Signing Certificate ...' openssl ca -in cms_signing_req.pem -config signing.conf -batch check_error $? openssl x509 -in $CURRENT_DIR/newcerts/11.pem -out $CERTS_DIR/signing_cert.pem check_error $? } function create_middleware_cert { cp $CERTS_DIR/ssl_cert.pem $CERTS_DIR/middleware.pem cat $PRIVATE_DIR/ssl_key.pem >> $CERTS_DIR/middleware.pem } function check_openssl { echo 'Checking openssl availability ...' which openssl check_error $? } function gen_sample_cms { for json_file in "${CMS_DIR}/auth_token_revoked.json" "${CMS_DIR}/auth_token_unscoped.json" "${CMS_DIR}/auth_token_scoped.json" "${CMS_DIR}/auth_token_scoped_expired.json" "${CMS_DIR}/revocation_list.json" "${CMS_DIR}/auth_v3_token_scoped.json" "${CMS_DIR}/auth_v3_token_revoked.json" do openssl cms -sign -in $json_file -nosmimecap -signer $CERTS_DIR/signing_cert.pem -inkey $PRIVATE_DIR/signing_key.pem -outform PEM -nodetach -nocerts -noattr -out ${json_file/.json/.pem} done } check_openssl rm_old cleanup setup generate_ca ssl_cert_req cms_signing_cert_req issue_certs create_middleware_cert gen_sample_cms cleanup python-keystoneclient-0.7.1/python_keystoneclient.egg-info/0000775000175400017540000000000012315030547025367 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/python_keystoneclient.egg-info/dependency_links.txt0000664000175400017540000000000112315030547031435 0ustar jenkinsjenkins00000000000000 python-keystoneclient-0.7.1/python_keystoneclient.egg-info/not-zip-safe0000664000175400017540000000000112315030545027613 0ustar jenkinsjenkins00000000000000 python-keystoneclient-0.7.1/python_keystoneclient.egg-info/PKG-INFO0000664000175400017540000003003112315030547026461 0ustar jenkinsjenkins00000000000000Metadata-Version: 1.1 Name: python-keystoneclient Version: 0.7.1 Summary: Client Library for OpenStack Identity Home-page: http://www.openstack.org/ Author: OpenStack Author-email: openstack-dev@lists.openstack.org License: UNKNOWN Description: Python bindings to the OpenStack Identity API (Keystone) ======================================================== This is a client for the OpenStack Identity API, implemented by Keystone. There's a Python API (the ``keystoneclient`` module), and a command-line script (``keystone``). Development takes place via the usual OpenStack processes as outlined in the `OpenStack wiki`_. The master repository is on GitHub__. __ http://wiki.openstack.org/HowToContribute __ http://github.com/openstack/python-keystoneclient This code a fork of `Rackspace's python-novaclient`__ which is in turn a fork of `Jacobian's python-cloudservers`__. The python-keystoneclient is licensed under the Apache License like the rest of OpenStack. __ http://github.com/rackspace/python-novaclient __ http://github.com/jacobian/python-cloudservers .. contents:: Contents: :local: Python API ---------- By way of a quick-start:: # use v2.0 auth with http://example.com:5000/v2.0 >>> from keystoneclient.v2_0 import client >>> keystone = client.Client(username=USERNAME, password=PASSWORD, tenant_name=TENANT, auth_url=AUTH_URL) >>> keystone.tenants.list() >>> tenant = keystone.tenants.create(tenant_name="test", description="My new tenant!", enabled=True) >>> tenant.delete() Command-line API ---------------- Installing this package gets you a shell command, ``keystone``, that you can use to interact with OpenStack's Identity API. You'll need to provide your OpenStack tenant, username and password. You can do this with the ``--os-tenant-name``, ``--os-username`` and ``--os-password`` params, but it's easier to just set them as environment variables:: export OS_TENANT_NAME=project export OS_USERNAME=user export OS_PASSWORD=pass You will also need to define the authentication url with ``--os-auth-url`` and the version of the API with ``--os-identity-api-version``. Or set them as an environment variables as well:: export OS_AUTH_URL=http://example.com:5000/v2.0 export OS_IDENTITY_API_VERSION=2.0 Alternatively, to bypass username/password authentication, you can provide a pre-established token. In Keystone, this approach is necessary to bootstrap the service with an administrative user, tenant & role (to do so, provide the client with the value of your ``admin_token`` defined in ``keystone.conf`` in addition to the URL of your admin API deployment, typically on port 35357):: export OS_SERVICE_TOKEN=thequickbrownfox-jumpsover-thelazydog export OS_SERVICE_ENDPOINT=http://example.com:35357/v2.0 Since the Identity service can return multiple regions in the service catalog, you can specify the one you want with ``--os-region-name`` (or ``export OS_REGION_NAME``):: export OS_REGION_NAME=north .. WARNING:: If a region is not specified and multiple regions are returned by the Identity service, the client may not access the same region consistently. If you need to connect to a server that is TLS-enabled (the auth URL begins with 'https') and it uses a certificate from a private CA or a self-signed certificate you will need to specify the path to an appropriate CA certificate to use to validate the server certificate with ``--os-cacert`` or an environment variable:: export OS_CACERT=/etc/ssl/my-root-cert.pem Certificate verification can be turned off using ``--insecure``. This should be used with caution. You'll find complete documentation on the shell by running ``keystone help``:: usage: keystone [--version] [--timeout ] [--os-username ] [--os-password ] [--os-tenant-name ] [--os-tenant-id ] [--os-auth-url ] [--os-region-name ] [--os-identity-api-version ] [--os-token ] [--os-endpoint ] [--os-cacert ] [--insecure] [--os-cert ] [--os-key ] [--os-cache] [--force-new-token] [--stale-duration ] ... Command-line interface to the OpenStack Identity API. Positional arguments: catalog ec2-credentials-create Create EC2-compatible credentials for user per tenant ec2-credentials-delete Delete EC2-compatible credentials ec2-credentials-get Display EC2-compatible credentials ec2-credentials-list List EC2-compatible credentials for a user endpoint-create Create a new endpoint associated with a service endpoint-delete Delete a service endpoint endpoint-get endpoint-list List configured service endpoints password-update Update own password role-create Create new role role-delete Delete role role-get Display role details role-list List all roles service-create Add service to Service Catalog service-delete Delete service from Service Catalog service-get Display service from Service Catalog service-list List all services in Service Catalog tenant-create Create new tenant tenant-delete Delete tenant tenant-get Display tenant details tenant-list List all tenants tenant-update Update tenant name, description, enabled status token-get user-create Create new user user-delete Delete user user-get Display user details. user-list List users user-password-update Update user password user-role-add Add role to user user-role-list List roles granted to a user user-role-remove Remove role from user user-update Update user's name, email, and enabled status discover Discover Keystone servers, supported API versions and extensions. bootstrap Grants a new role to a new user on a new tenant, after creating each. bash-completion Prints all of the commands and options to stdout. help Display help about this program or one of its subcommands. Optional arguments: --version Shows the client version and exits --timeout Set request timeout (in seconds) --os-username Name used for authentication with the OpenStack Identity service. Defaults to env[OS_USERNAME] --os-password Password used for authentication with the OpenStack Identity service. Defaults to env[OS_PASSWORD] --os-tenant-name Tenant to request authorization on. Defaults to env[OS_TENANT_NAME] --os-tenant-id Tenant to request authorization on. Defaults to env[OS_TENANT_ID] --os-auth-url Specify the Identity endpoint to use for authentication. Defaults to env[OS_AUTH_URL] --os-region-name Defaults to env[OS_REGION_NAME] --os-identity-api-version Defaults to env[OS_IDENTITY_API_VERSION] or 2.0 --os-token Specify an existing token to use instead of retrieving one via authentication (e.g. with username & password). Defaults to env[OS_SERVICE_TOKEN] --os-endpoint Specify an endpoint to use instead of retrieving one from the service catalog (via authentication). Defaults to env[OS_SERVICE_ENDPOINT] --os-cacert Specify a CA bundle file to use in verifying a TLS (https) server certificate. Defaults to env[OS_CACERT] --insecure Explicitly allow keystoneclient to perform "insecure" TLS (https) requests. The server's certificate will not be verified against any certificate authorities. This option should be used with caution. --os-cert Defaults to env[OS_CERT] --os-key Defaults to env[OS_KEY] --os-cache Use the auth token cache. Defaults to env[OS_CACHE] --force-new-token If the keyring is available and in use, token will always be stored and fetched from the keyring until the token has expired. Use this option to request a new token and replace the existing one in the keyring. --stale-duration Stale duration (in seconds) used to determine whether a token has expired when retrieving it from keyring. This is useful in mitigating process or network delays. Default is 30 seconds. See "keystone help COMMAND" for help on a specific command. Platform: UNKNOWN Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 python-keystoneclient-0.7.1/python_keystoneclient.egg-info/entry_points.txt0000664000175400017540000000007012315030547030662 0ustar jenkinsjenkins00000000000000[console_scripts] keystone = keystoneclient.shell:main python-keystoneclient-0.7.1/python_keystoneclient.egg-info/top_level.txt0000664000175400017540000000001712315030547030117 0ustar jenkinsjenkins00000000000000keystoneclient python-keystoneclient-0.7.1/python_keystoneclient.egg-info/requires.txt0000664000175400017540000000017012315030547027765 0ustar jenkinsjenkins00000000000000Babel>=1.3 iso8601>=0.1.8 netaddr>=0.7.6 oslo.config>=1.2.0 pbr>=0.6,<1.0 PrettyTable>=0.7,<0.8 requests>=1.1 six>=1.5.2python-keystoneclient-0.7.1/python_keystoneclient.egg-info/SOURCES.txt0000664000175400017540000001576212315030547027266 0ustar jenkinsjenkins00000000000000.coveragerc .mailmap .testr.conf AUTHORS ChangeLog HACKING.rst LICENSE MANIFEST.in README.rst babel.cfg openstack-common.conf requirements.txt run_tests.sh setup.cfg setup.py test-requirements.txt tox.ini doc/.gitignore doc/Makefile doc/ext/__init__.py doc/ext/apidoc.py doc/source/conf.py doc/source/index.rst doc/source/middlewarearchitecture.rst doc/source/releases.rst doc/source/using-api-v2.rst doc/source/using-api-v3.rst doc/source/_templates/.placeholder doc/source/_theme/layout.html doc/source/_theme/theme.conf doc/source/images/graphs_authComp.svg doc/source/images/graphs_authCompDelegate.svg doc/source/man/keystone.rst doc/source/static/basic.css doc/source/static/default.css doc/source/static/header-line.gif doc/source/static/header_bg.jpg doc/source/static/jquery.tweet.js doc/source/static/nature.css doc/source/static/openstack_logo.png doc/source/static/tweaks.css examples/pki/gen_pki.sh examples/pki/certs/cacert.pem examples/pki/certs/middleware.pem examples/pki/certs/signing_cert.pem examples/pki/certs/ssl_cert.pem examples/pki/cms/auth_token_revoked.json examples/pki/cms/auth_token_revoked.pem examples/pki/cms/auth_token_scoped.json examples/pki/cms/auth_token_scoped.pem examples/pki/cms/auth_token_scoped_expired.json examples/pki/cms/auth_token_scoped_expired.pem examples/pki/cms/auth_token_unscoped.json examples/pki/cms/auth_token_unscoped.pem examples/pki/cms/auth_v3_token_revoked.json examples/pki/cms/auth_v3_token_revoked.pem examples/pki/cms/auth_v3_token_scoped.json examples/pki/cms/auth_v3_token_scoped.pem examples/pki/cms/revocation_list.json examples/pki/cms/revocation_list.pem examples/pki/private/cakey.pem examples/pki/private/signing_key.pem examples/pki/private/ssl_key.pem keystoneclient/__init__.py keystoneclient/access.py keystoneclient/base.py keystoneclient/baseclient.py keystoneclient/client.py keystoneclient/discover.py keystoneclient/exceptions.py keystoneclient/httpclient.py keystoneclient/service_catalog.py keystoneclient/session.py keystoneclient/shell.py keystoneclient/utils.py keystoneclient/apiclient/__init__.py keystoneclient/apiclient/exceptions.py keystoneclient/auth/__init__.py keystoneclient/auth/base.py keystoneclient/auth/token_endpoint.py keystoneclient/auth/identity/__init__.py keystoneclient/auth/identity/base.py keystoneclient/auth/identity/v2.py keystoneclient/auth/identity/v3.py keystoneclient/common/__init__.py keystoneclient/common/cms.py keystoneclient/contrib/__init__.py keystoneclient/contrib/bootstrap/__init__.py keystoneclient/contrib/bootstrap/shell.py keystoneclient/contrib/ec2/__init__.py keystoneclient/contrib/ec2/utils.py keystoneclient/generic/__init__.py keystoneclient/generic/client.py keystoneclient/generic/shell.py keystoneclient/locale/keystoneclient.pot keystoneclient/middleware/__init__.py keystoneclient/middleware/auth_token.py keystoneclient/middleware/memcache_crypt.py keystoneclient/middleware/s3_token.py keystoneclient/openstack/__init__.py keystoneclient/openstack/common/__init__.py keystoneclient/openstack/common/gettextutils.py keystoneclient/openstack/common/importutils.py keystoneclient/openstack/common/jsonutils.py keystoneclient/openstack/common/memorycache.py keystoneclient/openstack/common/strutils.py keystoneclient/openstack/common/timeutils.py keystoneclient/openstack/common/apiclient/__init__.py keystoneclient/openstack/common/apiclient/auth.py keystoneclient/openstack/common/apiclient/base.py keystoneclient/openstack/common/apiclient/client.py keystoneclient/openstack/common/apiclient/exceptions.py keystoneclient/openstack/common/apiclient/fake_client.py keystoneclient/tests/__init__.py keystoneclient/tests/client_fixtures.py keystoneclient/tests/fakes.py keystoneclient/tests/test_auth_token_middleware.py keystoneclient/tests/test_base.py keystoneclient/tests/test_cms.py keystoneclient/tests/test_discovery.py keystoneclient/tests/test_ec2utils.py keystoneclient/tests/test_http.py keystoneclient/tests/test_https.py keystoneclient/tests/test_keyring.py keystoneclient/tests/test_memcache_crypt.py keystoneclient/tests/test_s3_token_middleware.py keystoneclient/tests/test_session.py keystoneclient/tests/test_shell.py keystoneclient/tests/test_utils.py keystoneclient/tests/utils.py keystoneclient/tests/apiclient/test_exceptions.py keystoneclient/tests/auth/__init__.py keystoneclient/tests/auth/test_identity_v2.py keystoneclient/tests/auth/test_identity_v3.py keystoneclient/tests/auth/test_token_endpoint.py keystoneclient/tests/generic/__init__.py keystoneclient/tests/generic/test_client.py keystoneclient/tests/generic/test_shell.py keystoneclient/tests/v2_0/__init__.py keystoneclient/tests/v2_0/client_fixtures.py keystoneclient/tests/v2_0/fakes.py keystoneclient/tests/v2_0/test_access.py keystoneclient/tests/v2_0/test_auth.py keystoneclient/tests/v2_0/test_client.py keystoneclient/tests/v2_0/test_discovery.py keystoneclient/tests/v2_0/test_ec2.py keystoneclient/tests/v2_0/test_endpoints.py keystoneclient/tests/v2_0/test_roles.py keystoneclient/tests/v2_0/test_service_catalog.py keystoneclient/tests/v2_0/test_services.py keystoneclient/tests/v2_0/test_shell.py keystoneclient/tests/v2_0/test_tenants.py keystoneclient/tests/v2_0/test_tokens.py keystoneclient/tests/v2_0/test_users.py keystoneclient/tests/v2_0/utils.py keystoneclient/tests/v3/__init__.py keystoneclient/tests/v3/client_fixtures.py keystoneclient/tests/v3/test_access.py keystoneclient/tests/v3/test_auth.py keystoneclient/tests/v3/test_client.py keystoneclient/tests/v3/test_credentials.py keystoneclient/tests/v3/test_discover.py keystoneclient/tests/v3/test_domains.py keystoneclient/tests/v3/test_endpoints.py keystoneclient/tests/v3/test_groups.py keystoneclient/tests/v3/test_policies.py keystoneclient/tests/v3/test_projects.py keystoneclient/tests/v3/test_roles.py keystoneclient/tests/v3/test_service_catalog.py keystoneclient/tests/v3/test_services.py keystoneclient/tests/v3/test_trusts.py keystoneclient/tests/v3/test_users.py keystoneclient/tests/v3/utils.py keystoneclient/v2_0/__init__.py keystoneclient/v2_0/client.py keystoneclient/v2_0/ec2.py keystoneclient/v2_0/endpoints.py keystoneclient/v2_0/roles.py keystoneclient/v2_0/services.py keystoneclient/v2_0/shell.py keystoneclient/v2_0/tenants.py keystoneclient/v2_0/tokens.py keystoneclient/v2_0/users.py keystoneclient/v3/__init__.py keystoneclient/v3/client.py keystoneclient/v3/credentials.py keystoneclient/v3/domains.py keystoneclient/v3/endpoints.py keystoneclient/v3/groups.py keystoneclient/v3/policies.py keystoneclient/v3/projects.py keystoneclient/v3/roles.py keystoneclient/v3/services.py keystoneclient/v3/users.py keystoneclient/v3/contrib/__init__.py keystoneclient/v3/contrib/trusts.py python_keystoneclient.egg-info/PKG-INFO python_keystoneclient.egg-info/SOURCES.txt python_keystoneclient.egg-info/dependency_links.txt python_keystoneclient.egg-info/entry_points.txt python_keystoneclient.egg-info/not-zip-safe python_keystoneclient.egg-info/requires.txt python_keystoneclient.egg-info/top_level.txt tools/debug_helper.sh tools/install_venv.py tools/install_venv_common.py tools/keystone.bash_completion tools/with_venv.shpython-keystoneclient-0.7.1/README.rst0000664000175400017540000002316512315030450020723 0ustar jenkinsjenkins00000000000000Python bindings to the OpenStack Identity API (Keystone) ======================================================== This is a client for the OpenStack Identity API, implemented by Keystone. There's a Python API (the ``keystoneclient`` module), and a command-line script (``keystone``). Development takes place via the usual OpenStack processes as outlined in the `OpenStack wiki`_. The master repository is on GitHub__. __ http://wiki.openstack.org/HowToContribute __ http://github.com/openstack/python-keystoneclient This code a fork of `Rackspace's python-novaclient`__ which is in turn a fork of `Jacobian's python-cloudservers`__. The python-keystoneclient is licensed under the Apache License like the rest of OpenStack. __ http://github.com/rackspace/python-novaclient __ http://github.com/jacobian/python-cloudservers .. contents:: Contents: :local: Python API ---------- By way of a quick-start:: # use v2.0 auth with http://example.com:5000/v2.0 >>> from keystoneclient.v2_0 import client >>> keystone = client.Client(username=USERNAME, password=PASSWORD, tenant_name=TENANT, auth_url=AUTH_URL) >>> keystone.tenants.list() >>> tenant = keystone.tenants.create(tenant_name="test", description="My new tenant!", enabled=True) >>> tenant.delete() Command-line API ---------------- Installing this package gets you a shell command, ``keystone``, that you can use to interact with OpenStack's Identity API. You'll need to provide your OpenStack tenant, username and password. You can do this with the ``--os-tenant-name``, ``--os-username`` and ``--os-password`` params, but it's easier to just set them as environment variables:: export OS_TENANT_NAME=project export OS_USERNAME=user export OS_PASSWORD=pass You will also need to define the authentication url with ``--os-auth-url`` and the version of the API with ``--os-identity-api-version``. Or set them as an environment variables as well:: export OS_AUTH_URL=http://example.com:5000/v2.0 export OS_IDENTITY_API_VERSION=2.0 Alternatively, to bypass username/password authentication, you can provide a pre-established token. In Keystone, this approach is necessary to bootstrap the service with an administrative user, tenant & role (to do so, provide the client with the value of your ``admin_token`` defined in ``keystone.conf`` in addition to the URL of your admin API deployment, typically on port 35357):: export OS_SERVICE_TOKEN=thequickbrownfox-jumpsover-thelazydog export OS_SERVICE_ENDPOINT=http://example.com:35357/v2.0 Since the Identity service can return multiple regions in the service catalog, you can specify the one you want with ``--os-region-name`` (or ``export OS_REGION_NAME``):: export OS_REGION_NAME=north .. WARNING:: If a region is not specified and multiple regions are returned by the Identity service, the client may not access the same region consistently. If you need to connect to a server that is TLS-enabled (the auth URL begins with 'https') and it uses a certificate from a private CA or a self-signed certificate you will need to specify the path to an appropriate CA certificate to use to validate the server certificate with ``--os-cacert`` or an environment variable:: export OS_CACERT=/etc/ssl/my-root-cert.pem Certificate verification can be turned off using ``--insecure``. This should be used with caution. You'll find complete documentation on the shell by running ``keystone help``:: usage: keystone [--version] [--timeout ] [--os-username ] [--os-password ] [--os-tenant-name ] [--os-tenant-id ] [--os-auth-url ] [--os-region-name ] [--os-identity-api-version ] [--os-token ] [--os-endpoint ] [--os-cacert ] [--insecure] [--os-cert ] [--os-key ] [--os-cache] [--force-new-token] [--stale-duration ] ... Command-line interface to the OpenStack Identity API. Positional arguments: catalog ec2-credentials-create Create EC2-compatible credentials for user per tenant ec2-credentials-delete Delete EC2-compatible credentials ec2-credentials-get Display EC2-compatible credentials ec2-credentials-list List EC2-compatible credentials for a user endpoint-create Create a new endpoint associated with a service endpoint-delete Delete a service endpoint endpoint-get endpoint-list List configured service endpoints password-update Update own password role-create Create new role role-delete Delete role role-get Display role details role-list List all roles service-create Add service to Service Catalog service-delete Delete service from Service Catalog service-get Display service from Service Catalog service-list List all services in Service Catalog tenant-create Create new tenant tenant-delete Delete tenant tenant-get Display tenant details tenant-list List all tenants tenant-update Update tenant name, description, enabled status token-get user-create Create new user user-delete Delete user user-get Display user details. user-list List users user-password-update Update user password user-role-add Add role to user user-role-list List roles granted to a user user-role-remove Remove role from user user-update Update user's name, email, and enabled status discover Discover Keystone servers, supported API versions and extensions. bootstrap Grants a new role to a new user on a new tenant, after creating each. bash-completion Prints all of the commands and options to stdout. help Display help about this program or one of its subcommands. Optional arguments: --version Shows the client version and exits --timeout Set request timeout (in seconds) --os-username Name used for authentication with the OpenStack Identity service. Defaults to env[OS_USERNAME] --os-password Password used for authentication with the OpenStack Identity service. Defaults to env[OS_PASSWORD] --os-tenant-name Tenant to request authorization on. Defaults to env[OS_TENANT_NAME] --os-tenant-id Tenant to request authorization on. Defaults to env[OS_TENANT_ID] --os-auth-url Specify the Identity endpoint to use for authentication. Defaults to env[OS_AUTH_URL] --os-region-name Defaults to env[OS_REGION_NAME] --os-identity-api-version Defaults to env[OS_IDENTITY_API_VERSION] or 2.0 --os-token Specify an existing token to use instead of retrieving one via authentication (e.g. with username & password). Defaults to env[OS_SERVICE_TOKEN] --os-endpoint Specify an endpoint to use instead of retrieving one from the service catalog (via authentication). Defaults to env[OS_SERVICE_ENDPOINT] --os-cacert Specify a CA bundle file to use in verifying a TLS (https) server certificate. Defaults to env[OS_CACERT] --insecure Explicitly allow keystoneclient to perform "insecure" TLS (https) requests. The server's certificate will not be verified against any certificate authorities. This option should be used with caution. --os-cert Defaults to env[OS_CERT] --os-key Defaults to env[OS_KEY] --os-cache Use the auth token cache. Defaults to env[OS_CACHE] --force-new-token If the keyring is available and in use, token will always be stored and fetched from the keyring until the token has expired. Use this option to request a new token and replace the existing one in the keyring. --stale-duration Stale duration (in seconds) used to determine whether a token has expired when retrieving it from keyring. This is useful in mitigating process or network delays. Default is 30 seconds. See "keystone help COMMAND" for help on a specific command. python-keystoneclient-0.7.1/openstack-common.conf0000664000175400017540000000041512315030451023352 0ustar jenkinsjenkins00000000000000[DEFAULT] # The list of modules to copy from oslo-incubator module=apiclient module=importutils module=install_venv_common module=jsonutils module=memorycache module=strutils module=timeutils # The base module to hold the copy of openstack.common base=keystoneclient python-keystoneclient-0.7.1/PKG-INFO0000664000175400017540000003003112315030547020326 0ustar jenkinsjenkins00000000000000Metadata-Version: 1.1 Name: python-keystoneclient Version: 0.7.1 Summary: Client Library for OpenStack Identity Home-page: http://www.openstack.org/ Author: OpenStack Author-email: openstack-dev@lists.openstack.org License: UNKNOWN Description: Python bindings to the OpenStack Identity API (Keystone) ======================================================== This is a client for the OpenStack Identity API, implemented by Keystone. There's a Python API (the ``keystoneclient`` module), and a command-line script (``keystone``). Development takes place via the usual OpenStack processes as outlined in the `OpenStack wiki`_. The master repository is on GitHub__. __ http://wiki.openstack.org/HowToContribute __ http://github.com/openstack/python-keystoneclient This code a fork of `Rackspace's python-novaclient`__ which is in turn a fork of `Jacobian's python-cloudservers`__. The python-keystoneclient is licensed under the Apache License like the rest of OpenStack. __ http://github.com/rackspace/python-novaclient __ http://github.com/jacobian/python-cloudservers .. contents:: Contents: :local: Python API ---------- By way of a quick-start:: # use v2.0 auth with http://example.com:5000/v2.0 >>> from keystoneclient.v2_0 import client >>> keystone = client.Client(username=USERNAME, password=PASSWORD, tenant_name=TENANT, auth_url=AUTH_URL) >>> keystone.tenants.list() >>> tenant = keystone.tenants.create(tenant_name="test", description="My new tenant!", enabled=True) >>> tenant.delete() Command-line API ---------------- Installing this package gets you a shell command, ``keystone``, that you can use to interact with OpenStack's Identity API. You'll need to provide your OpenStack tenant, username and password. You can do this with the ``--os-tenant-name``, ``--os-username`` and ``--os-password`` params, but it's easier to just set them as environment variables:: export OS_TENANT_NAME=project export OS_USERNAME=user export OS_PASSWORD=pass You will also need to define the authentication url with ``--os-auth-url`` and the version of the API with ``--os-identity-api-version``. Or set them as an environment variables as well:: export OS_AUTH_URL=http://example.com:5000/v2.0 export OS_IDENTITY_API_VERSION=2.0 Alternatively, to bypass username/password authentication, you can provide a pre-established token. In Keystone, this approach is necessary to bootstrap the service with an administrative user, tenant & role (to do so, provide the client with the value of your ``admin_token`` defined in ``keystone.conf`` in addition to the URL of your admin API deployment, typically on port 35357):: export OS_SERVICE_TOKEN=thequickbrownfox-jumpsover-thelazydog export OS_SERVICE_ENDPOINT=http://example.com:35357/v2.0 Since the Identity service can return multiple regions in the service catalog, you can specify the one you want with ``--os-region-name`` (or ``export OS_REGION_NAME``):: export OS_REGION_NAME=north .. WARNING:: If a region is not specified and multiple regions are returned by the Identity service, the client may not access the same region consistently. If you need to connect to a server that is TLS-enabled (the auth URL begins with 'https') and it uses a certificate from a private CA or a self-signed certificate you will need to specify the path to an appropriate CA certificate to use to validate the server certificate with ``--os-cacert`` or an environment variable:: export OS_CACERT=/etc/ssl/my-root-cert.pem Certificate verification can be turned off using ``--insecure``. This should be used with caution. You'll find complete documentation on the shell by running ``keystone help``:: usage: keystone [--version] [--timeout ] [--os-username ] [--os-password ] [--os-tenant-name ] [--os-tenant-id ] [--os-auth-url ] [--os-region-name ] [--os-identity-api-version ] [--os-token ] [--os-endpoint ] [--os-cacert ] [--insecure] [--os-cert ] [--os-key ] [--os-cache] [--force-new-token] [--stale-duration ] ... Command-line interface to the OpenStack Identity API. Positional arguments: catalog ec2-credentials-create Create EC2-compatible credentials for user per tenant ec2-credentials-delete Delete EC2-compatible credentials ec2-credentials-get Display EC2-compatible credentials ec2-credentials-list List EC2-compatible credentials for a user endpoint-create Create a new endpoint associated with a service endpoint-delete Delete a service endpoint endpoint-get endpoint-list List configured service endpoints password-update Update own password role-create Create new role role-delete Delete role role-get Display role details role-list List all roles service-create Add service to Service Catalog service-delete Delete service from Service Catalog service-get Display service from Service Catalog service-list List all services in Service Catalog tenant-create Create new tenant tenant-delete Delete tenant tenant-get Display tenant details tenant-list List all tenants tenant-update Update tenant name, description, enabled status token-get user-create Create new user user-delete Delete user user-get Display user details. user-list List users user-password-update Update user password user-role-add Add role to user user-role-list List roles granted to a user user-role-remove Remove role from user user-update Update user's name, email, and enabled status discover Discover Keystone servers, supported API versions and extensions. bootstrap Grants a new role to a new user on a new tenant, after creating each. bash-completion Prints all of the commands and options to stdout. help Display help about this program or one of its subcommands. Optional arguments: --version Shows the client version and exits --timeout Set request timeout (in seconds) --os-username Name used for authentication with the OpenStack Identity service. Defaults to env[OS_USERNAME] --os-password Password used for authentication with the OpenStack Identity service. Defaults to env[OS_PASSWORD] --os-tenant-name Tenant to request authorization on. Defaults to env[OS_TENANT_NAME] --os-tenant-id Tenant to request authorization on. Defaults to env[OS_TENANT_ID] --os-auth-url Specify the Identity endpoint to use for authentication. Defaults to env[OS_AUTH_URL] --os-region-name Defaults to env[OS_REGION_NAME] --os-identity-api-version Defaults to env[OS_IDENTITY_API_VERSION] or 2.0 --os-token Specify an existing token to use instead of retrieving one via authentication (e.g. with username & password). Defaults to env[OS_SERVICE_TOKEN] --os-endpoint Specify an endpoint to use instead of retrieving one from the service catalog (via authentication). Defaults to env[OS_SERVICE_ENDPOINT] --os-cacert Specify a CA bundle file to use in verifying a TLS (https) server certificate. Defaults to env[OS_CACERT] --insecure Explicitly allow keystoneclient to perform "insecure" TLS (https) requests. The server's certificate will not be verified against any certificate authorities. This option should be used with caution. --os-cert Defaults to env[OS_CERT] --os-key Defaults to env[OS_KEY] --os-cache Use the auth token cache. Defaults to env[OS_CACHE] --force-new-token If the keyring is available and in use, token will always be stored and fetched from the keyring until the token has expired. Use this option to request a new token and replace the existing one in the keyring. --stale-duration Stale duration (in seconds) used to determine whether a token has expired when retrieving it from keyring. This is useful in mitigating process or network delays. Default is 30 seconds. See "keystone help COMMAND" for help on a specific command. Platform: UNKNOWN Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 python-keystoneclient-0.7.1/.testr.conf0000664000175400017540000000025412315030450021314 0ustar jenkinsjenkins00000000000000[DEFAULT] test_command=${PYTHON:-python} -m subunit.run discover -t ./ ./keystoneclient/tests $LISTOPT $IDOPTION test_id_option=--load-list $IDFILE test_list_option=--list python-keystoneclient-0.7.1/LICENSE0000664000175400017540000002717412315030450020245 0ustar jenkinsjenkins00000000000000Copyright (c) 2009 Jacob Kaplan-Moss - initial codebase (< v2.1) Copyright (c) 2011 Rackspace - OpenStack extensions (>= v2.1) Copyright (c) 2011 Nebula, Inc - Keystone refactor (>= v2.7) All rights reserved. Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. --- License for python-keystoneclient versions prior to 2.1 --- All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of this project nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. python-keystoneclient-0.7.1/babel.cfg0000664000175400017540000000002012315030450020743 0ustar jenkinsjenkins00000000000000[python: **.py] python-keystoneclient-0.7.1/requirements.txt0000664000175400017540000000020212315030451022504 0ustar jenkinsjenkins00000000000000argparse Babel>=1.3 iso8601>=0.1.8 netaddr>=0.7.6 oslo.config>=1.2.0 pbr>=0.6,<1.0 PrettyTable>=0.7,<0.8 requests>=1.1 six>=1.5.2 python-keystoneclient-0.7.1/MANIFEST.in0000664000175400017540000000025612315030450020766 0ustar jenkinsjenkins00000000000000include README.rst include AUTHORS HACKING LICENSE include ChangeLog include run_tests.sh tox.ini recursive-include doc * recursive-include tests * recursive-include tools * python-keystoneclient-0.7.1/keystoneclient/0000775000175400017540000000000012315030547022274 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/apiclient/0000775000175400017540000000000012315030547024244 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/apiclient/exceptions.py0000664000175400017540000002707312315030450027001 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 Nebula, Inc. # Copyright 2013 Alessio Ababilov # Copyright 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Exception definitions. """ import itertools class ClientException(Exception): """The base exception class for all exceptions this library raises. """ pass class MissingArgs(ClientException): """Supplied arguments are not sufficient for calling a function.""" def __init__(self, missing): self.missing = missing msg = "Missing argument(s): %s" % ", ".join(missing) super(MissingArgs, self).__init__(msg) class ValidationError(ClientException): """Error in validation on API client side.""" pass class UnsupportedVersion(ClientException): """User is trying to use an unsupported version of the API.""" pass class CommandError(ClientException): """Error in CLI tool.""" pass class AuthorizationFailure(ClientException): """Cannot authorize API client.""" pass class AuthPluginOptionsMissing(AuthorizationFailure): """Auth plugin misses some options.""" def __init__(self, opt_names): super(AuthPluginOptionsMissing, self).__init__( "Authentication failed. Missing options: %s" % ", ".join(opt_names)) self.opt_names = opt_names class AuthSystemNotFound(AuthorizationFailure): """User has specified a AuthSystem that is not installed.""" def __init__(self, auth_system): super(AuthSystemNotFound, self).__init__( "AuthSystemNotFound: %s" % repr(auth_system)) self.auth_system = auth_system class NoUniqueMatch(ClientException): """Multiple entities found instead of one.""" pass class EndpointException(ClientException): """Something is rotten in Service Catalog.""" pass class EndpointNotFound(EndpointException): """Could not find requested endpoint in Service Catalog.""" pass class EmptyCatalog(EndpointNotFound): """The service catalog is empty.""" pass class AmbiguousEndpoints(EndpointException): """Found more than one matching endpoint in Service Catalog.""" def __init__(self, endpoints=None): super(AmbiguousEndpoints, self).__init__( "AmbiguousEndpoints: %s" % repr(endpoints)) self.endpoints = endpoints class HTTPError(ClientException): """The base exception class for all HTTP exceptions. """ http_status = 0 message = "HTTP Error" def __init__(self, message=None, details=None, response=None, request_id=None, url=None, method=None, http_status=None): self.http_status = http_status or self.http_status self.message = message or self.message self.details = details self.request_id = request_id self.response = response self.url = url self.method = method formatted_string = "%(message)s (HTTP %(status)s)" % { "message": self.message, "status": self.http_status} if request_id: formatted_string += " (Request-ID: %s)" % request_id super(HTTPError, self).__init__(formatted_string) class HTTPClientError(HTTPError): """Client-side HTTP error. Exception for cases in which the client seems to have erred. """ message = "HTTP Client Error" class HTTPServerError(HTTPError): """Server-side HTTP error. Exception for cases in which the server is aware that it has erred or is incapable of performing the request. """ message = "HTTP Server Error" class BadRequest(HTTPClientError): """HTTP 400 - Bad Request. The request cannot be fulfilled due to bad syntax. """ http_status = 400 message = "Bad Request" class Unauthorized(HTTPClientError): """HTTP 401 - Unauthorized. Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet been provided. """ http_status = 401 message = "Unauthorized" class PaymentRequired(HTTPClientError): """HTTP 402 - Payment Required. Reserved for future use. """ http_status = 402 message = "Payment Required" class Forbidden(HTTPClientError): """HTTP 403 - Forbidden. The request was a valid request, but the server is refusing to respond to it. """ http_status = 403 message = "Forbidden" class NotFound(HTTPClientError): """HTTP 404 - Not Found. The requested resource could not be found but may be available again in the future. """ http_status = 404 message = "Not Found" class MethodNotAllowed(HTTPClientError): """HTTP 405 - Method Not Allowed. A request was made of a resource using a request method not supported by that resource. """ http_status = 405 message = "Method Not Allowed" class NotAcceptable(HTTPClientError): """HTTP 406 - Not Acceptable. The requested resource is only capable of generating content not acceptable according to the Accept headers sent in the request. """ http_status = 406 message = "Not Acceptable" class ProxyAuthenticationRequired(HTTPClientError): """HTTP 407 - Proxy Authentication Required. The client must first authenticate itself with the proxy. """ http_status = 407 message = "Proxy Authentication Required" class RequestTimeout(HTTPClientError): """HTTP 408 - Request Timeout. The server timed out waiting for the request. """ http_status = 408 message = "Request Timeout" class Conflict(HTTPClientError): """HTTP 409 - Conflict. Indicates that the request could not be processed because of conflict in the request, such as an edit conflict. """ http_status = 409 message = "Conflict" class Gone(HTTPClientError): """HTTP 410 - Gone. Indicates that the resource requested is no longer available and will not be available again. """ http_status = 410 message = "Gone" class LengthRequired(HTTPClientError): """HTTP 411 - Length Required. The request did not specify the length of its content, which is required by the requested resource. """ http_status = 411 message = "Length Required" class PreconditionFailed(HTTPClientError): """HTTP 412 - Precondition Failed. The server does not meet one of the preconditions that the requester put on the request. """ http_status = 412 message = "Precondition Failed" class RequestEntityTooLarge(HTTPClientError): """HTTP 413 - Request Entity Too Large. The request is larger than the server is willing or able to process. """ http_status = 413 message = "Request Entity Too Large" def __init__(self, *args, **kwargs): try: self.retry_after = int(kwargs.pop('retry_after')) except (KeyError, ValueError): self.retry_after = 0 super(RequestEntityTooLarge, self).__init__(*args, **kwargs) class RequestUriTooLong(HTTPClientError): """HTTP 414 - Request-URI Too Long. The URI provided was too long for the server to process. """ http_status = 414 message = "Request-URI Too Long" class UnsupportedMediaType(HTTPClientError): """HTTP 415 - Unsupported Media Type. The request entity has a media type which the server or resource does not support. """ http_status = 415 message = "Unsupported Media Type" class RequestedRangeNotSatisfiable(HTTPClientError): """HTTP 416 - Requested Range Not Satisfiable. The client has asked for a portion of the file, but the server cannot supply that portion. """ http_status = 416 message = "Requested Range Not Satisfiable" class ExpectationFailed(HTTPClientError): """HTTP 417 - Expectation Failed. The server cannot meet the requirements of the Expect request-header field. """ http_status = 417 message = "Expectation Failed" class UnprocessableEntity(HTTPClientError): """HTTP 422 - Unprocessable Entity. The request was well-formed but was unable to be followed due to semantic errors. """ http_status = 422 message = "Unprocessable Entity" class InternalServerError(HTTPServerError): """HTTP 500 - Internal Server Error. A generic error message, given when no more specific message is suitable. """ http_status = 500 message = "Internal Server Error" # NotImplemented is a python keyword. class HTTPNotImplemented(HTTPServerError): """HTTP 501 - Not Implemented. The server either does not recognize the request method, or it lacks the ability to fulfill the request. """ http_status = 501 message = "Not Implemented" class BadGateway(HTTPServerError): """HTTP 502 - Bad Gateway. The server was acting as a gateway or proxy and received an invalid response from the upstream server. """ http_status = 502 message = "Bad Gateway" class ServiceUnavailable(HTTPServerError): """HTTP 503 - Service Unavailable. The server is currently unavailable. """ http_status = 503 message = "Service Unavailable" class GatewayTimeout(HTTPServerError): """HTTP 504 - Gateway Timeout. The server was acting as a gateway or proxy and did not receive a timely response from the upstream server. """ http_status = 504 message = "Gateway Timeout" class HTTPVersionNotSupported(HTTPServerError): """HTTP 505 - HTTPVersion Not Supported. The server does not support the HTTP protocol version used in the request. """ http_status = 505 message = "HTTP Version Not Supported" _code_map = dict( (cls.http_status, cls) for cls in itertools.chain(HTTPClientError.__subclasses__(), HTTPServerError.__subclasses__())) def from_response(response, method, url): """Returns an instance of :class:`HTTPError` or subclass based on response. :param response: instance of `requests.Response` class :param method: HTTP method used for request :param url: URL used for request """ kwargs = { "http_status": response.status_code, "response": response, "method": method, "url": url, "request_id": response.headers.get("x-compute-request-id"), } if "retry-after" in response.headers: kwargs["retry_after"] = response.headers["retry-after"] content_type = response.headers.get("Content-Type", "") if content_type.startswith("application/json"): try: body = response.json() except ValueError: pass else: if hasattr(body, "keys"): error = body[list(body)[0]] kwargs["message"] = error.get("message") kwargs["details"] = error.get("details") elif content_type.startswith("text/"): kwargs["details"] = response.text try: cls = _code_map[response.status_code] except KeyError: if 500 <= response.status_code < 600: cls = HTTPServerError elif 400 <= response.status_code < 500: cls = HTTPClientError else: cls = HTTPError return cls(**kwargs) python-keystoneclient-0.7.1/keystoneclient/apiclient/__init__.py0000664000175400017540000000123512315030450026347 0ustar jenkinsjenkins00000000000000# Copyright 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. __all__ = [ 'exceptions', ] python-keystoneclient-0.7.1/keystoneclient/auth/0000775000175400017540000000000012315030547023235 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/auth/base.py0000664000175400017540000000420212315030450024510 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import six @six.add_metaclass(abc.ABCMeta) class BaseAuthPlugin(object): """The basic structure of an authentication plugin.""" @abc.abstractmethod def get_token(self, session, **kwargs): """Obtain a token. How the token is obtained is up to the plugin. If it is still valid it may be re-used, retrieved from cache or invoke an authentication request against a server. There are no required kwargs. They are passed directly to the auth plugin and they are implementation specific. Returning None will indicate that no token was able to be retrieved. :param session: A session object so the plugin can make HTTP calls. :return string: A token to use. """ def get_endpoint(self, session, **kwargs): """Return an endpoint for the client. There are no required keyword arguments to ``get_endpoint`` as a plugin implementation should use best effort with the information available to determine the endpoint. However there are certain standard options that will be generated by the clients and should be used by plugins: - ``service_type``: what sort of service is required. - ``interface``: what visibility the endpoint should have. - ``region_name``: the region the endpoint exists in. :param Session session: The session object that the auth_plugin belongs to. :returns string: The base URL that will be used to talk to the required service or None if not available. """ python-keystoneclient-0.7.1/keystoneclient/auth/identity/0000775000175400017540000000000012315030547025066 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/auth/identity/v2.py0000664000175400017540000001106612315030450025764 0ustar jenkinsjenkins00000000000000# vim: tabstop=4 shiftwidth=4 softtabstop=4 # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import six from keystoneclient import access from keystoneclient.auth.identity import base from keystoneclient import exceptions from keystoneclient import utils @six.add_metaclass(abc.ABCMeta) class Auth(base.BaseIdentityPlugin): @staticmethod def _factory(auth_url, **kwargs): """Construct a plugin appropriate to your available arguments. This function should only be used for loading authentication from a config file or other source where you do not know the type of plugin that is required. If you know the style of authorization you require then you should construct that plugin directly. :raises NoMatchingPlugin: if a plugin cannot be constructed. return Auth: a plugin that can be passed to a session. """ username = kwargs.pop('username', None) password = kwargs.pop('password', None) token = kwargs.pop('token', None) if token: return Token(auth_url, token, **kwargs) elif username and password: return Password(auth_url, username, password, **kwargs) msg = 'A username and password or token is required.' raise exceptions.NoMatchingPlugin(msg) @utils.positional() def __init__(self, auth_url, trust_id=None, tenant_id=None, tenant_name=None): """Construct an Identity V2 Authentication Plugin. :param string auth_url: Identity service endpoint for authorization. :param string trust_id: Trust ID for trust scoping. :param string tenant_id: Tenant ID for project scoping. :param string tenant_name: Tenant name for project scoping. """ super(Auth, self).__init__(auth_url=auth_url) self.trust_id = trust_id self.tenant_id = tenant_id self.tenant_name = tenant_name def get_auth_ref(self, session, **kwargs): headers = {} url = self.auth_url + '/tokens' params = {'auth': self.get_auth_data(headers)} if self.tenant_id: params['auth']['tenantId'] = self.tenant_id elif self.tenant_name: params['auth']['tenantName'] = self.tenant_name if self.trust_id: params['auth']['trust_id'] = self.trust_id resp = session.post(url, json=params, headers=headers, authenticated=False) return access.AccessInfoV2(**resp.json()['access']) @abc.abstractmethod def get_auth_data(self, headers=None): """Return the authentication section of an auth plugin. :param dict headers: The headers that will be sent with the auth request if a plugin needs to add to them. :return dict: A dict of authentication data for the auth type. """ class Password(Auth): def __init__(self, auth_url, username, password, **kwargs): """A plugin for authenticating with a username and password. :param string auth_url: Identity service endpoint for authorization. :param string username: Username for authentication. :param string password: Password for authentication. """ super(Password, self).__init__(auth_url, **kwargs) self.username = username self.password = password def get_auth_data(self, headers=None): return {'passwordCredentials': {'username': self.username, 'password': self.password}} class Token(Auth): def __init__(self, auth_url, token, **kwargs): """A plugin for authenticating with an existing token. :param string auth_url: Identity service endpoint for authorization. :param string token: Existing token for authentication. """ super(Token, self).__init__(auth_url, **kwargs) self.token = token def get_auth_data(self, headers=None): if headers is not None: headers['X-Auth-Token'] = self.token return {'token': {'id': self.token}} python-keystoneclient-0.7.1/keystoneclient/auth/identity/base.py0000664000175400017540000001074412315030450026351 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import logging import six from keystoneclient.auth import base LOG = logging.getLogger(__name__) @six.add_metaclass(abc.ABCMeta) class BaseIdentityPlugin(base.BaseAuthPlugin): # we count a token as valid if it is valid for at least this many seconds MIN_TOKEN_LIFE_SECONDS = 1 def __init__(self, auth_url=None, username=None, password=None, token=None, trust_id=None): super(BaseIdentityPlugin, self).__init__() self.auth_url = auth_url self.auth_ref = None # NOTE(jamielennox): DEPRECATED. The following should not really be set # here but handled by the individual auth plugin. self.username = username self.password = password self.token = token self.trust_id = trust_id @abc.abstractmethod def get_auth_ref(self, session, **kwargs): """Obtain a token from an OpenStack Identity Service. This method is overridden by the various token version plugins. This function should not be called independently and is expected to be invoked via the do_authenticate function. This function will be invoked if the AcessInfo object cached by the plugin is not valid. Thus plugins should always fetch a new AccessInfo when invoked. If you are looking to just retrieve the current auth data then you should use get_access. :raises HTTPError: An error from an invalid HTTP response. :returns AccessInfo: Token access information. """ def get_token(self, session, **kwargs): """Return a valid auth token. If a valid token is not present then a new one will be fetched. :raises HTTPError: An error from an invalid HTTP response. :return string: A valid token. """ return self.get_access(session).auth_token def get_access(self, session, **kwargs): """Fetch or return a current AccessInfo object. If a valid AccessInfo is present then it is returned otherwise a new one will be fetched. :raises HTTPError: An error from an invalid HTTP response. :returns AccessInfo: Valid AccessInfo """ if (not self.auth_ref or self.auth_ref.will_expire_soon(self.MIN_TOKEN_LIFE_SECONDS)): self.auth_ref = self.get_auth_ref(session) return self.auth_ref def get_endpoint(self, session, service_type=None, interface=None, region_name=None, **kwargs): """Return a valid endpoint for a service. If a valid token is not present then a new one will be fetched using the session and kwargs. :param string service_type: The type of service to lookup the endpoint for. This plugin will return None (failure) if service_type is not provided. :param string interface: The exposure of the endpoint. Should be `public`, `internal` or `admin`. Defaults to `public`. :param string region_name: The region the endpoint should exist in. (optional) :raises HTTPError: An error from an invalid HTTP response. :return string or None: A valid endpoint URL or None if not available. """ if not service_type: LOG.warn('Plugin cannot return an endpoint without knowing the ' 'service type that is required. Add service_type to ' 'endpoint filtering data.') return None if not interface: interface = 'public' service_catalog = self.get_access(session).service_catalog return service_catalog.url_for(service_type=service_type, endpoint_type=interface, region_name=region_name) python-keystoneclient-0.7.1/keystoneclient/auth/identity/v3.py0000664000175400017540000002323012315030450025761 0ustar jenkinsjenkins00000000000000# vim: tabstop=4 shiftwidth=4 softtabstop=4 # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import logging import six from keystoneclient import access from keystoneclient.auth.identity import base from keystoneclient import exceptions from keystoneclient import utils _logger = logging.getLogger(__name__) class Auth(base.BaseIdentityPlugin): @utils.positional() def __init__(self, auth_url, auth_methods, trust_id=None, domain_id=None, domain_name=None, project_id=None, project_name=None, project_domain_id=None, project_domain_name=None): """Construct an Identity V3 Authentication Plugin. :param string auth_url: Identity service endpoint for authentication. :param list auth_methods: A collection of methods to authenticate with. :param string trust_id: Trust ID for trust scoping. :param string domain_id: Domain ID for domain scoping. :param string domain_name: Domain name for domain scoping. :param string project_id: Project ID for project scoping. :param string project_name: Project name for project scoping. :param string project_domain_id: Project's domain ID for project. :param string project_domain_name: Project's domain name for project. """ super(Auth, self).__init__(auth_url=auth_url) self.auth_methods = auth_methods self.trust_id = trust_id self.domain_id = domain_id self.domain_name = domain_name self.project_id = project_id self.project_name = project_name self.project_domain_id = project_domain_id self.project_domain_name = project_domain_name @property def token_url(self): """The full URL where we will send authentication data.""" return '%s/auth/tokens' % self.auth_url.rstrip('/') def get_auth_ref(self, session, **kwargs): headers = {} body = {'auth': {'identity': {}}} ident = body['auth']['identity'] for method in self.auth_methods: name, auth_data = method.get_auth_data(session, self, headers) ident.setdefault('methods', []).append(name) ident[name] = auth_data if not ident: raise exceptions.AuthorizationFailure('Authentication method ' 'required (e.g. password)') mutual_exclusion = [bool(self.domain_id or self.domain_name), bool(self.project_id or self.project_name), bool(self.trust_id)] if sum(mutual_exclusion) > 1: raise exceptions.AuthorizationFailure('Authentication cannot be ' 'scoped to multiple ' 'targets. Pick one of: ' 'project, domain or trust') if self.domain_id: body['auth']['scope'] = {'domain': {'id': self.domain_id}} elif self.domain_name: body['auth']['scope'] = {'domain': {'name': self.domain_name}} elif self.project_id: body['auth']['scope'] = {'project': {'id': self.project_id}} elif self.project_name: scope = body['auth']['scope'] = {'project': {}} scope['project']['name'] = self.project_name if self.project_domain_id: scope['project']['domain'] = {'id': self.project_domain_id} elif self.project_domain_name: scope['project']['domain'] = {'name': self.project_domain_name} elif self.trust_id: body['auth']['scope'] = {'OS-TRUST:trust': {'id': self.trust_id}} resp = session.post(self.token_url, json=body, headers=headers, authenticated=False) return access.AccessInfoV3(resp.headers['X-Subject-Token'], **resp.json()['token']) @staticmethod def _factory(auth_url, **kwargs): """Construct a plugin appropriate to your available arguments. This function is intended as a convenience and backwards compatibility. If you know the style of authorization you require then you should construct that plugin directly. """ methods = [] # NOTE(jamielennox): kwargs extraction is outside the if statement to # clear up additional args that might be passed but not valid for type. method_kwargs = PasswordMethod._extract_kwargs(kwargs) if method_kwargs.get('password'): methods.append(PasswordMethod(**method_kwargs)) method_kwargs = TokenMethod._extract_kwargs(kwargs) if method_kwargs.get('token'): methods.append(TokenMethod(**method_kwargs)) if not methods: msg = 'A username and password or token is required.' raise exceptions.AuthorizationFailure(msg) return Auth(auth_url, methods, **kwargs) @six.add_metaclass(abc.ABCMeta) class AuthMethod(object): """One part of a V3 Authentication strategy. V3 Tokens allow multiple methods to be presented when authentication against the server. Each one of these methods is implemented by an AuthMethod. Note: When implementing an AuthMethod use the method_parameters and do not use positional arguments. Otherwise they can't be picked up by the factory method and don't work as well with AuthConstructors. """ _method_parameters = [] def __init__(self, **kwargs): for param in self._method_parameters: setattr(self, param, kwargs.pop(param, None)) if kwargs: msg = "Unexpected Attributes: %s" % ", ".join(kwargs.keys()) raise AttributeError(msg) @classmethod def _extract_kwargs(cls, kwargs): """Remove parameters related to this method from other kwargs.""" return dict([(p, kwargs.pop(p, None)) for p in cls._method_parameters]) @abc.abstractmethod def get_auth_data(self, session, auth, headers, **kwargs): """Return the authentication section of an auth plugin. :param Session session: The communication session. :param Auth auth: The auth plugin calling the method. :param dict headers: The headers that will be sent with the auth request if a plugin needs to add to them. :return tuple(string, dict): The identifier of this plugin and a dict of authentication data for the auth type. """ @six.add_metaclass(abc.ABCMeta) class _AuthConstructor(Auth): """AuthConstructor is a means of creating an Auth Plugin that contains only one authentication method. This is generally the required usage. An AuthConstructor creates an AuthMethod based on the method's arguments and the auth_method_class defined by the plugin. It then creates the auth plugin with only that authentication method. """ _auth_method_class = None def __init__(self, auth_url, *args, **kwargs): method_kwargs = self._auth_method_class._extract_kwargs(kwargs) method = self._auth_method_class(*args, **method_kwargs) super(_AuthConstructor, self).__init__(auth_url, [method], **kwargs) class PasswordMethod(AuthMethod): _method_parameters = ['user_id', 'username', 'user_domain_id', 'user_domain_name', 'password'] def __init__(self, **kwargs): """Construct a User/Password based authentication method. :param string password: Password for authentication. :param string username: Username for authentication. :param string user_id: User ID for authentication. :param string user_domain_id: User's domain ID for authentication. :param string user_domain_name: User's domain name for authentication. """ super(PasswordMethod, self).__init__(**kwargs) def get_auth_data(self, session, auth, headers, **kwargs): user = {'password': self.password} if self.user_id: user['id'] = self.user_id elif self.username: user['name'] = self.username if self.user_domain_id: user['domain'] = {'id': self.user_domain_id} elif self.user_domain_name: user['domain'] = {'name': self.user_domain_name} return 'password', {'user': user} class Password(_AuthConstructor): _auth_method_class = PasswordMethod class TokenMethod(AuthMethod): _method_parameters = ['token'] def __init__(self, **kwargs): """Construct a Auth plugin to fetch a token from a token. :param string token: Token for authentication. """ super(TokenMethod, self).__init__(**kwargs) def get_auth_data(self, session, auth, headers, **kwargs): headers['X-Auth-Token'] = self.token return 'token', {'id': self.token} class Token(_AuthConstructor): _auth_method_class = TokenMethod def __init__(self, auth_url, token, **kwargs): super(Token, self).__init__(auth_url, token=token, **kwargs) python-keystoneclient-0.7.1/keystoneclient/auth/identity/__init__.py0000664000175400017540000000000012315030450027156 0ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/auth/__init__.py0000664000175400017540000000000012315030450025325 0ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/auth/token_endpoint.py0000664000175400017540000000214012315030450026615 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from keystoneclient.auth import base class Token(base.BaseAuthPlugin): """A provider that will always use the given token and endpoint. This is really only useful for testing and in certain CLI cases where you have a known endpoint and admin token that you want to use. """ def __init__(self, endpoint, token): # NOTE(jamielennox): endpoint is reserved for when plugins # can be used to provide that information self.endpoint = endpoint self.token = token def get_token(self, session): return self.token python-keystoneclient-0.7.1/keystoneclient/v3/0000775000175400017540000000000012315030547022624 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/v3/roles.py0000664000175400017540000001270512315030451024321 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # Copyright 2011 Nebula, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from keystoneclient import base from keystoneclient import exceptions from keystoneclient import utils class Role(base.Resource): """Represents an Identity role. Attributes: * id: a uuid that identifies the role * name: user-facing identifier """ pass class RoleManager(base.CrudManager): """Manager class for manipulating Identity roles.""" resource_class = Role collection_key = 'roles' key = 'role' def _role_grants_base_url(self, user, group, domain, project): # When called, we have already checked that only one of user & group # and one of domain & project have been specified params = {} if project: params['project_id'] = base.getid(project) base_url = '/projects/%(project_id)s' elif domain: params['domain_id'] = base.getid(domain) base_url = '/domains/%(domain_id)s' if user: params['user_id'] = base.getid(user) base_url += '/users/%(user_id)s' elif group: params['group_id'] = base.getid(group) base_url += '/groups/%(group_id)s' return base_url % params def _require_domain_xor_project(self, domain, project): if domain and project: msg = 'Specify either a domain or project, not both' raise exceptions.ValidationError(msg) elif not (domain or project): msg = 'Must specify either a domain or project' raise exceptions.ValidationError(msg) def _require_user_xor_group(self, user, group): if user and group: msg = 'Specify either a user or group, not both' raise exceptions.ValidationError(msg) elif not (user or group): msg = 'Must specify either a user or group' raise exceptions.ValidationError(msg) @utils.positional(1, enforcement=utils.positional.WARN) def create(self, name, **kwargs): return super(RoleManager, self).create( name=name, **kwargs) def get(self, role): return super(RoleManager, self).get( role_id=base.getid(role)) @utils.positional(enforcement=utils.positional.WARN) def list(self, user=None, group=None, domain=None, project=None, **kwargs): """Lists roles and role grants. If no arguments are provided, all roles in the system will be listed. If a user or group is specified, you must also specify either a domain or project to list role grants on that pair. And if ``**kwargs`` are provided, then also filter roles with attributes matching ``**kwargs``. """ if user or group: self._require_user_xor_group(user, group) self._require_domain_xor_project(domain, project) return super(RoleManager, self).list( base_url=self._role_grants_base_url(user, group, domain, project), **kwargs) return super(RoleManager, self).list(**kwargs) @utils.positional(enforcement=utils.positional.WARN) def update(self, role, name=None, **kwargs): return super(RoleManager, self).update( role_id=base.getid(role), name=name, **kwargs) def delete(self, role): return super(RoleManager, self).delete( role_id=base.getid(role)) @utils.positional(enforcement=utils.positional.WARN) def grant(self, role, user=None, group=None, domain=None, project=None): """Grants a role to a user or group on a domain or project.""" self._require_domain_xor_project(domain, project) self._require_user_xor_group(user, group) return super(RoleManager, self).put( base_url=self._role_grants_base_url(user, group, domain, project), role_id=base.getid(role)) @utils.positional(enforcement=utils.positional.WARN) def check(self, role, user=None, group=None, domain=None, project=None): """Checks if a user or group has a role on a domain or project.""" self._require_domain_xor_project(domain, project) self._require_user_xor_group(user, group) return super(RoleManager, self).head( base_url=self._role_grants_base_url(user, group, domain, project), role_id=base.getid(role)) @utils.positional(enforcement=utils.positional.WARN) def revoke(self, role, user=None, group=None, domain=None, project=None): """Revokes a role from a user or group on a domain or project.""" self._require_domain_xor_project(domain, project) self._require_user_xor_group(user, group) return super(RoleManager, self).delete( base_url=self._role_grants_base_url(user, group, domain, project), role_id=base.getid(role)) python-keystoneclient-0.7.1/keystoneclient/v3/endpoints.py0000664000175400017540000000664612315030451025207 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # Copyright 2011 Nebula, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from keystoneclient import base from keystoneclient import exceptions from keystoneclient import utils VALID_INTERFACES = ['public', 'admin', 'internal'] class Endpoint(base.Resource): """Represents an Identity endpoint. Attributes: * id: a uuid that identifies the endpoint * interface: 'public', 'admin' or 'internal' network interface * region: geographic location of the endpoint * service_id: service to which the endpoint belongs * url: fully qualified service endpoint * enabled: determines whether the endpoint appears in the catalog """ pass class EndpointManager(base.CrudManager): """Manager class for manipulating Identity endpoints.""" resource_class = Endpoint collection_key = 'endpoints' key = 'endpoint' def _validate_interface(self, interface): if interface is not None and interface not in VALID_INTERFACES: msg = '"interface" must be one of: %s' msg = msg % ', '.join(VALID_INTERFACES) raise exceptions.ValidationError(msg) @utils.positional(1, enforcement=utils.positional.WARN) def create(self, service, url, interface=None, region=None, enabled=True, **kwargs): self._validate_interface(interface) return super(EndpointManager, self).create( service_id=base.getid(service), interface=interface, url=url, region=region, enabled=enabled, **kwargs) def get(self, endpoint): return super(EndpointManager, self).get( endpoint_id=base.getid(endpoint)) @utils.positional(enforcement=utils.positional.WARN) def list(self, service=None, interface=None, region=None, enabled=None, **kwargs): """List endpoints. If ``**kwargs`` are provided, then filter endpoints with attributes matching ``**kwargs``. """ self._validate_interface(interface) return super(EndpointManager, self).list( service_id=base.getid(service), interface=interface, region=region, enabled=enabled, **kwargs) @utils.positional(enforcement=utils.positional.WARN) def update(self, endpoint, service=None, url=None, interface=None, region=None, enabled=None, **kwargs): self._validate_interface(interface) return super(EndpointManager, self).update( endpoint_id=base.getid(endpoint), service_id=base.getid(service), interface=interface, url=url, region=region, enabled=enabled, **kwargs) def delete(self, endpoint): return super(EndpointManager, self).delete( endpoint_id=base.getid(endpoint)) python-keystoneclient-0.7.1/keystoneclient/v3/policies.py0000664000175400017540000000477512315030451025014 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # Copyright 2011 Nebula, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from keystoneclient import base from keystoneclient import utils class Policy(base.Resource): """Represents an Identity policy. Attributes: * id: a uuid that identifies the policy * blob: a policy document (blob) * type: the mime type of the policy blob """ @utils.positional(enforcement=utils.positional.WARN) def update(self, blob=None, type=None): kwargs = { 'blob': blob if blob is not None else self.blob, 'type': type if type is not None else self.type, } try: retval = self.manager.update(self.id, **kwargs) self = retval except Exception: retval = None return retval class PolicyManager(base.CrudManager): """Manager class for manipulating Identity policies.""" resource_class = Policy collection_key = 'policies' key = 'policy' @utils.positional(1, enforcement=utils.positional.WARN) def create(self, blob, type='application/json', **kwargs): return super(PolicyManager, self).create( blob=blob, type=type, **kwargs) def get(self, policy): return super(PolicyManager, self).get( policy_id=base.getid(policy)) def list(self, **kwargs): """List policies. ``**kwargs`` allows filter criteria to be passed where supported by the server. """ return super(PolicyManager, self).list(**kwargs) @utils.positional(enforcement=utils.positional.WARN) def update(self, entity, blob=None, type=None, **kwargs): return super(PolicyManager, self).update( policy_id=base.getid(entity), blob=blob, type=type, **kwargs) def delete(self, policy): return super(PolicyManager, self).delete( policy_id=base.getid(policy)) python-keystoneclient-0.7.1/keystoneclient/v3/groups.py0000664000175400017540000000577112315030451024521 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # Copyright 2011 Nebula, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from keystoneclient import base from keystoneclient import utils class Group(base.Resource): """Represents an Identity user group. Attributes: * id: a uuid that identifies the group * name: group name * description: group description """ @utils.positional(enforcement=utils.positional.WARN) def update(self, name=None, description=None): kwargs = { 'name': name if name is not None else self.name, 'description': (description if description is not None else self.description), } try: retval = self.manager.update(self.id, **kwargs) self = retval except Exception: retval = None return retval class GroupManager(base.CrudManager): """Manager class for manipulating Identity groups.""" resource_class = Group collection_key = 'groups' key = 'group' @utils.positional(1, enforcement=utils.positional.WARN) def create(self, name, domain=None, description=None, **kwargs): return super(GroupManager, self).create( name=name, domain_id=base.getid(domain), description=description, **kwargs) @utils.positional(enforcement=utils.positional.WARN) def list(self, user=None, domain=None, **kwargs): """List groups. If domain or user is provided, then filter groups with that attribute. If ``**kwargs`` are provided, then filter groups with attributes matching ``**kwargs``. """ if user: base_url = '/users/%s' % base.getid(user) else: base_url = None return super(GroupManager, self).list( base_url=base_url, domain_id=base.getid(domain), **kwargs) def get(self, group): return super(GroupManager, self).get( group_id=base.getid(group)) @utils.positional(enforcement=utils.positional.WARN) def update(self, group, name=None, description=None, **kwargs): return super(GroupManager, self).update( group_id=base.getid(group), name=name, description=description, **kwargs) def delete(self, group): return super(GroupManager, self).delete( group_id=base.getid(group)) python-keystoneclient-0.7.1/keystoneclient/v3/domains.py0000664000175400017540000000450212315030451024623 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # Copyright 2011 Nebula, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from keystoneclient import base from keystoneclient import utils class Domain(base.Resource): """Represents an Identity domain. Attributes: * id: a uuid that identifies the domain """ pass class DomainManager(base.CrudManager): """Manager class for manipulating Identity domains.""" resource_class = Domain collection_key = 'domains' key = 'domain' @utils.positional(1, enforcement=utils.positional.WARN) def create(self, name, description=None, enabled=True, **kwargs): return super(DomainManager, self).create( name=name, description=description, enabled=enabled, **kwargs) def get(self, domain): return super(DomainManager, self).get( domain_id=base.getid(domain)) def list(self, **kwargs): """List domains. ``**kwargs`` allows filter criteria to be passed where supported by the server. """ # Ref bug #1267530 we have to pass 0 for False to get the expected # results on all keystone versions if kwargs.get('enabled') is False: kwargs['enabled'] = 0 return super(DomainManager, self).list(**kwargs) @utils.positional(enforcement=utils.positional.WARN) def update(self, domain, name=None, description=None, enabled=True, **kwargs): return super(DomainManager, self).update( domain_id=base.getid(domain), name=name, description=description, enabled=enabled, **kwargs) def delete(self, domain): return super(DomainManager, self).delete( domain_id=base.getid(domain)) python-keystoneclient-0.7.1/keystoneclient/v3/users.py0000664000175400017540000001412112315030451024330 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # Copyright 2011 Nebula, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging from keystoneclient import base from keystoneclient import exceptions from keystoneclient import utils LOG = logging.getLogger(__name__) class User(base.Resource): """Represents an Identity user. Attributes: * id: a uuid that identifies the user """ pass class UserManager(base.CrudManager): """Manager class for manipulating Identity users.""" resource_class = User collection_key = 'users' key = 'user' def _require_user_and_group(self, user, group): if not (user and group): msg = 'Specify both a user and a group' raise exceptions.ValidationError(msg) @utils.positional(1, enforcement=utils.positional.WARN) def create(self, name, domain=None, project=None, password=None, email=None, description=None, enabled=True, default_project=None, **kwargs): """Create a user. .. warning:: The project argument is deprecated, use default_project instead. If both default_project and project is provided, the default_project will be used. """ if project: LOG.warning("The project argument is deprecated, " "use default_project instead.") default_project_id = base.getid(default_project) or base.getid(project) return super(UserManager, self).create( name=name, domain_id=base.getid(domain), default_project_id=default_project_id, password=password, email=email, description=description, enabled=enabled, **kwargs) @utils.positional(enforcement=utils.positional.WARN) def list(self, project=None, domain=None, group=None, default_project=None, **kwargs): """List users. If project, domain or group are provided, then filter users with those attributes. If ``**kwargs`` are provided, then filter users with attributes matching ``**kwargs``. .. warning:: The project argument is deprecated, use default_project instead. If both default_project and project is provided, the default_project will be used. """ if project: LOG.warning("The project argument is deprecated, " "use default_project instead.") default_project_id = base.getid(default_project) or base.getid(project) if group: base_url = '/groups/%s' % base.getid(group) else: base_url = None return super(UserManager, self).list( base_url=base_url, domain_id=base.getid(domain), default_project_id=default_project_id, **kwargs) def get(self, user): return super(UserManager, self).get( user_id=base.getid(user)) @utils.positional(enforcement=utils.positional.WARN) def update(self, user, name=None, domain=None, project=None, password=None, email=None, description=None, enabled=None, default_project=None, **kwargs): """Update a user. .. warning:: The project argument is deprecated, use default_project instead. If both default_project and project is provided, the default_project will be used. """ if project: LOG.warning("The project argument is deprecated, " "use default_project instead.") default_project_id = base.getid(default_project) or base.getid(project) return super(UserManager, self).update( user_id=base.getid(user), name=name, domain_id=base.getid(domain), default_project_id=default_project_id, password=password, email=email, description=description, enabled=enabled, **kwargs) def update_password(self, old_password, new_password): """Update the password for the user the token belongs to.""" if not (old_password and new_password): msg = 'Specify both the current password and a new password' raise exceptions.ValidationError(msg) if old_password == new_password: msg = 'Old password and new password must be different.' raise exceptions.ValidationError(msg) params = {'user': {'password': new_password, 'original_password': old_password}} base_url = '/users/%s/password' % self.api.user_id return self._update(base_url, params, method='POST', management=False) def add_to_group(self, user, group): self._require_user_and_group(user, group) base_url = '/groups/%s' % base.getid(group) return super(UserManager, self).put( base_url=base_url, user_id=base.getid(user)) def check_in_group(self, user, group): self._require_user_and_group(user, group) base_url = '/groups/%s' % base.getid(group) return super(UserManager, self).head( base_url=base_url, user_id=base.getid(user)) def remove_from_group(self, user, group): self._require_user_and_group(user, group) base_url = '/groups/%s' % base.getid(group) return super(UserManager, self).delete( base_url=base_url, user_id=base.getid(user)) def delete(self, user): return super(UserManager, self).delete( user_id=base.getid(user)) python-keystoneclient-0.7.1/keystoneclient/v3/contrib/0000775000175400017540000000000012315030547024264 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/v3/contrib/trusts.py0000664000175400017540000000645012315030451026201 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from keystoneclient import base from keystoneclient import exceptions from keystoneclient.openstack.common import timeutils class Trust(base.Resource): """Represents a Trust. Attributes: * id: a uuid that identifies the trust * impersonation: allow explicit impersonation * project_id: project ID * trustee_user_id: a uuid that identifies the trustee * trustor_user_id: a uuid that identifies the trustor """ pass class TrustManager(base.CrudManager): """Manager class for manipulating Trusts.""" resource_class = Trust collection_key = 'trusts' key = 'trust' base_url = '/OS-TRUST' def create(self, trustee_user, trustor_user, role_names=None, project=None, impersonation=False, expires_at=None, **kwargs): """Create a Trust. :param string trustee_user: user who is capable of consuming the trust :param string trustor_user: user who's authorization is being delegated :param string role_names: subset of trustor's roles to be granted :param string project: project which the trustor is delegating :param boolean impersonation: enable explicit impersonation :param datetime.datetime expires_at: expiry time """ # Convert role_names list into list-of-dict API format if role_names: roles = [{'name': n} for n in role_names] else: roles = None # Convert datetime.datetime expires_at to iso format string if expires_at: expires_str = timeutils.isotime(at=expires_at, subsecond=True) else: expires_str = None return super(TrustManager, self).create( expires_at=expires_str, impersonation=impersonation, project_id=base.getid(project), roles=roles, trustee_user_id=base.getid(trustee_user), trustor_user_id=base.getid(trustor_user), **kwargs) def update(self): raise exceptions.HTTPNotImplemented("Update not supported for trusts") def list(self, trustee_user=None, trustor_user=None, **kwargs): """List Trusts.""" trustee_user_id = base.getid(trustee_user) trustor_user_id = base.getid(trustor_user) return super(TrustManager, self).list(trustee_user_id=trustee_user_id, trustor_user_id=trustor_user_id, **kwargs) def get(self, trust): """Get a specific trust.""" return super(TrustManager, self).get(trust_id=base.getid(trust)) def delete(self, trust): """Delete a trust.""" return super(TrustManager, self).delete(trust_id=base.getid(trust)) python-keystoneclient-0.7.1/keystoneclient/v3/contrib/__init__.py0000664000175400017540000000001712315030451026365 0ustar jenkinsjenkins00000000000000 __all__ = [ ] python-keystoneclient-0.7.1/keystoneclient/v3/projects.py0000664000175400017540000000645312315030451025031 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # Copyright 2011 Nebula, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from keystoneclient import base from keystoneclient import utils class Project(base.Resource): """Represents an Identity project. Attributes: * id: a uuid that identifies the project * name: project name * description: project description * enabled: boolean to indicate if project is enabled """ @utils.positional(enforcement=utils.positional.WARN) def update(self, name=None, description=None, enabled=None): kwargs = { 'name': name if name is not None else self.name, 'description': (description if description is not None else self.description), 'enabled': enabled if enabled is not None else self.enabled, } try: retval = self.manager.update(self.id, **kwargs) self = retval except Exception: retval = None return retval class ProjectManager(base.CrudManager): """Manager class for manipulating Identity projects.""" resource_class = Project collection_key = 'projects' key = 'project' @utils.positional(1, enforcement=utils.positional.WARN) def create(self, name, domain, description=None, enabled=True, **kwargs): return super(ProjectManager, self).create( domain_id=base.getid(domain), name=name, description=description, enabled=enabled, **kwargs) @utils.positional(enforcement=utils.positional.WARN) def list(self, domain=None, user=None, **kwargs): """List projects. If domain or user are provided, then filter projects with those attributes. If ``**kwargs`` are provided, then filter projects with attributes matching ``**kwargs``. """ base_url = '/users/%s' % base.getid(user) if user else None return super(ProjectManager, self).list( base_url=base_url, domain_id=base.getid(domain), **kwargs) def get(self, project): return super(ProjectManager, self).get( project_id=base.getid(project)) @utils.positional(enforcement=utils.positional.WARN) def update(self, project, name=None, domain=None, description=None, enabled=None, **kwargs): return super(ProjectManager, self).update( project_id=base.getid(project), domain_id=base.getid(domain), name=name, description=description, enabled=enabled, **kwargs) def delete(self, project): return super(ProjectManager, self).delete( project_id=base.getid(project)) python-keystoneclient-0.7.1/keystoneclient/v3/client.py0000664000175400017540000002022512315030451024447 0ustar jenkinsjenkins00000000000000# Copyright 2011 Nebula, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging from keystoneclient.auth.identity import v3 as v3_auth from keystoneclient import exceptions from keystoneclient import httpclient from keystoneclient.openstack.common import jsonutils from keystoneclient.v3.contrib import trusts from keystoneclient.v3 import credentials from keystoneclient.v3 import domains from keystoneclient.v3 import endpoints from keystoneclient.v3 import groups from keystoneclient.v3 import policies from keystoneclient.v3 import projects from keystoneclient.v3 import roles from keystoneclient.v3 import services from keystoneclient.v3 import users _logger = logging.getLogger(__name__) class Client(httpclient.HTTPClient): """Client for the OpenStack Identity API v3. :param string user_id: User ID for authentication. (optional) :param string username: Username for authentication. (optional) :param string user_domain_id: User's domain ID for authentication. (optional) :param string user_domain_name: User's domain name for authentication. (optional) :param string password: Password for authentication. (optional) :param string token: Token for authentication. (optional) :param string domain_id: Domain ID for domain scoping. (optional) :param string domain_name: Domain name for domain scoping. (optional) :param string project_id: Project ID for project scoping. (optional) :param string project_name: Project name for project scoping. (optional) :param string project_domain_id: Project's domain ID for project scoping. (optional) :param string project_domain_name: Project's domain name for project scoping. (optional) :param string tenant_name: Tenant name. (optional) The tenant_name keyword argument is deprecated, use project_name instead. :param string tenant_id: Tenant id. (optional) The tenant_id keyword argument is deprecated, use project_id instead. :param string auth_url: Identity service endpoint for authorization. :param string region_name: Name of a region to select when choosing an endpoint from the service catalog. :param string endpoint: A user-supplied endpoint URL for the identity service. Lazy-authentication is possible for API service calls if endpoint is set at instantiation. (optional) :param integer timeout: Allows customization of the timeout for client http requests. (optional) Example:: >>> from keystoneclient.v3 import client >>> keystone = client.Client(user_domain_name=DOMAIN_NAME, ... username=USER, ... password=PASS, ... project_domain_name=PROJECT_DOMAIN_NAME, ... project_name=PROJECT_NAME, ... auth_url=KEYSTONE_URL) ... >>> keystone.projects.list() ... >>> user = keystone.users.get(USER_ID) >>> user.delete() """ version = 'v3' def __init__(self, **kwargs): """Initialize a new client for the Keystone v3 API.""" super(Client, self).__init__(**kwargs) self.credentials = credentials.CredentialManager(self) self.endpoints = endpoints.EndpointManager(self) self.domains = domains.DomainManager(self) self.groups = groups.GroupManager(self) self.policies = policies.PolicyManager(self) self.projects = projects.ProjectManager(self) self.roles = roles.RoleManager(self) self.services = services.ServiceManager(self) self.users = users.UserManager(self) self.trusts = trusts.TrustManager(self) # DEPRECATED: if session is passed then we go to the new behaviour of # authenticating on the first required call. if 'session' not in kwargs and self.management_url is None: self.authenticate() def serialize(self, entity): return jsonutils.dumps(entity, sort_keys=True) def process_token(self, **kwargs): """Extract and process information from the new auth_ref. And set the relevant authentication information. """ super(Client, self).process_token(**kwargs) if self.auth_ref.domain_scoped: if not self.auth_ref.domain_id: raise exceptions.AuthorizationFailure( "Token didn't provide domain_id") self._process_management_url(kwargs.get('region_name')) self.domain_name = self.auth_ref.domain_name self.domain_id = self.auth_ref.domain_id if self._management_url: self._management_url = self._management_url.replace('/v2.0', '/v3') def get_raw_token_from_identity_service(self, auth_url, user_id=None, username=None, user_domain_id=None, user_domain_name=None, password=None, domain_id=None, domain_name=None, project_id=None, project_name=None, project_domain_id=None, project_domain_name=None, token=None, trust_id=None, **kwargs): """Authenticate against the v3 Identity API. :returns: access.AccessInfo if authentication was successful. :raises: AuthorizationFailure if unable to authenticate or validate the existing authorization token :raises: Unauthorized if authentication fails due to invalid token """ try: if auth_url is None: raise ValueError("Cannot authenticate without an auth_url") a = v3_auth.Auth._factory(auth_url, username=username, password=password, token=token, trust_id=trust_id, user_id=user_id, domain_id=domain_id, domain_name=domain_name, user_domain_id=user_domain_id, user_domain_name=user_domain_name, project_id=project_id, project_name=project_name, project_domain_id=project_domain_id, project_domain_name=project_domain_name) return a.get_auth_ref(self.session) except (exceptions.AuthorizationFailure, exceptions.Unauthorized): _logger.debug('Authorization failed.') raise except exceptions.EndpointNotFound: msg = 'There was no suitable authentication url for this request' raise exceptions.AuthorizationFailure(msg) except Exception as e: raise exceptions.AuthorizationFailure('Authorization failed: ' '%s' % e) python-keystoneclient-0.7.1/keystoneclient/v3/credentials.py0000664000175400017540000000570012315030451025467 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # Copyright 2011 Nebula, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from keystoneclient import base from keystoneclient import utils class Credential(base.Resource): """Represents an Identity credential. Attributes: * id: a uuid that identifies the credential """ pass class CredentialManager(base.CrudManager): """Manager class for manipulating Identity credentials.""" resource_class = Credential collection_key = 'credentials' key = 'credential' def _get_data_blob(self, blob, data): # Ref bug #1259461, the <= 0.4.1 keystoneclient calling convention was # to pass "data", but the underlying API expects "blob", so # support both in the python API for backwards compatibility if blob is not None: return blob elif data is not None: # FIXME(shardy): Passing data is deprecated. Provide an # appropriate warning. return data else: raise ValueError( "Credential requires blob to be specified") @utils.positional(1, enforcement=utils.positional.WARN) def create(self, user, type, blob=None, data=None, project=None, **kwargs): return super(CredentialManager, self).create( user_id=base.getid(user), type=type, blob=self._get_data_blob(blob, data), project_id=base.getid(project), **kwargs) def get(self, credential): return super(CredentialManager, self).get( credential_id=base.getid(credential)) def list(self, **kwargs): """List credentials. If ``**kwargs`` are provided, then filter credentials with attributes matching ``**kwargs``. """ return super(CredentialManager, self).list(**kwargs) @utils.positional(2, enforcement=utils.positional.WARN) def update(self, credential, user, type=None, blob=None, data=None, project=None, **kwargs): return super(CredentialManager, self).update( credential_id=base.getid(credential), user_id=base.getid(user), type=type, blob=self._get_data_blob(blob, data), project_id=base.getid(project), **kwargs) def delete(self, credential): return super(CredentialManager, self).delete( credential_id=base.getid(credential)) python-keystoneclient-0.7.1/keystoneclient/v3/__init__.py0000664000175400017540000000013112315030451024722 0ustar jenkinsjenkins00000000000000# flake8: noqa from keystoneclient.v3.client import Client __all__ = [ 'client', ] python-keystoneclient-0.7.1/keystoneclient/v3/services.py0000664000175400017540000000405312315030451025015 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # Copyright 2011 Nebula, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from keystoneclient import base from keystoneclient import utils class Service(base.Resource): """Represents an Identity service. Attributes: * id: a uuid that identifies the service * name: user-facing name of the service (e.g. Keystone) * type: 'compute', 'identity', etc * enabled: determines whether the service appears in the catalog """ pass class ServiceManager(base.CrudManager): """Manager class for manipulating Identity services.""" resource_class = Service collection_key = 'services' key = 'service' @utils.positional(1, enforcement=utils.positional.WARN) def create(self, name, type, enabled=True, **kwargs): return super(ServiceManager, self).create( name=name, type=type, enabled=enabled, **kwargs) def get(self, service): return super(ServiceManager, self).get( service_id=base.getid(service)) @utils.positional(enforcement=utils.positional.WARN) def update(self, service, name=None, type=None, enabled=None, **kwargs): return super(ServiceManager, self).update( service_id=base.getid(service), name=name, type=type, enabled=enabled, **kwargs) def delete(self, service): return super(ServiceManager, self).delete( service_id=base.getid(service)) python-keystoneclient-0.7.1/keystoneclient/common/0000775000175400017540000000000012315030547023564 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/common/cms.py0000664000175400017540000002240412315030450024713 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """Certificate signing functions. Call set_subprocess() with the subprocess module. Either Python's subprocess or eventlet.green.subprocess can be used. If set_subprocess() is not called, this module will pick Python's subprocess or eventlet.green.subprocess based on if os module is patched by eventlet. """ import errno import hashlib import logging import six from keystoneclient import exceptions subprocess = None LOG = logging.getLogger(__name__) PKI_ANS1_PREFIX = 'MII' def _ensure_subprocess(): # NOTE(vish): late loading subprocess so we can # use the green version if we are in # eventlet. global subprocess if not subprocess: try: from eventlet import patcher if patcher.already_patched: from eventlet.green import subprocess else: import subprocess except ImportError: import subprocess # noqa def set_subprocess(_subprocess=None): """Set subprocess module to use. The subprocess could be eventlet.green.subprocess if using eventlet, or Python's subprocess otherwise. """ global subprocess subprocess = _subprocess def _check_files_accessible(files): err = None try: for try_file in files: with open(try_file, 'r'): pass except IOError as e: # Catching IOError means there is an issue with # the given file. err = ('Hit OSError in _process_communicate_handle_oserror()\n' 'Likely due to %s: %s') % (try_file, e.strerror) return err def _process_communicate_handle_oserror(process, text, files): """Wrapper around process.communicate that checks for OSError.""" try: output, err = process.communicate(text) except OSError as e: if e.errno != errno.EPIPE: raise # OSError with EPIPE only occurs with Python 2.6.x/old 2.7.x # http://bugs.python.org/issue10963 # The quick exit is typically caused by the openssl command not being # able to read an input file, so check ourselves if can't read a file. err = _check_files_accessible(files) if process.stderr: err += process.stderr.read() output = "" retcode = -1 else: retcode = process.poll() return output, err, retcode def cms_verify(formatted, signing_cert_file_name, ca_file_name): """Verifies the signature of the contents IAW CMS syntax. :raises: subprocess.CalledProcessError :raises: CertificateConfigError if certificate is not configured properly. """ _ensure_subprocess() process = subprocess.Popen(["openssl", "cms", "-verify", "-certfile", signing_cert_file_name, "-CAfile", ca_file_name, "-inform", "PEM", "-nosmimecap", "-nodetach", "-nocerts", "-noattr"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) output, err, retcode = _process_communicate_handle_oserror( process, formatted, (signing_cert_file_name, ca_file_name)) # Do not log errors, as some happen in the positive thread # instead, catch them in the calling code and log them there. # When invoke the openssl with not exist file, return code 2 # and error msg will be returned. # You can get more from # http://www.openssl.org/docs/apps/cms.html#EXIT_CODES # # $ openssl cms -verify -certfile not_exist_file -CAfile \ # not_exist_file -inform PEM -nosmimecap -nodetach \ # -nocerts -noattr # Error opening certificate file not_exist_file # if retcode == 2: raise exceptions.CertificateConfigError(err) elif retcode: # NOTE(dmllr): Python 2.6 compatibility: # CalledProcessError did not have output keyword argument e = subprocess.CalledProcessError(retcode, "openssl") e.output = err raise e return output def token_to_cms(signed_text): copy_of_text = signed_text.replace('-', '/') formatted = "-----BEGIN CMS-----\n" line_length = 64 while len(copy_of_text) > 0: if (len(copy_of_text) > line_length): formatted += copy_of_text[:line_length] copy_of_text = copy_of_text[line_length:] else: formatted += copy_of_text copy_of_text = "" formatted += "\n" formatted += "-----END CMS-----\n" return formatted def verify_token(token, signing_cert_file_name, ca_file_name): return cms_verify(token_to_cms(token), signing_cert_file_name, ca_file_name) def is_ans1_token(token): """Determine if a token appears to be PKI-based. thx to ayoung for sorting this out. base64 decoded hex representation of MII is 3082:: In [3]: binascii.hexlify(base64.b64decode('MII=')) Out[3]: '3082' re: http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf :: pg4: For tags from 0 to 30 the first octet is the identfier pg10: Hex 30 means sequence, followed by the length of that sequence. pg5: Second octet is the length octet first bit indicates short or long form, next 7 bits encode the number of subsequent octets that make up the content length octets as an unsigned binary int 82 = 10000010 (first bit indicates long form) 0000010 = 2 octets of content length so read the next 2 octets to get the length of the content. In the case of a very large content length there could be a requirement to have more than 2 octets to designate the content length, therefore requiring us to check for MIM, MIQ, etc. :: In [4]: base64.b64encode(binascii.a2b_hex('3083')) Out[4]: 'MIM=' In [5]: base64.b64encode(binascii.a2b_hex('3084')) Out[5]: 'MIQ=' Checking for MI would become invalid at 16 octets of content length 10010000 = 90 In [6]: base64.b64encode(binascii.a2b_hex('3090')) Out[6]: 'MJA=' Checking for just M is insufficient But we will only check for MII: Max length of the content using 2 octets is 7FFF or 32767. It's not practical to support a token of this length or greater in http therefore, we will check for MII only and ignore the case of larger tokens """ return token[:3] == PKI_ANS1_PREFIX def cms_sign_text(text, signing_cert_file_name, signing_key_file_name): """Uses OpenSSL to sign a document. Produces a Base64 encoding of a DER formatted CMS Document http://en.wikipedia.org/wiki/Cryptographic_Message_Syntax """ _ensure_subprocess() process = subprocess.Popen(["openssl", "cms", "-sign", "-signer", signing_cert_file_name, "-inkey", signing_key_file_name, "-outform", "PEM", "-nosmimecap", "-nodetach", "-nocerts", "-noattr"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) output, err, retcode = _process_communicate_handle_oserror( process, text, (signing_cert_file_name, signing_key_file_name)) if retcode or "Error" in err: LOG.error('Signing error: %s' % err) raise subprocess.CalledProcessError(retcode, "openssl") return output def cms_sign_token(text, signing_cert_file_name, signing_key_file_name): output = cms_sign_text(text, signing_cert_file_name, signing_key_file_name) return cms_to_token(output) def cms_to_token(cms_text): start_delim = "-----BEGIN CMS-----" end_delim = "-----END CMS-----" signed_text = cms_text signed_text = signed_text.replace('/', '-') signed_text = signed_text.replace(start_delim, '') signed_text = signed_text.replace(end_delim, '') signed_text = signed_text.replace('\n', '') return signed_text def cms_hash_token(token_id): """Hash PKI tokens. return: for ans1_token, returns the hash of the passed in token otherwise, returns what it was passed in. """ if token_id is None: return None if is_ans1_token(token_id): hasher = hashlib.md5() if isinstance(token_id, six.text_type): token_id = token_id.encode('utf-8') hasher.update(token_id) return hasher.hexdigest() else: return token_id python-keystoneclient-0.7.1/keystoneclient/common/__init__.py0000664000175400017540000000000012315030450025654 0ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/middleware/0000775000175400017540000000000012315030547024411 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/middleware/auth_token.py0000664000175400017540000015650612315030450027132 0ustar jenkinsjenkins00000000000000# Copyright 2010-2012 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. """ TOKEN-BASED AUTH MIDDLEWARE This WSGI component: * Verifies that incoming client requests have valid tokens by validating tokens with the auth service. * Rejects unauthenticated requests UNLESS it is in 'delay_auth_decision' mode, which means the final decision is delegated to the downstream WSGI component (usually the OpenStack service) * Collects and forwards identity information based on a valid token such as user name, tenant, etc Refer to: http://docs.openstack.org/developer/python-keystoneclient/ middlewarearchitecture.html HEADERS ------- * Headers starting with HTTP\_ is a standard http header * Headers starting with HTTP_X is an extended http header Coming in from initial call from client or customer ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ HTTP_X_AUTH_TOKEN The client token being passed in. HTTP_X_STORAGE_TOKEN The client token being passed in (legacy Rackspace use) to support swift/cloud files Used for communication between components ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ WWW-Authenticate HTTP header returned to a user indicating which endpoint to use to retrieve a new token What we add to the request for use by the OpenStack service ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ HTTP_X_IDENTITY_STATUS 'Confirmed' or 'Invalid' The underlying service will only see a value of 'Invalid' if the Middleware is configured to run in 'delay_auth_decision' mode HTTP_X_DOMAIN_ID Identity service managed unique identifier, string. Only present if this is a domain-scoped v3 token. HTTP_X_DOMAIN_NAME Unique domain name, string. Only present if this is a domain-scoped v3 token. HTTP_X_PROJECT_ID Identity service managed unique identifier, string. Only present if this is a project-scoped v3 token, or a tenant-scoped v2 token. HTTP_X_PROJECT_NAME Project name, unique within owning domain, string. Only present if this is a project-scoped v3 token, or a tenant-scoped v2 token. HTTP_X_PROJECT_DOMAIN_ID Identity service managed unique identifier of owning domain of project, string. Only present if this is a project-scoped v3 token. If this variable is set, this indicates that the PROJECT_NAME can only be assumed to be unique within this domain. HTTP_X_PROJECT_DOMAIN_NAME Name of owning domain of project, string. Only present if this is a project-scoped v3 token. If this variable is set, this indicates that the PROJECT_NAME can only be assumed to be unique within this domain. HTTP_X_USER_ID Identity-service managed unique identifier, string HTTP_X_USER_NAME User identifier, unique within owning domain, string HTTP_X_USER_DOMAIN_ID Identity service managed unique identifier of owning domain of user, string. If this variable is set, this indicates that the USER_NAME can only be assumed to be unique within this domain. HTTP_X_USER_DOMAIN_NAME Name of owning domain of user, string. If this variable is set, this indicates that the USER_NAME can only be assumed to be unique within this domain. HTTP_X_ROLES Comma delimited list of case-sensitive role names HTTP_X_SERVICE_CATALOG json encoded keystone service catalog (optional). HTTP_X_TENANT_ID *Deprecated* in favor of HTTP_X_PROJECT_ID Identity service managed unique identifier, string. For v3 tokens, this will be set to the same value as HTTP_X_PROJECT_ID HTTP_X_TENANT_NAME *Deprecated* in favor of HTTP_X_PROJECT_NAME Project identifier, unique within owning domain, string. For v3 tokens, this will be set to the same value as HTTP_X_PROJECT_NAME HTTP_X_TENANT *Deprecated* in favor of HTTP_X_TENANT_ID and HTTP_X_TENANT_NAME Keystone-assigned unique identifier, string. For v3 tokens, this will be set to the same value as HTTP_X_PROJECT_ID HTTP_X_USER *Deprecated* in favor of HTTP_X_USER_ID and HTTP_X_USER_NAME User name, unique within owning domain, string HTTP_X_ROLE *Deprecated* in favor of HTTP_X_ROLES Will contain the same values as HTTP_X_ROLES. OTHER ENVIRONMENT VARIABLES --------------------------- keystone.token_info Information about the token discovered in the process of validation. This may include extended information returned by the Keystone token validation call, as well as basic information about the tenant and user. """ import contextlib import datetime import logging import os import requests import stat import tempfile import time import netaddr from oslo.config import cfg import six from six.moves import urllib from keystoneclient.common import cms from keystoneclient import exceptions from keystoneclient.middleware import memcache_crypt from keystoneclient.openstack.common import jsonutils from keystoneclient.openstack.common import memorycache from keystoneclient.openstack.common import timeutils from keystoneclient import utils # alternative middleware configuration in the main application's # configuration file e.g. in nova.conf # [keystone_authtoken] # auth_host = 127.0.0.1 # auth_port = 35357 # auth_protocol = http # admin_tenant_name = admin # admin_user = admin # admin_password = badpassword # when deploy Keystone auth_token middleware with Swift, user may elect # to use Swift memcache instead of the local Keystone memcache. Swift memcache # is passed in from the request environment and its identified by the # 'swift.cache' key. However it could be different, depending on deployment. # To use Swift memcache, you must set the 'cache' option to the environment # key where the Swift cache object is stored. opts = [ cfg.StrOpt('auth_admin_prefix', default='', help='Prefix to prepend at the beginning of the path'), cfg.StrOpt('auth_host', default='127.0.0.1', help='Host providing the admin Identity API endpoint'), cfg.IntOpt('auth_port', default=35357, help='Port of the admin Identity API endpoint'), cfg.StrOpt('auth_protocol', default='https', help='Protocol of the admin Identity API endpoint' '(http or https)'), cfg.StrOpt('auth_uri', default=None, # FIXME(dolph): should be default='http://127.0.0.1:5000/v2.0/', # or (depending on client support) an unversioned, publicly # accessible identity endpoint (see bug 1207517) help='Complete public Identity API endpoint'), cfg.StrOpt('auth_version', default=None, help='API version of the admin Identity API endpoint'), cfg.BoolOpt('delay_auth_decision', default=False, help='Do not handle authorization requests within the' ' middleware, but delegate the authorization decision to' ' downstream WSGI components'), cfg.BoolOpt('http_connect_timeout', default=None, help='Request timeout value for communicating with Identity' ' API server.'), cfg.IntOpt('http_request_max_retries', default=3, help='How many times are we trying to reconnect when' ' communicating with Identity API Server.'), cfg.StrOpt('admin_token', secret=True, help='Single shared secret with the Keystone configuration' ' used for bootstrapping a Keystone installation, or otherwise' ' bypassing the normal authentication process.'), cfg.StrOpt('admin_user', help='Keystone account username'), cfg.StrOpt('admin_password', secret=True, help='Keystone account password'), cfg.StrOpt('admin_tenant_name', default='admin', help='Keystone service account tenant name to validate' ' user tokens'), cfg.StrOpt('cache', default=None, help='Env key for the swift cache'), cfg.StrOpt('certfile', help='Required if Keystone server requires client certificate'), cfg.StrOpt('keyfile', help='Required if Keystone server requires client certificate'), cfg.StrOpt('cafile', default=None, help='A PEM encoded Certificate Authority to use when ' 'verifying HTTPs connections. Defaults to system CAs.'), cfg.BoolOpt('insecure', default=False, help='Verify HTTPS connections.'), cfg.StrOpt('signing_dir', help='Directory used to cache files related to PKI tokens'), cfg.ListOpt('memcached_servers', deprecated_name='memcache_servers', help='Optionally specify a list of memcached server(s) to' ' use for caching. If left undefined, tokens will instead be' ' cached in-process.'), cfg.IntOpt('token_cache_time', default=300, help='In order to prevent excessive effort spent validating' ' tokens, the middleware caches previously-seen tokens for a' ' configurable duration (in seconds). Set to -1 to disable' ' caching completely.'), cfg.IntOpt('revocation_cache_time', default=300, help='Determines the frequency at which the list of revoked' ' tokens is retrieved from the Identity service (in seconds). A' ' high number of revocation events combined with a low cache' ' duration may significantly reduce performance.'), cfg.StrOpt('memcache_security_strategy', default=None, help='(optional) if defined, indicate whether token data' ' should be authenticated or authenticated and encrypted.' ' Acceptable values are MAC or ENCRYPT. If MAC, token data is' ' authenticated (with HMAC) in the cache. If ENCRYPT, token' ' data is encrypted and authenticated in the cache. If the' ' value is not one of these options or empty, auth_token will' ' raise an exception on initialization.'), cfg.StrOpt('memcache_secret_key', default=None, secret=True, help='(optional, mandatory if memcache_security_strategy is' ' defined) this string is used for key derivation.'), cfg.BoolOpt('include_service_catalog', default=True, help='(optional) indicate whether to set the X-Service-Catalog' ' header. If False, middleware will not ask for service' ' catalog on token validation and will not set the' ' X-Service-Catalog header.'), cfg.StrOpt('enforce_token_bind', default='permissive', help='Used to control the use and type of token binding. Can' ' be set to: "disabled" to not check token binding.' ' "permissive" (default) to validate binding information if the' ' bind type is of a form known to the server and ignore it if' ' not. "strict" like "permissive" but if the bind type is' ' unknown the token will be rejected. "required" any form of' ' token binding is needed to be allowed. Finally the name of a' ' binding method that must be present in tokens.'), ] CONF = cfg.CONF CONF.register_opts(opts, group='keystone_authtoken') LIST_OF_VERSIONS_TO_ATTEMPT = ['v2.0', 'v3.0'] CACHE_KEY_TEMPLATE = 'tokens/%s' class BIND_MODE: DISABLED = 'disabled' PERMISSIVE = 'permissive' STRICT = 'strict' REQUIRED = 'required' KERBEROS = 'kerberos' def will_expire_soon(expiry): """Determines if expiration is about to occur. :param expiry: a datetime of the expected expiration :returns: boolean : true if expiration is within 30 seconds """ soon = (timeutils.utcnow() + datetime.timedelta(seconds=30)) return expiry < soon def _token_is_v2(token_info): return ('access' in token_info) def _token_is_v3(token_info): return ('token' in token_info) def confirm_token_not_expired(data): if not data: raise InvalidUserToken('Token authorization failed') if _token_is_v2(data): timestamp = data['access']['token']['expires'] elif _token_is_v3(data): timestamp = data['token']['expires_at'] else: raise InvalidUserToken('Token authorization failed') expires = timeutils.parse_isotime(timestamp) expires = timeutils.normalize_time(expires) utcnow = timeutils.utcnow() if utcnow >= expires: raise InvalidUserToken('Token authorization failed') return timeutils.isotime(at=expires, subsecond=True) def safe_quote(s): """URL-encode strings that are not already URL-encoded.""" return urllib.parse.quote(s) if s == urllib.parse.unquote(s) else s class InvalidUserToken(Exception): pass class ServiceError(Exception): pass class ConfigurationError(Exception): pass class NetworkError(Exception): pass class MiniResp(object): def __init__(self, error_message, env, headers=[]): # The HEAD method is unique: it must never return a body, even if # it reports an error (RFC-2616 clause 9.4). We relieve callers # from varying the error responses depending on the method. if env['REQUEST_METHOD'] == 'HEAD': self.body = [''] else: self.body = [error_message] self.headers = list(headers) self.headers.append(('Content-type', 'text/plain')) class AuthProtocol(object): """Auth Middleware that handles authenticating client calls.""" def __init__(self, app, conf): self.LOG = logging.getLogger(conf.get('log_name', __name__)) self.LOG.info('Starting keystone auth_token middleware') self.conf = conf self.app = app # delay_auth_decision means we still allow unauthenticated requests # through and we let the downstream service make the final decision self.delay_auth_decision = (self._conf_get('delay_auth_decision') in (True, 'true', 't', '1', 'on', 'yes', 'y')) # where to find the auth service (we use this to validate tokens) auth_host = self._conf_get('auth_host') auth_port = int(self._conf_get('auth_port')) auth_protocol = self._conf_get('auth_protocol') auth_admin_prefix = self._conf_get('auth_admin_prefix') self.auth_uri = self._conf_get('auth_uri') if netaddr.valid_ipv6(auth_host): # Note(dzyu) it is an IPv6 address, so it needs to be wrapped # with '[]' to generate a valid IPv6 URL, based on # http://www.ietf.org/rfc/rfc2732.txt auth_host = '[%s]' % auth_host self.request_uri = '%s://%s:%s' % (auth_protocol, auth_host, auth_port) if self.auth_uri is None: self.LOG.warning( 'Configuring auth_uri to point to the public identity ' 'endpoint is required; clients may not be able to ' 'authenticate against an admin endpoint') # FIXME(dolph): drop support for this fallback behavior as # documented in bug 1207517 self.auth_uri = self.request_uri if auth_admin_prefix: self.request_uri = "%s/%s" % (self.request_uri, auth_admin_prefix.strip('/')) # SSL self.cert_file = self._conf_get('certfile') self.key_file = self._conf_get('keyfile') self.ssl_ca_file = self._conf_get('cafile') self.ssl_insecure = self._conf_get('insecure') # signing self.signing_dirname = self._conf_get('signing_dir') if self.signing_dirname is None: self.signing_dirname = tempfile.mkdtemp(prefix='keystone-signing-') self.LOG.info('Using %s as cache directory for signing certificate', self.signing_dirname) self.verify_signing_dir() val = '%s/signing_cert.pem' % self.signing_dirname self.signing_cert_file_name = val val = '%s/cacert.pem' % self.signing_dirname self.signing_ca_file_name = val val = '%s/revoked.pem' % self.signing_dirname self.revoked_file_name = val # Credentials used to verify this component with the Auth service since # validating tokens is a privileged call self.admin_token = self._conf_get('admin_token') self.admin_token_expiry = None self.admin_user = self._conf_get('admin_user') self.admin_password = self._conf_get('admin_password') self.admin_tenant_name = self._conf_get('admin_tenant_name') # Token caching self._cache_pool = None self._cache_initialized = False # memcache value treatment, ENCRYPT or MAC self._memcache_security_strategy = \ self._conf_get('memcache_security_strategy') if self._memcache_security_strategy is not None: self._memcache_security_strategy = \ self._memcache_security_strategy.upper() self._memcache_secret_key = \ self._conf_get('memcache_secret_key') self._assert_valid_memcache_protection_config() # By default the token will be cached for 5 minutes self.token_cache_time = int(self._conf_get('token_cache_time')) self._token_revocation_list = None self._token_revocation_list_fetched_time = None self.token_revocation_list_cache_timeout = datetime.timedelta( seconds=self._conf_get('revocation_cache_time')) http_connect_timeout_cfg = self._conf_get('http_connect_timeout') self.http_connect_timeout = (http_connect_timeout_cfg and int(http_connect_timeout_cfg)) self.auth_version = None self.http_request_max_retries = \ self._conf_get('http_request_max_retries') self.include_service_catalog = self._conf_get( 'include_service_catalog') def _assert_valid_memcache_protection_config(self): if self._memcache_security_strategy: if self._memcache_security_strategy not in ('MAC', 'ENCRYPT'): raise ConfigurationError('memcache_security_strategy must be ' 'ENCRYPT or MAC') if not self._memcache_secret_key: raise ConfigurationError('memcache_secret_key must be defined ' 'when a memcache_security_strategy ' 'is defined') def _init_cache(self, env): self._cache_pool = CachePool( env.get(self._conf_get('cache')), self._conf_get('memcached_servers')) self._cache_initialized = True def _conf_get(self, name): # try config from paste-deploy first if name in self.conf: return self.conf[name] else: return CONF.keystone_authtoken[name] def _choose_api_version(self): """Determine the api version that we should use.""" # If the configuration specifies an auth_version we will just # assume that is correct and use it. We could, of course, check # that this version is supported by the server, but in case # there are some problems in the field, we want as little code # as possible in the way of letting auth_token talk to the # server. if self._conf_get('auth_version'): version_to_use = self._conf_get('auth_version') self.LOG.info('Auth Token proceeding with requested %s apis', version_to_use) else: version_to_use = None versions_supported_by_server = self._get_supported_versions() if versions_supported_by_server: for version in LIST_OF_VERSIONS_TO_ATTEMPT: if version in versions_supported_by_server: version_to_use = version break if version_to_use: self.LOG.info('Auth Token confirmed use of %s apis', version_to_use) else: self.LOG.error( 'Attempted versions [%s] not in list supported by ' 'server [%s]', ', '.join(LIST_OF_VERSIONS_TO_ATTEMPT), ', '.join(versions_supported_by_server)) raise ServiceError('No compatible apis supported by server') return version_to_use def _get_supported_versions(self): versions = [] response, data = self._json_request('GET', '/') if response.status_code == 501: self.LOG.warning("Old keystone installation found...assuming v2.0") versions.append("v2.0") elif response.status_code != 300: self.LOG.error('Unable to get version info from keystone: %s', response.status_code) raise ServiceError('Unable to get version info from keystone') else: try: for version in data['versions']['values']: versions.append(version['id']) except KeyError: self.LOG.error( 'Invalid version response format from server') raise ServiceError('Unable to parse version response ' 'from keystone') self.LOG.debug('Server reports support for api versions: %s', ', '.join(versions)) return versions def __call__(self, env, start_response): """Handle incoming request. Authenticate send downstream on success. Reject request if we can't authenticate. """ self.LOG.debug('Authenticating user token') # initialize memcache if we haven't done so if not self._cache_initialized: self._init_cache(env) try: self._remove_auth_headers(env) user_token = self._get_user_token_from_header(env) token_info = self._validate_user_token(user_token, env) env['keystone.token_info'] = token_info user_headers = self._build_user_headers(token_info) self._add_headers(env, user_headers) return self.app(env, start_response) except InvalidUserToken: if self.delay_auth_decision: self.LOG.info( 'Invalid user token - deferring reject downstream') self._add_headers(env, {'X-Identity-Status': 'Invalid'}) return self.app(env, start_response) else: self.LOG.info('Invalid user token - rejecting request') return self._reject_request(env, start_response) except ServiceError as e: self.LOG.critical('Unable to obtain admin token: %s', e) resp = MiniResp('Service unavailable', env) start_response('503 Service Unavailable', resp.headers) return resp.body def _remove_auth_headers(self, env): """Remove headers so a user can't fake authentication. :param env: wsgi request environment """ auth_headers = ( 'X-Identity-Status', 'X-Domain-Id', 'X-Domain-Name', 'X-Project-Id', 'X-Project-Name', 'X-Project-Domain-Id', 'X-Project-Domain-Name', 'X-User-Id', 'X-User-Name', 'X-User-Domain-Id', 'X-User-Domain-Name', 'X-Roles', 'X-Service-Catalog', # Deprecated 'X-User', 'X-Tenant-Id', 'X-Tenant-Name', 'X-Tenant', 'X-Role', ) self.LOG.debug('Removing headers from request environment: %s', ','.join(auth_headers)) self._remove_headers(env, auth_headers) def _get_user_token_from_header(self, env): """Get token id from request. :param env: wsgi request environment :return token id :raises InvalidUserToken if no token is provided in request """ token = self._get_header(env, 'X-Auth-Token', self._get_header(env, 'X-Storage-Token')) if token: return token else: if not self.delay_auth_decision: self.LOG.warn("Unable to find authentication token" " in headers") self.LOG.debug("Headers: %s", env) raise InvalidUserToken('Unable to find token in headers') def _reject_request(self, env, start_response): """Redirect client to auth server. :param env: wsgi request environment :param start_response: wsgi response callback :returns HTTPUnauthorized http response """ headers = [('WWW-Authenticate', 'Keystone uri=\'%s\'' % self.auth_uri)] resp = MiniResp('Authentication required', env, headers) start_response('401 Unauthorized', resp.headers) return resp.body def get_admin_token(self): """Return admin token, possibly fetching a new one. if self.admin_token_expiry is set from fetching an admin token, check it for expiration, and request a new token is the existing token is about to expire. :return admin token id :raise ServiceError when unable to retrieve token from keystone """ if self.admin_token_expiry: if will_expire_soon(self.admin_token_expiry): self.admin_token = None if not self.admin_token: (self.admin_token, self.admin_token_expiry) = self._request_admin_token() return self.admin_token def _http_request(self, method, path, **kwargs): """HTTP request helper used to make unspecified content type requests. :param method: http method :param path: relative request url :return (http response object, response body) :raise ServerError when unable to communicate with keystone """ url = "%s/%s" % (self.request_uri, path.lstrip('/')) kwargs.setdefault('timeout', self.http_connect_timeout) if self.cert_file and self.key_file: kwargs['cert'] = (self.cert_file, self.key_file) elif self.cert_file or self.key_file: self.LOG.warn('Cannot use only a cert or key file. ' 'Please provide both. Ignoring.') kwargs['verify'] = self.ssl_ca_file or True if self.ssl_insecure: kwargs['verify'] = False RETRIES = self.http_request_max_retries retry = 0 while True: try: response = requests.request(method, url, **kwargs) break except Exception as e: if retry >= RETRIES: self.LOG.error('HTTP connection exception: %s', e) raise NetworkError('Unable to communicate with keystone') # NOTE(vish): sleep 0.5, 1, 2 self.LOG.warn('Retrying on HTTP connection exception: %s', e) time.sleep(2.0 ** retry / 2) retry += 1 return response def _json_request(self, method, path, body=None, additional_headers=None): """HTTP request helper used to make json requests. :param method: http method :param path: relative request url :param body: dict to encode to json as request body. Optional. :param additional_headers: dict of additional headers to send with http request. Optional. :return (http response object, response body parsed as json) :raise ServerError when unable to communicate with keystone """ kwargs = { 'headers': { 'Content-type': 'application/json', 'Accept': 'application/json', }, } if additional_headers: kwargs['headers'].update(additional_headers) if body: kwargs['data'] = jsonutils.dumps(body) response = self._http_request(method, path, **kwargs) try: data = jsonutils.loads(response.text) except ValueError: self.LOG.debug('Keystone did not return json-encoded body') data = {} return response, data def _request_admin_token(self): """Retrieve new token as admin user from keystone. :return token id upon success :raises ServerError when unable to communicate with keystone Irrespective of the auth version we are going to use for the user token, for simplicity we always use a v2 admin token to validate the user token. """ params = { 'auth': { 'passwordCredentials': { 'username': self.admin_user, 'password': self.admin_password, }, 'tenantName': self.admin_tenant_name, } } response, data = self._json_request('POST', '/v2.0/tokens', body=params) try: token = data['access']['token']['id'] expiry = data['access']['token']['expires'] if not (token and expiry): raise AssertionError('invalid token or expire') datetime_expiry = timeutils.parse_isotime(expiry) return (token, timeutils.normalize_time(datetime_expiry)) except (AssertionError, KeyError): self.LOG.warn( "Unexpected response from keystone service: %s", data) raise ServiceError('invalid json response') except (ValueError): data['access']['token']['id'] = '' self.LOG.warn( "Unable to parse expiration time from token: %s", data) raise ServiceError('invalid json response') def _validate_user_token(self, user_token, env, retry=True): """Authenticate user using PKI :param user_token: user's token id :param retry: Ignored, as it is not longer relevant :return uncrypted body of the token if the token is valid :raise InvalidUserToken if token is rejected :no longer raises ServiceError since it no longer makes RPC """ token_id = None try: token_id = cms.cms_hash_token(user_token) cached = self._cache_get(token_id) if cached: return cached if cms.is_ans1_token(user_token): verified = self.verify_signed_token(user_token) data = jsonutils.loads(verified) else: data = self.verify_uuid_token(user_token, retry) expires = confirm_token_not_expired(data) self._confirm_token_bind(data, env) self._cache_put(token_id, data, expires) return data except NetworkError: self.LOG.debug('Token validation failure.', exc_info=True) self.LOG.warn("Authorization failed for token") raise InvalidUserToken('Token authorization failed') except Exception: self.LOG.debug('Token validation failure.', exc_info=True) if token_id: self._cache_store_invalid(token_id) self.LOG.warn("Authorization failed for token") raise InvalidUserToken('Token authorization failed') def _build_user_headers(self, token_info): """Convert token object into headers. Build headers that represent authenticated user - see main doc info at start of file for details of headers to be defined. :param token_info: token object returned by keystone on authentication :raise InvalidUserToken when unable to parse token object """ def get_tenant_info(): """Returns a (tenant_id, tenant_name) tuple from context.""" def essex(): """Essex puts the tenant ID and name on the token.""" return (token['tenant']['id'], token['tenant']['name']) def pre_diablo(): """Pre-diablo, Keystone only provided tenantId.""" return (token['tenantId'], token['tenantId']) def default_tenant(): """Pre-grizzly, assume the user's default tenant.""" return (user['tenantId'], user['tenantName']) for method in [essex, pre_diablo, default_tenant]: try: return method() except KeyError: pass raise InvalidUserToken('Unable to determine tenancy.') # For clarity. set all those attributes that are optional in # either a v2 or v3 token to None first domain_id = None domain_name = None project_id = None project_name = None user_domain_id = None user_domain_name = None project_domain_id = None project_domain_name = None if _token_is_v2(token_info): user = token_info['access']['user'] token = token_info['access']['token'] roles = ','.join([role['name'] for role in user.get('roles', [])]) catalog_root = token_info['access'] catalog_key = 'serviceCatalog' project_id, project_name = get_tenant_info() else: #v3 token token = token_info['token'] user = token['user'] user_domain_id = user['domain']['id'] user_domain_name = user['domain']['name'] roles = (','.join([role['name'] for role in token.get('roles', [])])) catalog_root = token catalog_key = 'catalog' # For v3, the server will put in the default project if there is # one, so no need for us to add it here (like we do for a v2 token) if 'domain' in token: domain_id = token['domain']['id'] domain_name = token['domain']['name'] elif 'project' in token: project_id = token['project']['id'] project_name = token['project']['name'] project_domain_id = token['project']['domain']['id'] project_domain_name = token['project']['domain']['name'] user_id = user['id'] user_name = user['name'] rval = { 'X-Identity-Status': 'Confirmed', 'X-Domain-Id': domain_id, 'X-Domain-Name': domain_name, 'X-Project-Id': project_id, 'X-Project-Name': project_name, 'X-Project-Domain-Id': project_domain_id, 'X-Project-Domain-Name': project_domain_name, 'X-User-Id': user_id, 'X-User-Name': user_name, 'X-User-Domain-Id': user_domain_id, 'X-User-Domain-Name': user_domain_name, 'X-Roles': roles, # Deprecated 'X-User': user_name, 'X-Tenant-Id': project_id, 'X-Tenant-Name': project_name, 'X-Tenant': project_name, 'X-Role': roles, } self.LOG.debug("Received request from user: %s with project_id : %s" " and roles: %s ", user_id, project_id, roles) if self.include_service_catalog and catalog_key in catalog_root: catalog = catalog_root[catalog_key] rval['X-Service-Catalog'] = jsonutils.dumps(catalog) return rval def _header_to_env_var(self, key): """Convert header to wsgi env variable. :param key: http header name (ex. 'X-Auth-Token') :return wsgi env variable name (ex. 'HTTP_X_AUTH_TOKEN') """ return 'HTTP_%s' % key.replace('-', '_').upper() def _add_headers(self, env, headers): """Add http headers to environment.""" for (k, v) in six.iteritems(headers): env_key = self._header_to_env_var(k) env[env_key] = v def _remove_headers(self, env, keys): """Remove http headers from environment.""" for k in keys: env_key = self._header_to_env_var(k) try: del env[env_key] except KeyError: pass def _get_header(self, env, key, default=None): """Get http header from environment.""" env_key = self._header_to_env_var(key) return env.get(env_key, default) def _cache_get(self, token_id, ignore_expires=False): """Return token information from cache. If token is invalid raise InvalidUserToken return token only if fresh (not expired). """ if token_id: if self._memcache_security_strategy is None: key = CACHE_KEY_TEMPLATE % token_id with self._cache_pool.reserve() as cache: serialized = cache.get(key) else: secret_key = self._memcache_secret_key if isinstance(secret_key, six.string_types): secret_key = secret_key.encode('utf-8') security_strategy = self._memcache_security_strategy if isinstance(security_strategy, six.string_types): security_strategy = security_strategy.encode('utf-8') keys = memcache_crypt.derive_keys( token_id, secret_key, security_strategy) cache_key = CACHE_KEY_TEMPLATE % ( memcache_crypt.get_cache_key(keys)) with self._cache_pool.reserve() as cache: raw_cached = cache.get(cache_key) try: # unprotect_data will return None if raw_cached is None serialized = memcache_crypt.unprotect_data(keys, raw_cached) except Exception: msg = 'Failed to decrypt/verify cache data' self.LOG.exception(msg) # this should have the same effect as data not # found in cache serialized = None if serialized is None: return None # Note that 'invalid' and (data, expires) are the only # valid types of serialized cache entries, so there is not # a collision with jsonutils.loads(serialized) == None. if not isinstance(serialized, six.string_types): serialized = serialized.decode('utf-8') cached = jsonutils.loads(serialized) if cached == 'invalid': self.LOG.debug('Cached Token is marked unauthorized') raise InvalidUserToken('Token authorization failed') data, expires = cached try: expires = timeutils.parse_isotime(expires) except ValueError: # Gracefully handle upgrade of expiration times from *nix # timestamps to ISO 8601 formatted dates by ignoring old cached # values. return expires = timeutils.normalize_time(expires) utcnow = timeutils.utcnow() if ignore_expires or utcnow < expires: self.LOG.debug('Returning cached token') return data else: self.LOG.debug('Cached Token seems expired') def _cache_store(self, token_id, data): """Store value into memcache. data may be the string 'invalid' or a tuple like (data, expires) """ serialized_data = jsonutils.dumps(data) if isinstance(serialized_data, six.text_type): serialized_data = serialized_data.encode('utf-8') if self._memcache_security_strategy is None: cache_key = CACHE_KEY_TEMPLATE % token_id data_to_store = serialized_data else: secret_key = self._memcache_secret_key if isinstance(secret_key, six.string_types): secret_key = secret_key.encode('utf-8') security_strategy = self._memcache_security_strategy if isinstance(security_strategy, six.string_types): security_strategy = security_strategy.encode('utf-8') keys = memcache_crypt.derive_keys( token_id, secret_key, security_strategy) cache_key = CACHE_KEY_TEMPLATE % memcache_crypt.get_cache_key(keys) data_to_store = memcache_crypt.protect_data(keys, serialized_data) with self._cache_pool.reserve() as cache: cache.set(cache_key, data_to_store, time=self.token_cache_time) def _invalid_user_token(self, msg=False): # NOTE(jamielennox): use False as the default so that None is valid if msg is False: msg = 'Token authorization failed' raise InvalidUserToken(msg) def _confirm_token_bind(self, data, env): bind_mode = self._conf_get('enforce_token_bind') if bind_mode == BIND_MODE.DISABLED: return try: if _token_is_v2(data): bind = data['access']['token']['bind'] elif _token_is_v3(data): bind = data['token']['bind'] else: self._invalid_user_token() except KeyError: bind = {} # permissive and strict modes don't require there to be a bind permissive = bind_mode in (BIND_MODE.PERMISSIVE, BIND_MODE.STRICT) if not bind: if permissive: # no bind provided and none required return else: self.LOG.info("No bind information present in token.") self._invalid_user_token() # get the named mode if bind_mode is not one of the predefined if permissive or bind_mode == BIND_MODE.REQUIRED: name = None else: name = bind_mode if name and name not in bind: self.LOG.info("Named bind mode %s not in bind information", name) self._invalid_user_token() for bind_type, identifier in six.iteritems(bind): if bind_type == BIND_MODE.KERBEROS: if not env.get('AUTH_TYPE', '').lower() == 'negotiate': self.LOG.info("Kerberos credentials required and " "not present.") self._invalid_user_token() if not env.get('REMOTE_USER') == identifier: self.LOG.info("Kerberos credentials do not match " "those in bind.") self._invalid_user_token() self.LOG.debug("Kerberos bind authentication successful.") elif bind_mode == BIND_MODE.PERMISSIVE: self.LOG.debug("Ignoring Unknown bind for permissive mode: " "%(bind_type)s: %(identifier)s.", {'bind_type': bind_type, 'identifier': identifier}) else: self.LOG.info("Couldn't verify unknown bind: %(bind_type)s: " "%(identifier)s.", {'bind_type': bind_type, 'identifier': identifier}) self._invalid_user_token() def _cache_put(self, token_id, data, expires): """Put token data into the cache. Stores the parsed expire date in cache allowing quick check of token freshness on retrieval. """ self.LOG.debug('Storing token in cache') self._cache_store(token_id, (data, expires)) def _cache_store_invalid(self, token_id): """Store invalid token in cache.""" self.LOG.debug('Marking token as unauthorized in cache') self._cache_store(token_id, 'invalid') def cert_file_missing(self, proc_output, file_name): return (file_name in proc_output and not os.path.exists(file_name)) def verify_uuid_token(self, user_token, retry=True): """Authenticate user token with keystone. :param user_token: user's token id :param retry: flag that forces the middleware to retry user authentication when an indeterminate response is received. Optional. :return: token object received from keystone on success :raise InvalidUserToken: if token is rejected :raise ServiceError: if unable to authenticate token """ # Determine the highest api version we can use. if not self.auth_version: self.auth_version = self._choose_api_version() if self.auth_version == 'v3.0': headers = {'X-Auth-Token': self.get_admin_token(), 'X-Subject-Token': safe_quote(user_token)} path = '/v3/auth/tokens' if not self.include_service_catalog: # NOTE(gyee): only v3 API support this option path = path + '?nocatalog' response, data = self._json_request( 'GET', path, additional_headers=headers) else: headers = {'X-Auth-Token': self.get_admin_token()} response, data = self._json_request( 'GET', '/v2.0/tokens/%s' % safe_quote(user_token), additional_headers=headers) if response.status_code == 200: return data if response.status_code == 404: self.LOG.warn("Authorization failed for token") raise InvalidUserToken('Token authorization failed') if response.status_code == 401: self.LOG.info( 'Keystone rejected admin token, resetting') self.admin_token = None else: self.LOG.error('Bad response code while validating token: %s', response.status_code) if retry: self.LOG.info('Retrying validation') return self.verify_uuid_token(user_token, False) else: self.LOG.warn("Invalid user token. Keystone response: %s", data) raise InvalidUserToken() def is_signed_token_revoked(self, signed_text): """Indicate whether the token appears in the revocation list.""" revocation_list = self.token_revocation_list revoked_tokens = revocation_list.get('revoked', []) if not revoked_tokens: return revoked_ids = (x['id'] for x in revoked_tokens) if isinstance(signed_text, six.text_type): signed_text = signed_text.encode('utf-8') token_id = utils.hash_signed_token(signed_text) for revoked_id in revoked_ids: if token_id == revoked_id: self.LOG.debug('Token is marked as having been revoked') return True return False def cms_verify(self, data): """Verifies the signature of the provided data's IAW CMS syntax. If either of the certificate files are missing, fetch them and retry. """ while True: try: output = cms.cms_verify(data, self.signing_cert_file_name, self.signing_ca_file_name) except exceptions.CertificateConfigError as err: if self.cert_file_missing(err.output, self.signing_cert_file_name): self.fetch_signing_cert() continue if self.cert_file_missing(err.output, self.signing_ca_file_name): self.fetch_ca_cert() continue self.LOG.error('CMS Verify output: %s', err.output) raise except cms.subprocess.CalledProcessError as err: self.LOG.warning('Verify error: %s', err) raise return output def verify_signed_token(self, signed_text): """Check that the token is unrevoked and has a valid signature.""" if self.is_signed_token_revoked(signed_text): raise InvalidUserToken('Token has been revoked') formatted = cms.token_to_cms(signed_text) return self.cms_verify(formatted) def verify_signing_dir(self): if os.path.exists(self.signing_dirname): if not os.access(self.signing_dirname, os.W_OK): raise ConfigurationError( 'unable to access signing_dir %s' % self.signing_dirname) uid = os.getuid() if os.stat(self.signing_dirname).st_uid != uid: self.LOG.warning( 'signing_dir is not owned by %s', uid) current_mode = stat.S_IMODE(os.stat(self.signing_dirname).st_mode) if current_mode != stat.S_IRWXU: self.LOG.warning( 'signing_dir mode is %s instead of %s', oct(current_mode), oct(stat.S_IRWXU)) else: os.makedirs(self.signing_dirname, stat.S_IRWXU) @property def token_revocation_list_fetched_time(self): if not self._token_revocation_list_fetched_time: # If the fetched list has been written to disk, use its # modification time. if os.path.exists(self.revoked_file_name): mtime = os.path.getmtime(self.revoked_file_name) fetched_time = datetime.datetime.utcfromtimestamp(mtime) # Otherwise the list will need to be fetched. else: fetched_time = datetime.datetime.min self._token_revocation_list_fetched_time = fetched_time return self._token_revocation_list_fetched_time @token_revocation_list_fetched_time.setter def token_revocation_list_fetched_time(self, value): self._token_revocation_list_fetched_time = value @property def token_revocation_list(self): timeout = (self.token_revocation_list_fetched_time + self.token_revocation_list_cache_timeout) list_is_current = timeutils.utcnow() < timeout if list_is_current: # Load the list from disk if required if not self._token_revocation_list: open_kwargs = {'encoding': 'utf-8'} if six.PY3 else {} with open(self.revoked_file_name, 'r', **open_kwargs) as f: self._token_revocation_list = jsonutils.loads(f.read()) else: self.token_revocation_list = self.fetch_revocation_list() return self._token_revocation_list def _atomic_write_to_signing_dir(self, file_name, value): # In Python2, encoding is slow so the following check avoids it if it # is not absolutely necessary. if isinstance(value, six.text_type): value = value.encode('utf-8') def _atomic_write(destination, data): with tempfile.NamedTemporaryFile(dir=self.signing_dirname, delete=False) as f: f.write(data) os.rename(f.name, destination) try: _atomic_write(file_name, value) except (OSError, IOError): self.verify_signing_dir() _atomic_write(file_name, value) @token_revocation_list.setter def token_revocation_list(self, value): """Save a revocation list to memory and to disk. :param value: A json-encoded revocation list """ self._token_revocation_list = jsonutils.loads(value) self.token_revocation_list_fetched_time = timeutils.utcnow() self._atomic_write_to_signing_dir(self.revoked_file_name, value) def fetch_revocation_list(self, retry=True): headers = {'X-Auth-Token': self.get_admin_token()} response, data = self._json_request('GET', '/v2.0/tokens/revoked', additional_headers=headers) if response.status_code == 401: if retry: self.LOG.info( 'Keystone rejected admin token, resetting admin token') self.admin_token = None return self.fetch_revocation_list(retry=False) if response.status_code != 200: raise ServiceError('Unable to fetch token revocation list.') if 'signed' not in data: raise ServiceError('Revocation list improperly formatted.') return self.cms_verify(data['signed']) def _fetch_cert_file(self, cert_file_name, cert_type): path = '/v2.0/certificates/' + cert_type response = self._http_request('GET', path) if response.status_code != 200: raise exceptions.CertificateConfigError(response.text) self._atomic_write_to_signing_dir(cert_file_name, response.text) def fetch_signing_cert(self): self._fetch_cert_file(self.signing_cert_file_name, 'signing') def fetch_ca_cert(self): self._fetch_cert_file(self.signing_ca_file_name, 'ca') class CachePool(list): """A lazy pool of cache references.""" def __init__(self, cache, memcached_servers): self._environment_cache = cache self._memcached_servers = memcached_servers @contextlib.contextmanager def reserve(self): """Context manager to manage a pooled cache reference.""" if self._environment_cache is not None: # skip pooling and just use the cache from the upstream filter yield self._environment_cache return # otherwise the context manager will continue! try: c = self.pop() except IndexError: # the pool is empty, so we need to create a new client c = memorycache.get_client(self._memcached_servers) try: yield c finally: self.append(c) def filter_factory(global_conf, **local_conf): """Returns a WSGI filter app for use with paste.deploy.""" conf = global_conf.copy() conf.update(local_conf) def auth_filter(app): return AuthProtocol(app, conf) return auth_filter def app_factory(global_conf, **local_conf): conf = global_conf.copy() conf.update(local_conf) return AuthProtocol(None, conf) if __name__ == '__main__': """Run this module directly to start a protected echo service:: $ python -m keystoneclient.middleware.auth_token When the ``auth_token`` module authenticates a request, the echo service will respond with all the environment variables presented to it by this module. """ def echo_app(environ, start_response): """A WSGI application that echoes the CGI environment to the user.""" start_response('200 OK', [('Content-Type', 'application/json')]) environment = dict((k, v) for k, v in six.iteritems(environ) if k.startswith('HTTP_X_')) yield jsonutils.dumps(environment) from wsgiref import simple_server # hardcode any non-default configuration here conf = {'auth_protocol': 'http', 'admin_token': 'ADMIN'} app = AuthProtocol(echo_app, conf) server = simple_server.make_server('', 8000, app) print('Serving on port 8000 (Ctrl+C to end)...') server.serve_forever() python-keystoneclient-0.7.1/keystoneclient/middleware/memcache_crypt.py0000664000175400017540000001403312315030450027740 0ustar jenkinsjenkins00000000000000# Copyright 2010-2013 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. """ Utilities for memcache encryption and integrity check. Data should be serialized before entering these functions. Encryption has a dependency on the pycrypto. If pycrypto is not available, CryptoUnavailableError will be raised. This module will not be called unless signing or encryption is enabled in the config. It will always validate signatures, and will decrypt data if encryption is enabled. It is not valid to mix protection modes. """ import base64 import functools import hashlib import hmac import math import os import six import sys # make sure pycrypto is available try: from Crypto.Cipher import AES except ImportError: AES = None HASH_FUNCTION = hashlib.sha384 DIGEST_LENGTH = HASH_FUNCTION().digest_size DIGEST_SPLIT = DIGEST_LENGTH // 3 DIGEST_LENGTH_B64 = 4 * int(math.ceil(DIGEST_LENGTH / 3.0)) class InvalidMacError(Exception): """raise when unable to verify MACed data. This usually indicates that data had been expectedly modified in memcache. """ pass class DecryptError(Exception): """raise when unable to decrypt encrypted data. """ pass class CryptoUnavailableError(Exception): """raise when Python Crypto module is not available. """ pass def assert_crypto_availability(f): """Ensure Crypto module is available.""" @functools.wraps(f) def wrapper(*args, **kwds): if AES is None: raise CryptoUnavailableError() return f(*args, **kwds) return wrapper if sys.version_info >= (3, 3): constant_time_compare = hmac.compare_digest else: def constant_time_compare(first, second): """Returns True if both string inputs are equal, otherwise False. This function should take a constant amount of time regardless of how many characters in the strings match. """ if len(first) != len(second): return False result = 0 if six.PY3 and isinstance(first, bytes) and isinstance(second, bytes): for x, y in zip(first, second): result |= x ^ y else: for x, y in zip(first, second): result |= ord(x) ^ ord(y) return result == 0 def derive_keys(token, secret, strategy): """Derives keys for MAC and ENCRYPTION from the user-provided secret. The resulting keys should be passed to the protect and unprotect functions. As suggested by NIST Special Publication 800-108, this uses the first 128 bits from the sha384 KDF for the obscured cache key value, the second 128 bits for the message authentication key and the remaining 128 bits for the encryption key. This approach is faster than computing a separate hmac as the KDF for each desired key. """ digest = hmac.new(secret, token + strategy, HASH_FUNCTION).digest() return {'CACHE_KEY': digest[:DIGEST_SPLIT], 'MAC': digest[DIGEST_SPLIT: 2 * DIGEST_SPLIT], 'ENCRYPTION': digest[2 * DIGEST_SPLIT:], 'strategy': strategy} def sign_data(key, data): """Sign the data using the defined function and the derived key.""" mac = hmac.new(key, data, HASH_FUNCTION).digest() return base64.b64encode(mac) @assert_crypto_availability def encrypt_data(key, data): """Encrypt the data with the given secret key. Padding is n bytes of the value n, where 1 <= n <= blocksize. """ iv = os.urandom(16) cipher = AES.new(key, AES.MODE_CBC, iv) padding = 16 - len(data) % 16 return iv + cipher.encrypt(data + six.int2byte(padding) * padding) @assert_crypto_availability def decrypt_data(key, data): """Decrypt the data with the given secret key.""" iv = data[:16] cipher = AES.new(key, AES.MODE_CBC, iv) try: result = cipher.decrypt(data[16:]) except Exception: raise DecryptError('Encrypted data appears to be corrupted.') # Strip the last n padding bytes where n is the last value in # the plaintext return result[:-1 * six.byte2int([result[-1]])] def protect_data(keys, data): """Given keys and serialized data, returns an appropriately protected string suitable for storage in the cache. """ if keys['strategy'] == b'ENCRYPT': data = encrypt_data(keys['ENCRYPTION'], data) encoded_data = base64.b64encode(data) signature = sign_data(keys['MAC'], encoded_data) return signature + encoded_data def unprotect_data(keys, signed_data): """Given keys and cached string data, verifies the signature, decrypts if necessary, and returns the original serialized data. """ # cache backends return None when no data is found. We don't mind # that this particular special value is unsigned. if signed_data is None: return None # First we calculate the signature provided_mac = signed_data[:DIGEST_LENGTH_B64] calculated_mac = sign_data( keys['MAC'], signed_data[DIGEST_LENGTH_B64:]) # Then verify that it matches the provided value if not constant_time_compare(provided_mac, calculated_mac): raise InvalidMacError('Invalid MAC; data appears to be corrupted.') data = base64.b64decode(signed_data[DIGEST_LENGTH_B64:]) # then if necessary decrypt the data if keys['strategy'] == b'ENCRYPT': data = decrypt_data(keys['ENCRYPTION'], data) return data def get_cache_key(keys): """Given keys generated by derive_keys(), returns a base64 encoded value suitable for use as a cache key in memcached. """ return base64.b64encode(keys['CACHE_KEY']) python-keystoneclient-0.7.1/keystoneclient/middleware/s3_token.py0000664000175400017540000002420412315030450026503 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. # Copyright 2011,2012 Akira YOSHIYAMA # 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. # This source code is based ./auth_token.py and ./ec2_token.py. # See them for their copyright. """ S3 TOKEN MIDDLEWARE This WSGI component: * Get a request from the swift3 middleware with an S3 Authorization access key. * Validate s3 token in Keystone. * Transform the account name to AUTH_%(tenant_name). """ import logging import webob import requests import six from six.moves import urllib from keystoneclient.openstack.common import jsonutils PROTOCOL_NAME = 'S3 Token Authentication' # TODO(kun): remove it after oslo merge this. def split_path(path, minsegs=1, maxsegs=None, rest_with_last=False): """Validate and split the given HTTP request path. **Examples**:: ['a'] = split_path('/a') ['a', None] = split_path('/a', 1, 2) ['a', 'c'] = split_path('/a/c', 1, 2) ['a', 'c', 'o/r'] = split_path('/a/c/o/r', 1, 3, True) :param path: HTTP Request path to be split :param minsegs: Minimum number of segments to be extracted :param maxsegs: Maximum number of segments to be extracted :param rest_with_last: If True, trailing data will be returned as part of last segment. If False, and there is trailing data, raises ValueError. :returns: list of segments with a length of maxsegs (non-existant segments will return as None) :raises: ValueError if given an invalid path """ if not maxsegs: maxsegs = minsegs if minsegs > maxsegs: raise ValueError('minsegs > maxsegs: %d > %d' % (minsegs, maxsegs)) if rest_with_last: segs = path.split('/', maxsegs) minsegs += 1 maxsegs += 1 count = len(segs) if (segs[0] or count < minsegs or count > maxsegs or '' in segs[1:minsegs]): raise ValueError('Invalid path: %s' % urllib.parse.quote(path)) else: minsegs += 1 maxsegs += 1 segs = path.split('/', maxsegs) count = len(segs) if (segs[0] or count < minsegs or count > maxsegs + 1 or '' in segs[1:minsegs] or (count == maxsegs + 1 and segs[maxsegs])): raise ValueError('Invalid path: %s' % urllib.parse.quote(path)) segs = segs[1:maxsegs] segs.extend([None] * (maxsegs - 1 - len(segs))) return segs class ServiceError(Exception): pass class S3Token(object): """Auth Middleware that handles S3 authenticating client calls.""" def __init__(self, app, conf): """Common initialization code.""" self.app = app self.logger = logging.getLogger(conf.get('log_name', __name__)) self.logger.debug('Starting the %s component' % PROTOCOL_NAME) self.reseller_prefix = conf.get('reseller_prefix', 'AUTH_') # where to find the auth service (we use this to validate tokens) auth_host = conf.get('auth_host') auth_port = int(conf.get('auth_port', 35357)) auth_protocol = conf.get('auth_protocol', 'https') self.request_uri = '%s://%s:%s' % (auth_protocol, auth_host, auth_port) # SSL insecure = conf.get('insecure', False) cert_file = conf.get('certfile') key_file = conf.get('keyfile') if insecure: self.verify = False elif cert_file and key_file: self.verify = (cert_file, key_file) elif cert_file: self.verify = cert_file else: self.verify = None def deny_request(self, code): error_table = { 'AccessDenied': (401, 'Access denied'), 'InvalidURI': (400, 'Could not parse the specified URI'), } resp = webob.Response(content_type='text/xml') resp.status = error_table[code][0] error_msg = ('\r\n' '\r\n %s\r\n ' '%s\r\n\r\n' % (code, error_table[code][1])) if six.PY3: error_msg = error_msg.encode() resp.body = error_msg return resp def _json_request(self, creds_json): headers = {'Content-Type': 'application/json'} try: response = requests.post('%s/v2.0/s3tokens' % self.request_uri, headers=headers, data=creds_json, verify=self.verify) except requests.exceptions.RequestException as e: self.logger.info('HTTP connection exception: %s' % e) resp = self.deny_request('InvalidURI') raise ServiceError(resp) if response.status_code < 200 or response.status_code >= 300: self.logger.debug('Keystone reply error: status=%s reason=%s' % (response.status_code, response.reason)) resp = self.deny_request('AccessDenied') raise ServiceError(resp) return response def __call__(self, environ, start_response): """Handle incoming request. authenticate and send downstream.""" req = webob.Request(environ) self.logger.debug('Calling S3Token middleware.') try: parts = split_path(req.path, 1, 4, True) version, account, container, obj = parts except ValueError: msg = 'Not a path query, skipping.' self.logger.debug(msg) return self.app(environ, start_response) # Read request signature and access id. if 'Authorization' not in req.headers: msg = 'No Authorization header. skipping.' self.logger.debug(msg) return self.app(environ, start_response) token = req.headers.get('X-Auth-Token', req.headers.get('X-Storage-Token')) if not token: msg = 'You did not specify a auth or a storage token. skipping.' self.logger.debug(msg) return self.app(environ, start_response) auth_header = req.headers['Authorization'] try: access, signature = auth_header.split(' ')[-1].rsplit(':', 1) except ValueError: msg = 'You have an invalid Authorization header: %s' self.logger.debug(msg % (auth_header)) return self.deny_request('InvalidURI')(environ, start_response) # NOTE(chmou): This is to handle the special case with nova # when we have the option s3_affix_tenant. We will force it to # connect to another account than the one # authenticated. Before people start getting worried about # security, I should point that we are connecting with # username/token specified by the user but instead of # connecting to its own account we will force it to go to an # another account. In a normal scenario if that user don't # have the reseller right it will just fail but since the # reseller account can connect to every account it is allowed # by the swift_auth middleware. force_tenant = None if ':' in access: access, force_tenant = access.split(':') # Authenticate request. creds = {'credentials': {'access': access, 'token': token, 'signature': signature}} creds_json = jsonutils.dumps(creds) self.logger.debug('Connecting to Keystone sending this JSON: %s' % creds_json) # NOTE(vish): We could save a call to keystone by having # keystone return token, tenant, user, and roles # from this call. # # NOTE(chmou): We still have the same problem we would need to # change token_auth to detect if we already # identified and not doing a second query and just # pass it through to swiftauth in this case. try: resp = self._json_request(creds_json) except ServiceError as e: resp = e.args[0] msg = 'Received error, exiting middleware with error: %s' self.logger.debug(msg % (resp.status_code)) return resp(environ, start_response) self.logger.debug('Keystone Reply: Status: %d, Output: %s' % ( resp.status_code, resp.content)) try: identity_info = resp.json() token_id = str(identity_info['access']['token']['id']) tenant = identity_info['access']['token']['tenant'] except (ValueError, KeyError): error = 'Error on keystone reply: %d %s' self.logger.debug(error % (resp.status_code, str(resp.content))) return self.deny_request('InvalidURI')(environ, start_response) req.headers['X-Auth-Token'] = token_id tenant_to_connect = force_tenant or tenant['id'] self.logger.debug('Connecting with tenant: %s' % (tenant_to_connect)) new_tenant_name = '%s%s' % (self.reseller_prefix, tenant_to_connect) environ['PATH_INFO'] = environ['PATH_INFO'].replace(account, new_tenant_name) return self.app(environ, start_response) def filter_factory(global_conf, **local_conf): """Returns a WSGI filter app for use with paste.deploy.""" conf = global_conf.copy() conf.update(local_conf) def auth_filter(app): return S3Token(app, conf) return auth_filter python-keystoneclient-0.7.1/keystoneclient/middleware/__init__.py0000664000175400017540000000000012315030450026501 0ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/tests/0000775000175400017540000000000012315030547023436 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/tests/test_shell.py0000664000175400017540000005643112315030450026160 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import argparse import json import logging import os import sys import uuid import fixtures import mock import six import testtools from testtools import matchers from keystoneclient import exceptions from keystoneclient import session from keystoneclient import shell as openstack_shell from keystoneclient.tests import utils from keystoneclient.v2_0 import shell as shell_v2_0 DEFAULT_USERNAME = 'username' DEFAULT_PASSWORD = 'password' DEFAULT_TENANT_ID = 'tenant_id' DEFAULT_TENANT_NAME = 'tenant_name' DEFAULT_AUTH_URL = 'http://127.0.0.1:5000/v2.0/' class NoExitArgumentParser(argparse.ArgumentParser): def error(self, message): raise exceptions.CommandError(message) class ShellTest(utils.TestCase): FAKE_ENV = { 'OS_USERNAME': DEFAULT_USERNAME, 'OS_PASSWORD': DEFAULT_PASSWORD, 'OS_TENANT_ID': DEFAULT_TENANT_ID, 'OS_TENANT_NAME': DEFAULT_TENANT_NAME, 'OS_AUTH_URL': DEFAULT_AUTH_URL, } def _tolerant_shell(self, cmd): t_shell = openstack_shell.OpenStackIdentityShell(NoExitArgumentParser) t_shell.main(cmd.split()) # Patch os.environ to avoid required auth info. def setUp(self): super(ShellTest, self).setUp() for var in os.environ: if var.startswith("OS_"): self.useFixture(fixtures.EnvironmentVariable(var, "")) for var in self.FAKE_ENV: self.useFixture(fixtures.EnvironmentVariable(var, self.FAKE_ENV[var])) # Make a fake shell object, a helping wrapper to call it, and a quick # way of asserting that certain API calls were made. global shell, _shell, assert_called, assert_called_anytime _shell = openstack_shell.OpenStackIdentityShell() shell = lambda cmd: _shell.main(cmd.split()) def test_help_unknown_command(self): self.assertRaises(exceptions.CommandError, shell, 'help %s' % uuid.uuid4().hex) def shell(self, argstr): orig = sys.stdout clean_env = {} _old_env, os.environ = os.environ, clean_env.copy() try: sys.stdout = six.StringIO() _shell = openstack_shell.OpenStackIdentityShell() _shell.main(argstr.split()) except SystemExit: exc_type, exc_value, exc_traceback = sys.exc_info() self.assertEqual(exc_value.code, 0) finally: out = sys.stdout.getvalue() sys.stdout.close() sys.stdout = orig os.environ = _old_env return out def test_help_no_args(self): do_tenant_mock = mock.MagicMock() with mock.patch('keystoneclient.shell.OpenStackIdentityShell.do_help', do_tenant_mock): self.shell('') assert do_tenant_mock.called def test_help(self): required = 'usage:' help_text = self.shell('help') self.assertThat(help_text, matchers.MatchesRegex(required)) def test_help_command(self): required = 'usage: keystone user-create' help_text = self.shell('help user-create') self.assertThat(help_text, matchers.MatchesRegex(required)) def test_auth_no_credentials(self): with testtools.ExpectedException( exceptions.CommandError, 'Expecting'): self.shell('user-list') def test_debug(self): logging_mock = mock.MagicMock() with mock.patch('logging.basicConfig', logging_mock): self.assertRaises(exceptions.CommandError, self.shell, '--debug user-list') self.assertTrue(logging_mock.called) self.assertEqual([(), {'level': logging.DEBUG}], list(logging_mock.call_args)) def test_auth_password_authurl_no_username(self): with testtools.ExpectedException( exceptions.CommandError, 'Expecting a username provided via either'): self.shell('--os-password=%s --os-auth-url=%s user-list' % (uuid.uuid4().hex, uuid.uuid4().hex)) def test_auth_username_password_no_authurl(self): with testtools.ExpectedException( exceptions.CommandError, 'Expecting an auth URL via either'): self.shell('--os-password=%s --os-username=%s user-list' % (uuid.uuid4().hex, uuid.uuid4().hex)) def test_token_no_endpoint(self): with testtools.ExpectedException( exceptions.CommandError, 'Expecting an endpoint provided'): self.shell('--os-token=%s user-list' % uuid.uuid4().hex) def test_endpoint_no_token(self): with testtools.ExpectedException( exceptions.CommandError, 'Expecting a token provided'): self.shell('--os-endpoint=http://10.0.0.1:5000/v2.0/ user-list') def test_shell_args(self): do_tenant_mock = mock.MagicMock() with mock.patch('keystoneclient.v2_0.shell.do_user_list', do_tenant_mock): shell('user-list') assert do_tenant_mock.called ((a, b), c) = do_tenant_mock.call_args actual = (b.os_auth_url, b.os_password, b.os_tenant_id, b.os_tenant_name, b.os_username, b.os_identity_api_version) expect = (DEFAULT_AUTH_URL, DEFAULT_PASSWORD, DEFAULT_TENANT_ID, DEFAULT_TENANT_NAME, DEFAULT_USERNAME, '') self.assertTrue(all([x == y for x, y in zip(actual, expect)])) # Old_style options shell('--os_auth_url http://0.0.0.0:5000/ --os_password xyzpdq ' '--os_tenant_id 1234 --os_tenant_name fred ' '--os_username barney ' '--os_identity_api_version 2.0 user-list') assert do_tenant_mock.called ((a, b), c) = do_tenant_mock.call_args actual = (b.os_auth_url, b.os_password, b.os_tenant_id, b.os_tenant_name, b.os_username, b.os_identity_api_version) expect = ('http://0.0.0.0:5000/', 'xyzpdq', '1234', 'fred', 'barney', '2.0') self.assertTrue(all([x == y for x, y in zip(actual, expect)])) # New-style options shell('--os-auth-url http://1.1.1.1:5000/ --os-password xyzpdq ' '--os-tenant-id 4321 --os-tenant-name wilma ' '--os-username betty ' '--os-identity-api-version 2.0 user-list') assert do_tenant_mock.called ((a, b), c) = do_tenant_mock.call_args actual = (b.os_auth_url, b.os_password, b.os_tenant_id, b.os_tenant_name, b.os_username, b.os_identity_api_version) expect = ('http://1.1.1.1:5000/', 'xyzpdq', '4321', 'wilma', 'betty', '2.0') self.assertTrue(all([x == y for x, y in zip(actual, expect)])) # Test keyring options shell('--os-auth-url http://1.1.1.1:5000/ --os-password xyzpdq ' '--os-tenant-id 4321 --os-tenant-name wilma ' '--os-username betty ' '--os-identity-api-version 2.0 ' '--os-cache ' '--stale-duration 500 ' '--force-new-token user-list') assert do_tenant_mock.called ((a, b), c) = do_tenant_mock.call_args actual = (b.os_auth_url, b.os_password, b.os_tenant_id, b.os_tenant_name, b.os_username, b.os_identity_api_version, b.os_cache, b.stale_duration, b.force_new_token) expect = ('http://1.1.1.1:5000/', 'xyzpdq', '4321', 'wilma', 'betty', '2.0', True, '500', True) self.assertTrue(all([x == y for x, y in zip(actual, expect)])) # Test os-identity-api-version fall back to 2.0 shell('--os-identity-api-version 3.0 user-list') assert do_tenant_mock.called self.assertTrue(b.os_identity_api_version, '2.0') def test_shell_user_create_args(self): """Test user-create args.""" do_uc_mock = mock.MagicMock() # grab the decorators for do_user_create uc_func = getattr(shell_v2_0, 'do_user_create') do_uc_mock.arguments = getattr(uc_func, 'arguments', []) with mock.patch('keystoneclient.v2_0.shell.do_user_create', do_uc_mock): # Old_style options # Test case with one --tenant_id args present: ec2 creds shell('user-create --name=FOO ' '--pass=secrete --tenant_id=barrr --enabled=true') assert do_uc_mock.called ((a, b), c) = do_uc_mock.call_args actual = (b.os_auth_url, b.os_password, b.os_tenant_id, b.os_tenant_name, b.os_username, b.os_identity_api_version) expect = (DEFAULT_AUTH_URL, DEFAULT_PASSWORD, DEFAULT_TENANT_ID, DEFAULT_TENANT_NAME, DEFAULT_USERNAME, '') self.assertTrue(all([x == y for x, y in zip(actual, expect)])) actual = (b.tenant_id, b.name, b.passwd, b.enabled) expect = ('barrr', 'FOO', 'secrete', 'true') self.assertTrue(all([x == y for x, y in zip(actual, expect)])) # New-style options # Test case with one --tenant args present: ec2 creds shell('user-create --name=foo ' '--pass=secrete --tenant=BARRR --enabled=true') assert do_uc_mock.called ((a, b), c) = do_uc_mock.call_args actual = (b.os_auth_url, b.os_password, b.os_tenant_id, b.os_tenant_name, b.os_username, b.os_identity_api_version) expect = (DEFAULT_AUTH_URL, DEFAULT_PASSWORD, DEFAULT_TENANT_ID, DEFAULT_TENANT_NAME, DEFAULT_USERNAME, '') self.assertTrue(all([x == y for x, y in zip(actual, expect)])) actual = (b.tenant, b.name, b.passwd, b.enabled) expect = ('BARRR', 'foo', 'secrete', 'true') self.assertTrue(all([x == y for x, y in zip(actual, expect)])) # New-style options # Test case with one --tenant-id args present: ec2 creds shell('user-create --name=foo ' '--pass=secrete --tenant-id=BARRR --enabled=true') assert do_uc_mock.called ((a, b), c) = do_uc_mock.call_args actual = (b.os_auth_url, b.os_password, b.os_tenant_id, b.os_tenant_name, b.os_username, b.os_identity_api_version) expect = (DEFAULT_AUTH_URL, DEFAULT_PASSWORD, DEFAULT_TENANT_ID, DEFAULT_TENANT_NAME, DEFAULT_USERNAME, '') self.assertTrue(all([x == y for x, y in zip(actual, expect)])) actual = (b.tenant, b.name, b.passwd, b.enabled) expect = ('BARRR', 'foo', 'secrete', 'true') self.assertTrue(all([x == y for x, y in zip(actual, expect)])) # Old_style options # Test case with --os_tenant_id and --tenant_id args present shell('--os_tenant_id=os-tenant user-create --name=FOO ' '--pass=secrete --tenant_id=barrr --enabled=true') assert do_uc_mock.called ((a, b), c) = do_uc_mock.call_args actual = (b.os_auth_url, b.os_password, b.os_tenant_id, b.os_tenant_name, b.os_username, b.os_identity_api_version) expect = (DEFAULT_AUTH_URL, DEFAULT_PASSWORD, 'os-tenant', DEFAULT_TENANT_NAME, DEFAULT_USERNAME, '') self.assertTrue(all([x == y for x, y in zip(actual, expect)])) actual = (b.tenant_id, b.name, b.passwd, b.enabled) expect = ('barrr', 'FOO', 'secrete', 'true') self.assertTrue(all([x == y for x, y in zip(actual, expect)])) # New-style options # Test case with --os-tenant-id and --tenant-id args present shell('--os-tenant-id=ostenant user-create --name=foo ' '--pass=secrete --tenant-id=BARRR --enabled=true') assert do_uc_mock.called ((a, b), c) = do_uc_mock.call_args actual = (b.os_auth_url, b.os_password, b.os_tenant_id, b.os_tenant_name, b.os_username, b.os_identity_api_version) expect = (DEFAULT_AUTH_URL, DEFAULT_PASSWORD, 'ostenant', DEFAULT_TENANT_NAME, DEFAULT_USERNAME, '') self.assertTrue(all([x == y for x, y in zip(actual, expect)])) actual = (b.tenant, b.name, b.passwd, b.enabled) expect = ('BARRR', 'foo', 'secrete', 'true') self.assertTrue(all([x == y for x, y in zip(actual, expect)])) def test_do_tenant_create(self): do_tenant_mock = mock.MagicMock() with mock.patch('keystoneclient.v2_0.shell.do_tenant_create', do_tenant_mock): shell('tenant-create') assert do_tenant_mock.called # FIXME(dtroyer): how do you test the decorators? #shell('tenant-create --tenant-name wilma ' # '--description "fred\'s wife"') #assert do_tenant_mock.called def test_do_tenant_list(self): do_tenant_mock = mock.MagicMock() with mock.patch('keystoneclient.v2_0.shell.do_tenant_list', do_tenant_mock): shell('tenant-list') assert do_tenant_mock.called def test_shell_tenant_id_args(self): """Test a corner case where --tenant_id appears on the command-line twice. """ do_ec2_mock = mock.MagicMock() # grab the decorators for do_ec2_create_credentials ec2_func = getattr(shell_v2_0, 'do_ec2_credentials_create') do_ec2_mock.arguments = getattr(ec2_func, 'arguments', []) with mock.patch('keystoneclient.v2_0.shell.do_ec2_credentials_create', do_ec2_mock): # Old_style options # Test case with one --tenant_id args present: ec2 creds shell('ec2-credentials-create ' '--tenant_id=ec2-tenant --user_id=ec2-user') assert do_ec2_mock.called ((a, b), c) = do_ec2_mock.call_args actual = (b.os_auth_url, b.os_password, b.os_tenant_id, b.os_tenant_name, b.os_username, b.os_identity_api_version) expect = (DEFAULT_AUTH_URL, DEFAULT_PASSWORD, DEFAULT_TENANT_ID, DEFAULT_TENANT_NAME, DEFAULT_USERNAME, '') self.assertTrue(all([x == y for x, y in zip(actual, expect)])) actual = (b.tenant_id, b.user_id) expect = ('ec2-tenant', 'ec2-user') self.assertTrue(all([x == y for x, y in zip(actual, expect)])) # New-style options # Test case with one --tenant-id args present: ec2 creds shell('ec2-credentials-create ' '--tenant-id=dash-tenant --user-id=dash-user') assert do_ec2_mock.called ((a, b), c) = do_ec2_mock.call_args actual = (b.os_auth_url, b.os_password, b.os_tenant_id, b.os_tenant_name, b.os_username, b.os_identity_api_version) expect = (DEFAULT_AUTH_URL, DEFAULT_PASSWORD, DEFAULT_TENANT_ID, DEFAULT_TENANT_NAME, DEFAULT_USERNAME, '') self.assertTrue(all([x == y for x, y in zip(actual, expect)])) actual = (b.tenant_id, b.user_id) expect = ('dash-tenant', 'dash-user') self.assertTrue(all([x == y for x, y in zip(actual, expect)])) # Old_style options # Test case with two --tenant_id args present shell('--os_tenant_id=os-tenant ec2-credentials-create ' '--tenant_id=ec2-tenant --user_id=ec2-user') assert do_ec2_mock.called ((a, b), c) = do_ec2_mock.call_args actual = (b.os_auth_url, b.os_password, b.os_tenant_id, b.os_tenant_name, b.os_username, b.os_identity_api_version) expect = (DEFAULT_AUTH_URL, DEFAULT_PASSWORD, 'os-tenant', DEFAULT_TENANT_NAME, DEFAULT_USERNAME, '') self.assertTrue(all([x == y for x, y in zip(actual, expect)])) actual = (b.tenant_id, b.user_id) expect = ('ec2-tenant', 'ec2-user') self.assertTrue(all([x == y for x, y in zip(actual, expect)])) # New-style options # Test case with two --tenant-id args present shell('--os-tenant-id=ostenant ec2-credentials-create ' '--tenant-id=dash-tenant --user-id=dash-user') assert do_ec2_mock.called ((a, b), c) = do_ec2_mock.call_args actual = (b.os_auth_url, b.os_password, b.os_tenant_id, b.os_tenant_name, b.os_username, b.os_identity_api_version) expect = (DEFAULT_AUTH_URL, DEFAULT_PASSWORD, 'ostenant', DEFAULT_TENANT_NAME, DEFAULT_USERNAME, '') self.assertTrue(all([x == y for x, y in zip(actual, expect)])) actual = (b.tenant_id, b.user_id) expect = ('dash-tenant', 'dash-user') self.assertTrue(all([x == y for x, y in zip(actual, expect)])) def test_do_ec2_get(self): do_shell_mock = mock.MagicMock() with mock.patch('keystoneclient.v2_0.shell.do_ec2_credentials_create', do_shell_mock): shell('ec2-credentials-create') assert do_shell_mock.called with mock.patch('keystoneclient.v2_0.shell.do_ec2_credentials_get', do_shell_mock): shell('ec2-credentials-get') assert do_shell_mock.called with mock.patch('keystoneclient.v2_0.shell.do_ec2_credentials_list', do_shell_mock): shell('ec2-credentials-list') assert do_shell_mock.called with mock.patch('keystoneclient.v2_0.shell.do_ec2_credentials_delete', do_shell_mock): shell('ec2-credentials-delete') assert do_shell_mock.called def test_timeout_parse_invalid_type(self): for f in ['foobar', 'xyz']: cmd = '--timeout %s endpoint-create' % (f) self.assertRaises(exceptions.CommandError, self._tolerant_shell, cmd) def test_timeout_parse_invalid_number(self): for f in [-1, 0]: cmd = '--timeout %s endpoint-create' % (f) self.assertRaises(exceptions.CommandError, self._tolerant_shell, cmd) def test_do_timeout(self): response_mock = mock.MagicMock() response_mock.status_code = 200 response_mock.text = json.dumps({ 'endpoints': [], }) request_mock = mock.MagicMock(return_value=response_mock) with mock.patch.object(session.requests, 'request', request_mock): shell(('--timeout 2 --os-token=blah --os-endpoint=blah' ' --os-auth-url=blah.com endpoint-list')) request_mock.assert_called_with(mock.ANY, mock.ANY, timeout=2, allow_redirects=False, headers=mock.ANY, verify=mock.ANY) def test_do_endpoints(self): do_shell_mock = mock.MagicMock() # grab the decorators for do_endpoint_create shell_func = getattr(shell_v2_0, 'do_endpoint_create') do_shell_mock.arguments = getattr(shell_func, 'arguments', []) with mock.patch('keystoneclient.v2_0.shell.do_endpoint_create', do_shell_mock): # Old_style options # Test create args shell('endpoint-create ' '--service_id=2 --publicurl=http://example.com:1234/go ' '--adminurl=http://example.com:9876/adm') assert do_shell_mock.called ((a, b), c) = do_shell_mock.call_args actual = (b.os_auth_url, b.os_password, b.os_tenant_id, b.os_tenant_name, b.os_username, b.os_identity_api_version) expect = (DEFAULT_AUTH_URL, DEFAULT_PASSWORD, DEFAULT_TENANT_ID, DEFAULT_TENANT_NAME, DEFAULT_USERNAME, '') self.assertTrue(all([x == y for x, y in zip(actual, expect)])) actual = (b.service, b.publicurl, b.adminurl) expect = ('2', 'http://example.com:1234/go', 'http://example.com:9876/adm') self.assertTrue(all([x == y for x, y in zip(actual, expect)])) # New-style options # Test create args shell('endpoint-create ' '--service-id=3 --publicurl=http://example.com:4321/go ' '--adminurl=http://example.com:9876/adm') assert do_shell_mock.called ((a, b), c) = do_shell_mock.call_args actual = (b.os_auth_url, b.os_password, b.os_tenant_id, b.os_tenant_name, b.os_username, b.os_identity_api_version) expect = (DEFAULT_AUTH_URL, DEFAULT_PASSWORD, DEFAULT_TENANT_ID, DEFAULT_TENANT_NAME, DEFAULT_USERNAME, '') self.assertTrue(all([x == y for x, y in zip(actual, expect)])) actual = (b.service, b.publicurl, b.adminurl) expect = ('3', 'http://example.com:4321/go', 'http://example.com:9876/adm') self.assertTrue(all([x == y for x, y in zip(actual, expect)])) # New-style options # Test create args shell('endpoint-create ' '--service=3 --publicurl=http://example.com:4321/go ' '--adminurl=http://example.com:9876/adm') assert do_shell_mock.called ((a, b), c) = do_shell_mock.call_args actual = (b.os_auth_url, b.os_password, b.os_tenant_id, b.os_tenant_name, b.os_username, b.os_identity_api_version) expect = (DEFAULT_AUTH_URL, DEFAULT_PASSWORD, DEFAULT_TENANT_ID, DEFAULT_TENANT_NAME, DEFAULT_USERNAME, '') self.assertTrue(all([x == y for x, y in zip(actual, expect)])) actual = (b.service, b.publicurl, b.adminurl) expect = ('3', 'http://example.com:4321/go', 'http://example.com:9876/adm') self.assertTrue(all([x == y for x, y in zip(actual, expect)])) python-keystoneclient-0.7.1/keystoneclient/tests/apiclient/0000775000175400017540000000000012315030547025406 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/tests/apiclient/test_exceptions.py0000664000175400017540000000463512315030450031201 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import six from keystoneclient.apiclient import exceptions from keystoneclient.tests import utils class FakeResponse(object): json_data = {} def __init__(self, **kwargs): for key, value in six.iteritems(kwargs): setattr(self, key, value) def json(self): return self.json_data class ExceptionsArgsTest(utils.TestCase): def assert_exception(self, ex_cls, method, url, status_code, json_data): ex = exceptions.from_response( FakeResponse(status_code=status_code, headers={"Content-Type": "application/json"}, json_data=json_data), method, url) self.assertIsInstance(ex, ex_cls) self.assertEqual(ex.message, json_data["error"]["message"]) self.assertEqual(ex.details, json_data["error"]["details"]) self.assertEqual(ex.method, method) self.assertEqual(ex.url, url) self.assertEqual(ex.http_status, status_code) def test_from_response_known(self): method = "GET" url = "/fake" status_code = 400 json_data = {"error": {"message": "fake message", "details": "fake details"}} self.assert_exception( exceptions.BadRequest, method, url, status_code, json_data) def test_from_response_unknown(self): method = "POST" url = "/fake-unknown" status_code = 499 json_data = {"error": {"message": "fake unknown message", "details": "fake unknown details"}} self.assert_exception( exceptions.HTTPClientError, method, url, status_code, json_data) status_code = 600 self.assert_exception( exceptions.HTTPError, method, url, status_code, json_data) python-keystoneclient-0.7.1/keystoneclient/tests/auth/0000775000175400017540000000000012315030547024377 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/tests/auth/test_identity_v2.py0000664000175400017540000001721212315030450030244 0ustar jenkinsjenkins00000000000000# vim: tabstop=4 shiftwidth=4 softtabstop=4 # 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 httpretty from six.moves import urllib from keystoneclient.auth.identity import v2 from keystoneclient import exceptions from keystoneclient import session from keystoneclient.tests import utils class V2IdentityPlugin(utils.TestCase): TEST_ROOT_URL = 'http://127.0.0.1:5000/' TEST_URL = '%s%s' % (TEST_ROOT_URL, 'v2.0') TEST_ROOT_ADMIN_URL = 'http://127.0.0.1:35357/' TEST_ADMIN_URL = '%s%s' % (TEST_ROOT_ADMIN_URL, 'v2.0') TEST_PASS = 'password' TEST_SERVICE_CATALOG = [{ "endpoints": [{ "adminURL": "http://cdn.admin-nets.local:8774/v1.0", "region": "RegionOne", "internalURL": "http://127.0.0.1:8774/v1.0", "publicURL": "http://cdn.admin-nets.local:8774/v1.0/" }], "type": "nova_compat", "name": "nova_compat" }, { "endpoints": [{ "adminURL": "http://nova/novapi/admin", "region": "RegionOne", "internalURL": "http://nova/novapi/internal", "publicURL": "http://nova/novapi/public" }], "type": "compute", "name": "nova" }, { "endpoints": [{ "adminURL": "http://glance/glanceapi/admin", "region": "RegionOne", "internalURL": "http://glance/glanceapi/internal", "publicURL": "http://glance/glanceapi/public" }], "type": "image", "name": "glance" }, { "endpoints": [{ "adminURL": TEST_ADMIN_URL, "region": "RegionOne", "internalURL": "http://127.0.0.1:5000/v2.0", "publicURL": "http://127.0.0.1:5000/v2.0" }], "type": "identity", "name": "keystone" }, { "endpoints": [{ "adminURL": "http://swift/swiftapi/admin", "region": "RegionOne", "internalURL": "http://swift/swiftapi/internal", "publicURL": "http://swift/swiftapi/public" }], "type": "object-store", "name": "swift" }] def setUp(self): super(V2IdentityPlugin, self).setUp() self.TEST_RESPONSE_DICT = { "access": { "token": { "expires": "2020-01-01T00:00:10.000123Z", "id": self.TEST_TOKEN, "tenant": { "id": self.TEST_TENANT_ID }, }, "user": { "id": self.TEST_USER }, "serviceCatalog": self.TEST_SERVICE_CATALOG, }, } def stub_auth(self, **kwargs): self.stub_url(httpretty.POST, ['tokens'], **kwargs) @httpretty.activate def test_authenticate_with_username_password(self): self.stub_auth(json=self.TEST_RESPONSE_DICT) a = v2.Password(self.TEST_URL, username=self.TEST_USER, password=self.TEST_PASS) s = session.Session(a) s.get_token() req = {'auth': {'passwordCredentials': {'username': self.TEST_USER, 'password': self.TEST_PASS}}} self.assertRequestBodyIs(json=req) self.assertEqual(s.auth.auth_ref.auth_token, self.TEST_TOKEN) @httpretty.activate def test_authenticate_with_username_password_scoped(self): self.stub_auth(json=self.TEST_RESPONSE_DICT) a = v2.Password(self.TEST_URL, username=self.TEST_USER, password=self.TEST_PASS, tenant_id=self.TEST_TENANT_ID) s = session.Session(a) s.get_token() req = {'auth': {'passwordCredentials': {'username': self.TEST_USER, 'password': self.TEST_PASS}, 'tenantId': self.TEST_TENANT_ID}} self.assertRequestBodyIs(json=req) self.assertEqual(s.auth.auth_ref.auth_token, self.TEST_TOKEN) @httpretty.activate def test_authenticate_with_token(self): self.stub_auth(json=self.TEST_RESPONSE_DICT) a = v2.Token(self.TEST_URL, 'foo') s = session.Session(a) s.get_token() req = {'auth': {'token': {'id': 'foo'}}} self.assertRequestBodyIs(json=req) self.assertRequestHeaderEqual('x-Auth-Token', 'foo') self.assertEqual(s.auth.auth_ref.auth_token, self.TEST_TOKEN) def test_missing_auth_params(self): self.assertRaises(exceptions.NoMatchingPlugin, v2.Auth._factory, self.TEST_URL) @httpretty.activate def test_with_trust_id(self): self.stub_auth(json=self.TEST_RESPONSE_DICT) a = v2.Password(self.TEST_URL, username=self.TEST_USER, password=self.TEST_PASS, trust_id='trust') s = session.Session(a) s.get_token() req = {'auth': {'passwordCredentials': {'username': self.TEST_USER, 'password': self.TEST_PASS}, 'trust_id': 'trust'}} self.assertRequestBodyIs(json=req) self.assertEqual(s.auth.auth_ref.auth_token, self.TEST_TOKEN) @httpretty.activate def _do_service_url_test(self, base_url, endpoint_filter): self.stub_auth(json=self.TEST_RESPONSE_DICT) self.stub_url(httpretty.GET, ['path'], base_url=base_url, body='SUCCESS', status=200) a = v2.Password(self.TEST_URL, username=self.TEST_USER, password=self.TEST_PASS) s = session.Session(auth=a) resp = s.get('/path', endpoint_filter=endpoint_filter) self.assertEqual(resp.status_code, 200) path = "%s/%s" % (urllib.parse.urlparse(base_url).path, 'path') self.assertEqual(httpretty.last_request().path, path) def test_service_url(self): endpoint_filter = {'service_type': 'compute', 'interface': 'admin'} self._do_service_url_test('http://nova/novapi/admin', endpoint_filter) def test_service_url_defaults_to_public(self): endpoint_filter = {'service_type': 'compute'} self._do_service_url_test('http://nova/novapi/public', endpoint_filter) @httpretty.activate def test_endpoint_filter_without_service_type_fails(self): self.stub_auth(json=self.TEST_RESPONSE_DICT) a = v2.Password(self.TEST_URL, username=self.TEST_USER, password=self.TEST_PASS) s = session.Session(auth=a) self.assertRaises(exceptions.EndpointNotFound, s.get, '/path', endpoint_filter={'interface': 'admin'}) @httpretty.activate def test_full_url_overrides_endpoint_filter(self): self.stub_auth(json=self.TEST_RESPONSE_DICT) self.stub_url(httpretty.GET, [], base_url='http://testurl/', body='SUCCESS', status=200) a = v2.Password(self.TEST_URL, username=self.TEST_USER, password=self.TEST_PASS) s = session.Session(auth=a) resp = s.get('http://testurl/', endpoint_filter={'service_type': 'compute'}) self.assertEqual(resp.status_code, 200) self.assertEqual(resp.text, 'SUCCESS') python-keystoneclient-0.7.1/keystoneclient/tests/auth/test_identity_v3.py0000664000175400017540000003223512315030450030247 0ustar jenkinsjenkins00000000000000# vim: tabstop=4 shiftwidth=4 softtabstop=4 # 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 copy import httpretty from six.moves import urllib from keystoneclient import access from keystoneclient.auth.identity import v3 from keystoneclient import exceptions from keystoneclient import session from keystoneclient.tests import utils class V3IdentityPlugin(utils.TestCase): TEST_ROOT_URL = 'http://127.0.0.1:5000/' TEST_URL = '%s%s' % (TEST_ROOT_URL, 'v3') TEST_ROOT_ADMIN_URL = 'http://127.0.0.1:35357/' TEST_ADMIN_URL = '%s%s' % (TEST_ROOT_ADMIN_URL, 'v3') TEST_PASS = 'password' TEST_SERVICE_CATALOG = [{ "endpoints": [{ "url": "http://cdn.admin-nets.local:8774/v1.0/", "region": "RegionOne", "interface": "public" }, { "url": "http://127.0.0.1:8774/v1.0", "region": "RegionOne", "interface": "internal" }, { "url": "http://cdn.admin-nets.local:8774/v1.0", "region": "RegionOne", "interface": "admin" }], "type": "nova_compat" }, { "endpoints": [{ "url": "http://nova/novapi/public", "region": "RegionOne", "interface": "public" }, { "url": "http://nova/novapi/internal", "region": "RegionOne", "interface": "internal" }, { "url": "http://nova/novapi/admin", "region": "RegionOne", "interface": "admin" }], "type": "compute" }, { "endpoints": [{ "url": "http://glance/glanceapi/public", "region": "RegionOne", "interface": "public" }, { "url": "http://glance/glanceapi/internal", "region": "RegionOne", "interface": "internal" }, { "url": "http://glance/glanceapi/admin", "region": "RegionOne", "interface": "admin" }], "type": "image", "name": "glance" }, { "endpoints": [{ "url": "http://127.0.0.1:5000/v3", "region": "RegionOne", "interface": "public" }, { "url": "http://127.0.0.1:5000/v3", "region": "RegionOne", "interface": "internal" }, { "url": TEST_ADMIN_URL, "region": "RegionOne", "interface": "admin" }], "type": "identity" }, { "endpoints": [{ "url": "http://swift/swiftapi/public", "region": "RegionOne", "interface": "public" }, { "url": "http://swift/swiftapi/internal", "region": "RegionOne", "interface": "internal" }, { "url": "http://swift/swiftapi/admin", "region": "RegionOne", "interface": "admin" }], "type": "object-store" }] def setUp(self): super(V3IdentityPlugin, self).setUp() self.TEST_RESPONSE_DICT = { "token": { "methods": [ "token", "password" ], "expires_at": "2020-01-01T00:00:10.000123Z", "project": { "domain": { "id": self.TEST_DOMAIN_ID, "name": self.TEST_DOMAIN_NAME }, "id": self.TEST_TENANT_ID, "name": self.TEST_TENANT_NAME }, "user": { "domain": { "id": self.TEST_DOMAIN_ID, "name": self.TEST_DOMAIN_NAME }, "id": self.TEST_USER, "name": self.TEST_USER }, "issued_at": "2013-05-29T16:55:21.468960Z", "catalog": self.TEST_SERVICE_CATALOG }, } def stub_auth(self, subject_token=None, **kwargs): if not subject_token: subject_token = self.TEST_TOKEN self.stub_url(httpretty.POST, ['auth', 'tokens'], X_Subject_Token=subject_token, **kwargs) @httpretty.activate def test_authenticate_with_username_password(self): self.stub_auth(json=self.TEST_RESPONSE_DICT) a = v3.Password(self.TEST_URL, username=self.TEST_USER, password=self.TEST_PASS) s = session.Session(auth=a) s.get_token() req = {'auth': {'identity': {'methods': ['password'], 'password': {'user': {'name': self.TEST_USER, 'password': self.TEST_PASS}}}}} self.assertRequestBodyIs(json=req) self.assertEqual(s.auth.auth_ref.auth_token, self.TEST_TOKEN) @httpretty.activate def test_authenticate_with_username_password_domain_scoped(self): self.stub_auth(json=self.TEST_RESPONSE_DICT) a = v3.Password(self.TEST_URL, username=self.TEST_USER, password=self.TEST_PASS, domain_id=self.TEST_DOMAIN_ID) s = session.Session(a) s.get_token() req = {'auth': {'identity': {'methods': ['password'], 'password': {'user': {'name': self.TEST_USER, 'password': self.TEST_PASS}}}, 'scope': {'domain': {'id': self.TEST_DOMAIN_ID}}}} self.assertRequestBodyIs(json=req) self.assertEqual(s.auth.auth_ref.auth_token, self.TEST_TOKEN) @httpretty.activate def test_authenticate_with_username_password_project_scoped(self): self.stub_auth(json=self.TEST_RESPONSE_DICT) a = v3.Password(self.TEST_URL, username=self.TEST_USER, password=self.TEST_PASS, project_id=self.TEST_DOMAIN_ID) s = session.Session(a) s.get_token() req = {'auth': {'identity': {'methods': ['password'], 'password': {'user': {'name': self.TEST_USER, 'password': self.TEST_PASS}}}, 'scope': {'project': {'id': self.TEST_DOMAIN_ID}}}} self.assertRequestBodyIs(json=req) self.assertEqual(s.auth.auth_ref.auth_token, self.TEST_TOKEN) self.assertEqual(s.auth.auth_ref.project_id, self.TEST_DOMAIN_ID) @httpretty.activate def test_authenticate_with_token(self): self.stub_auth(json=self.TEST_RESPONSE_DICT) a = v3.Token(self.TEST_URL, self.TEST_TOKEN) s = session.Session(auth=a) s.get_token() req = {'auth': {'identity': {'methods': ['token'], 'token': {'id': self.TEST_TOKEN}}}} self.assertRequestBodyIs(json=req) self.assertEqual(s.auth.auth_ref.auth_token, self.TEST_TOKEN) def test_missing_auth_params(self): self.assertRaises(exceptions.AuthorizationFailure, v3.Auth._factory, self.TEST_URL) @httpretty.activate def test_with_expired(self): self.stub_auth(json=self.TEST_RESPONSE_DICT) d = copy.deepcopy(self.TEST_RESPONSE_DICT) d['token']['expires_at'] = '2000-01-01T00:00:10.000123Z' a = v3.Password(self.TEST_URL, username='username', password='password') a.auth_ref = access.AccessInfo.factory(body=d) s = session.Session(auth=a) s.get_token() self.assertEqual(a.auth_ref['expires_at'], self.TEST_RESPONSE_DICT['token']['expires_at']) def test_with_domain_and_project_scoping(self): a = v3.Password(self.TEST_URL, username='username', password='password', project_id='project', domain_id='domain') self.assertRaises(exceptions.AuthorizationFailure, a.get_token, None) @httpretty.activate def test_with_trust_id(self): self.stub_auth(json=self.TEST_RESPONSE_DICT) a = v3.Password(self.TEST_URL, username=self.TEST_USER, password=self.TEST_PASS, trust_id='trust') s = session.Session(a) s.get_token() req = {'auth': {'identity': {'methods': ['password'], 'password': {'user': {'name': self.TEST_USER, 'password': self.TEST_PASS}}}, 'scope': {'OS-TRUST:trust': {'id': 'trust'}}}} self.assertRequestBodyIs(json=req) self.assertEqual(s.auth.auth_ref.auth_token, self.TEST_TOKEN) @httpretty.activate def test_with_multiple_mechanisms_factory(self): self.stub_auth(json=self.TEST_RESPONSE_DICT) p = v3.PasswordMethod(username=self.TEST_USER, password=self.TEST_PASS) t = v3.TokenMethod(token='foo') a = v3.Auth(self.TEST_URL, [p, t], trust_id='trust') s = session.Session(a) s.get_token() req = {'auth': {'identity': {'methods': ['password', 'token'], 'password': {'user': {'name': self.TEST_USER, 'password': self.TEST_PASS}}, 'token': {'id': 'foo'}}, 'scope': {'OS-TRUST:trust': {'id': 'trust'}}}} self.assertRequestBodyIs(json=req) self.assertEqual(s.auth.auth_ref.auth_token, self.TEST_TOKEN) @httpretty.activate def test_with_multiple_mechanisms(self): self.stub_auth(json=self.TEST_RESPONSE_DICT) p = v3.PasswordMethod(username=self.TEST_USER, password=self.TEST_PASS) t = v3.TokenMethod(token='foo') a = v3.Auth(self.TEST_URL, [p, t], trust_id='trust') s = session.Session(auth=a) s.get_token() req = {'auth': {'identity': {'methods': ['password', 'token'], 'password': {'user': {'name': self.TEST_USER, 'password': self.TEST_PASS}}, 'token': {'id': 'foo'}}, 'scope': {'OS-TRUST:trust': {'id': 'trust'}}}} self.assertRequestBodyIs(json=req) self.assertEqual(s.auth.auth_ref.auth_token, self.TEST_TOKEN) def test_with_multiple_scopes(self): s = session.Session() a = v3.Password(self.TEST_URL, username=self.TEST_USER, password=self.TEST_PASS, domain_id='x', project_id='x') self.assertRaises(exceptions.AuthorizationFailure, a.get_auth_ref, s) a = v3.Password(self.TEST_URL, username=self.TEST_USER, password=self.TEST_PASS, domain_id='x', trust_id='x') self.assertRaises(exceptions.AuthorizationFailure, a.get_auth_ref, s) @httpretty.activate def _do_service_url_test(self, base_url, endpoint_filter): self.stub_auth(json=self.TEST_RESPONSE_DICT) self.stub_url(httpretty.GET, ['path'], base_url=base_url, body='SUCCESS', status=200) a = v3.Password(self.TEST_URL, username=self.TEST_USER, password=self.TEST_PASS) s = session.Session(auth=a) resp = s.get('/path', endpoint_filter=endpoint_filter) self.assertEqual(resp.status_code, 200) path = "%s/%s" % (urllib.parse.urlparse(base_url).path, 'path') self.assertEqual(httpretty.last_request().path, path) def test_service_url(self): endpoint_filter = {'service_type': 'compute', 'interface': 'admin'} self._do_service_url_test('http://nova/novapi/admin', endpoint_filter) def test_service_url_defaults_to_public(self): endpoint_filter = {'service_type': 'compute'} self._do_service_url_test('http://nova/novapi/public', endpoint_filter) @httpretty.activate def test_endpoint_filter_without_service_type_fails(self): self.stub_auth(json=self.TEST_RESPONSE_DICT) a = v3.Password(self.TEST_URL, username=self.TEST_USER, password=self.TEST_PASS) s = session.Session(auth=a) self.assertRaises(exceptions.EndpointNotFound, s.get, '/path', endpoint_filter={'interface': 'admin'}) @httpretty.activate def test_full_url_overrides_endpoint_filter(self): self.stub_auth(json=self.TEST_RESPONSE_DICT) self.stub_url(httpretty.GET, [], base_url='http://testurl/', body='SUCCESS', status=200) a = v3.Password(self.TEST_URL, username=self.TEST_USER, password=self.TEST_PASS) s = session.Session(auth=a) resp = s.get('http://testurl/', endpoint_filter={'service_type': 'compute'}) self.assertEqual(resp.status_code, 200) self.assertEqual(resp.text, 'SUCCESS') python-keystoneclient-0.7.1/keystoneclient/tests/auth/test_token_endpoint.py0000664000175400017540000000225612315030450031026 0ustar jenkinsjenkins00000000000000# 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 httpretty from keystoneclient.auth import token_endpoint from keystoneclient import session from keystoneclient.tests import utils class TokenEndpointTest(utils.TestCase): TEST_TOKEN = 'aToken' TEST_URL = 'http://server/prefix' @httpretty.activate def test_basic_case(self): httpretty.register_uri(httpretty.GET, self.TEST_URL, body='body') a = token_endpoint.Token(self.TEST_URL, self.TEST_TOKEN) s = session.Session(auth=a) data = s.get(self.TEST_URL, authenticated=True) self.assertEqual(data.text, 'body') self.assertRequestHeaderEqual('X-Auth-Token', self.TEST_TOKEN) python-keystoneclient-0.7.1/keystoneclient/tests/auth/__init__.py0000664000175400017540000000000012315030450026467 0ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/tests/v3/0000775000175400017540000000000012315030547023766 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/tests/v3/client_fixtures.py0000664000175400017540000003130712315030451027545 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from __future__ import unicode_literals UNSCOPED_TOKEN = { 'token': { 'methods': [ 'password' ], 'catalog': {}, 'expires_at': '2010-11-01T03:32:15-05:00', 'user': { 'domain': { 'id': '4e6893b7ba0b4006840c3845660b86ed', 'name': 'exampledomain' }, 'id': 'c4da488862bd435c9e6c0275a0d0e49a', 'name': 'exampleuser', } } } DOMAIN_SCOPED_TOKEN = { 'token': { 'methods': [ 'password' ], 'catalog': [{ 'endpoints': [{ 'url': 'http://public.com:8776/v1/None', 'region': 'RegionOne', 'interface': 'public' }, { 'url': 'http://internal:8776/v1/None', 'region': 'RegionOne', 'interface': 'internal' }, { 'url': 'http://admin:8776/v1/None', 'region': 'RegionOne', 'interface': 'admin' }], 'type': 'volume' }, { 'endpoints': [{ 'url': 'http://public.com:9292/v1', 'region': 'RegionOne', 'interface': 'public' }, { 'url': 'http://internal:9292/v1', 'region': 'RegionOne', 'interface': 'internal' }, { 'url': 'http://admin:9292/v1', 'region': 'RegionOne', 'interface': 'admin' }], 'type': 'image' }, { 'endpoints': [{ 'url': 'http://public.com:8774/v1.1/None', 'region': 'RegionOne', 'interface': 'public' }, { 'url': 'http://internal:8774/v1.1/None', 'region': 'RegionOne', 'interface': 'internal' }, { 'url': 'http://admin:8774/v1.1/None', 'region': 'RegionOne', 'interface': 'admin' }], 'type': 'compute' }, { 'endpoints': [{ 'url': 'http://public.com:8773/services/Cloud', 'region': 'RegionOne', 'interface': 'public' }, { 'url': 'http://internal:8773/services/Cloud', 'region': 'RegionOne', 'interface': 'internal' }, { 'url': 'http://admin:8773/services/Admin', 'region': 'RegionOne', 'interface': 'admin' }], 'type': 'ec2' }, { 'endpoints': [{ 'url': 'http://public.com:5000/v3', 'region': 'RegionOne', 'interface': 'public' }, { 'url': 'http://internal:5000/v3', 'region': 'RegionOne', 'interface': 'internal' }, { 'url': 'http://admin:35357/v3', 'region': 'RegionOne', 'interface': 'admin' }], 'type': 'identity' }], 'expires_at': '2010-11-01T03:32:15-05:00', 'user': { 'domain': { 'id': '4e6893b7ba0b4006840c3845660b86ed', 'name': 'exampledomain' }, 'id': 'c4da488862bd435c9e6c0275a0d0e49a', 'name': 'exampleuser', }, 'roles': [ { "id": "76e72a", "links": { "self": "http://identity:35357/v3/roles/76e72a" }, "name": "admin" }, { "id": "f4f392", "links": { "self": "http://identity:35357/v3/roles/f4f392" }, "name": "member" } ], 'domain': { 'id': '8e9283b7ba0b1038840c3842058b86ab', 'name': 'anotherdomain' }, } } PROJECT_SCOPED_TOKEN = { 'token': { 'methods': [ 'password' ], 'catalog': [{ 'endpoints': [{ 'url': 'http://public.com:8776/v1/225da22d3ce34b15877ea70b2a575f58', 'region': 'RegionOne', 'interface': 'public' }, { 'url': 'http://internal:8776/v1/225da22d3ce34b15877ea70b2a575f58', 'region': 'RegionOne', 'interface': 'internal' }, { 'url': 'http://admin:8776/v1/225da22d3ce34b15877ea70b2a575f58', 'region': 'RegionOne', 'interface': 'admin' }], 'type': 'volume' }, { 'endpoints': [{ 'url': 'http://public.com:9292/v1', 'region': 'RegionOne', 'interface': 'public' }, { 'url': 'http://internal:9292/v1', 'region': 'RegionOne', 'interface': 'internal' }, { 'url': 'http://admin:9292/v1', 'region': 'RegionOne', 'interface': 'admin' }], 'type': 'image' }, { 'endpoints': [{ 'url': 'http://public.com:8774/v2/225da22d3ce34b15877ea70b2a575f58', 'region': 'RegionOne', 'interface': 'public' }, { 'url': 'http://internal:8774/v2/225da22d3ce34b15877ea70b2a575f58', 'region': 'RegionOne', 'interface': 'internal' }, { 'url': 'http://admin:8774/v2/225da22d3ce34b15877ea70b2a575f58', 'region': 'RegionOne', 'interface': 'admin' }], 'type': 'compute' }, { 'endpoints': [{ 'url': 'http://public.com:8773/services/Cloud', 'region': 'RegionOne', 'interface': 'public' }, { 'url': 'http://internal:8773/services/Cloud', 'region': 'RegionOne', 'interface': 'internal' }, { 'url': 'http://admin:8773/services/Admin', 'region': 'RegionOne', 'interface': 'admin' }], 'type': 'ec2' }, { 'endpoints': [{ 'url': 'http://public.com:5000/v3', 'region': 'RegionOne', 'interface': 'public' }, { 'url': 'http://internal:5000/v3', 'region': 'RegionOne', 'interface': 'internal' }, { 'url': 'http://admin:35357/v3', 'region': 'RegionOne', 'interface': 'admin' }], 'type': 'identity' }], 'expires_at': '2010-11-01T03:32:15-05:00', 'user': { 'domain': { 'id': '4e6893b7ba0b4006840c3845660b86ed', 'name': 'exampledomain' }, 'id': 'c4da488862bd435c9e6c0275a0d0e49a', 'name': 'exampleuser', }, 'roles': [ { "id": "76e72a", "links": { "self": "http://identity:35357/v3/roles/76e72a" }, "name": "admin" }, { "id": "f4f392", "links": { "self": "http://identity:35357/v3/roles/f4f392" }, "name": "member" } ], 'project': { 'domain': { 'id': '4e6893b7ba0b4006840c3845660b86ed', 'name': 'exampledomain' }, 'id': '225da22d3ce34b15877ea70b2a575f58', 'name': 'exampleproject', }, } } AUTH_SUBJECT_TOKEN = '3e2813b7ba0b4006840c3825860b86ed' AUTH_RESPONSE_HEADERS = { 'X-Subject-Token': AUTH_SUBJECT_TOKEN } AUTH_RESPONSE_BODY = { 'token': { 'methods': [ 'password' ], 'expires_at': '2010-11-01T03:32:15-05:00', 'project': { 'domain': { 'id': '123', 'name': 'aDomain' }, 'id': '345', 'name': 'aTenant' }, 'user': { 'domain': { 'id': '1', 'name': 'aDomain' }, 'id': '567', 'name': 'test', 'roles': [ { "id": "76e72a", "links": { "self": "http://identity:35357/v3/roles/76e72a" }, "name": "admin" }, { "id": "f4f392", "links": { "self": "http://identity:35357/v3/roles/f4f392" }, "name": "member" } ], }, 'issued_at': '2010-10-31T03:32:15-05:00', 'catalog': [{ 'endpoints': [{ 'url': 'https://compute.north.host/novapi/public', 'region': 'North', 'interface': 'public' }, { 'url': 'https://compute.north.host/novapi/internal', 'region': 'North', 'interface': 'internal' }, { 'url': 'https://compute.north.host/novapi/admin', 'region': 'North', 'interface': 'admin' }], 'type': 'compute' }, { 'endpoints': [{ 'url': 'http://swift.north.host/swiftapi/public', 'region': 'South', 'interface': 'public' }, { 'url': 'http://swift.north.host/swiftapi/internal', 'region': 'South', 'interface': 'internal' }, { 'url': 'http://swift.north.host/swiftapi/admin', 'region': 'South', 'interface': 'admin' }], 'type': 'object-store' }, { 'endpoints': [{ 'url': 'http://glance.north.host/glanceapi/public', 'region': 'North', 'interface': 'public' }, { 'url': 'http://glance.north.host/glanceapi/internal', 'region': 'North', 'interface': 'internal' }, { 'url': 'http://glance.north.host/glanceapi/admin', 'region': 'North', 'interface': 'admin' }, { 'url': 'http://glance.south.host/glanceapi/public', 'region': 'South', 'interface': 'public' }, { 'url': 'http://glance.south.host/glanceapi/internal', 'region': 'South', 'interface': 'internal' }, { 'url': 'http://glance.south.host/glanceapi/admin', 'region': 'South', 'interface': 'admin' }], 'type': 'image' }] } } TRUST_TOKEN = { 'token': { 'methods': [ 'password' ], 'catalog': {}, 'expires_at': '2010-11-01T03:32:15-05:00', "OS-TRUST:trust": { "id": "fe0aef", "impersonation": False, "links": { "self": "http://identity:35357/v3/trusts/fe0aef" }, "trustee_user": { "id": "0ca8f6", "links": { "self": "http://identity:35357/v3/users/0ca8f6" } }, "trustor_user": { "id": "bd263c", "links": { "self": "http://identity:35357/v3/users/bd263c" } } }, 'user': { 'domain': { 'id': '4e6893b7ba0b4006840c3845660b86ed', 'name': 'exampledomain' }, 'id': '0ca8f6', 'name': 'exampleuser', } } } python-keystoneclient-0.7.1/keystoneclient/tests/v3/test_roles.py0000664000175400017540000002611012315030451026515 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import uuid import httpretty from keystoneclient import exceptions from keystoneclient.tests.v3 import utils from keystoneclient.v3 import roles class RoleTests(utils.TestCase, utils.CrudTests): def setUp(self): super(RoleTests, self).setUp() self.key = 'role' self.collection_key = 'roles' self.model = roles.Role self.manager = self.client.roles def new_ref(self, **kwargs): kwargs = super(RoleTests, self).new_ref(**kwargs) kwargs.setdefault('name', uuid.uuid4().hex) return kwargs @httpretty.activate def test_domain_role_grant(self): user_id = uuid.uuid4().hex domain_id = uuid.uuid4().hex ref = self.new_ref() self.stub_url(httpretty.PUT, ['domains', domain_id, 'users', user_id, self.collection_key, ref['id']], status=201) self.manager.grant(role=ref['id'], domain=domain_id, user=user_id) @httpretty.activate def test_domain_group_role_grant(self): group_id = uuid.uuid4().hex domain_id = uuid.uuid4().hex ref = self.new_ref() self.stub_url(httpretty.PUT, ['domains', domain_id, 'groups', group_id, self.collection_key, ref['id']], status=201) self.manager.grant(role=ref['id'], domain=domain_id, group=group_id) @httpretty.activate def test_domain_role_list(self): user_id = uuid.uuid4().hex domain_id = uuid.uuid4().hex ref_list = [self.new_ref(), self.new_ref()] self.stub_entity(httpretty.GET, ['domains', domain_id, 'users', user_id, self.collection_key], entity=ref_list) self.manager.list(domain=domain_id, user=user_id) @httpretty.activate def test_domain_group_role_list(self): group_id = uuid.uuid4().hex domain_id = uuid.uuid4().hex ref_list = [self.new_ref(), self.new_ref()] self.stub_entity(httpretty.GET, ['domains', domain_id, 'groups', group_id, self.collection_key], entity=ref_list) self.manager.list(domain=domain_id, group=group_id) @httpretty.activate def test_domain_role_check(self): user_id = uuid.uuid4().hex domain_id = uuid.uuid4().hex ref = self.new_ref() self.stub_url(httpretty.HEAD, ['domains', domain_id, 'users', user_id, self.collection_key, ref['id']], status=204) self.manager.check(role=ref['id'], domain=domain_id, user=user_id) @httpretty.activate def test_domain_group_role_check(self): return group_id = uuid.uuid4().hex domain_id = uuid.uuid4().hex ref = self.new_ref() self.stub_url(httpretty.HEAD, ['domains', domain_id, 'groups', group_id, self.collection_key, ref['id']], status=204) self.manager.check(role=ref['id'], domain=domain_id, group=group_id) @httpretty.activate def test_domain_role_revoke(self): user_id = uuid.uuid4().hex domain_id = uuid.uuid4().hex ref = self.new_ref() self.stub_url(httpretty.DELETE, ['domains', domain_id, 'users', user_id, self.collection_key, ref['id']], status=204) self.manager.revoke(role=ref['id'], domain=domain_id, user=user_id) @httpretty.activate def test_domain_group_role_revoke(self): group_id = uuid.uuid4().hex domain_id = uuid.uuid4().hex ref = self.new_ref() self.stub_url(httpretty.DELETE, ['domains', domain_id, 'groups', group_id, self.collection_key, ref['id']], status=204) self.manager.revoke(role=ref['id'], domain=domain_id, group=group_id) @httpretty.activate def test_project_role_grant(self): user_id = uuid.uuid4().hex project_id = uuid.uuid4().hex ref = self.new_ref() self.stub_url(httpretty.PUT, ['projects', project_id, 'users', user_id, self.collection_key, ref['id']], status=201) self.manager.grant(role=ref['id'], project=project_id, user=user_id) @httpretty.activate def test_project_group_role_grant(self): group_id = uuid.uuid4().hex project_id = uuid.uuid4().hex ref = self.new_ref() self.stub_url(httpretty.PUT, ['projects', project_id, 'groups', group_id, self.collection_key, ref['id']], status=201) self.manager.grant(role=ref['id'], project=project_id, group=group_id) @httpretty.activate def test_project_role_list(self): user_id = uuid.uuid4().hex project_id = uuid.uuid4().hex ref_list = [self.new_ref(), self.new_ref()] self.stub_entity(httpretty.GET, ['projects', project_id, 'users', user_id, self.collection_key], entity=ref_list) self.manager.list(project=project_id, user=user_id) @httpretty.activate def test_project_group_role_list(self): group_id = uuid.uuid4().hex project_id = uuid.uuid4().hex ref_list = [self.new_ref(), self.new_ref()] self.stub_entity(httpretty.GET, ['projects', project_id, 'groups', group_id, self.collection_key], entity=ref_list) self.manager.list(project=project_id, group=group_id) @httpretty.activate def test_project_role_check(self): user_id = uuid.uuid4().hex project_id = uuid.uuid4().hex ref = self.new_ref() self.stub_url(httpretty.HEAD, ['projects', project_id, 'users', user_id, self.collection_key, ref['id']], status=200) self.manager.check(role=ref['id'], project=project_id, user=user_id) @httpretty.activate def test_project_group_role_check(self): group_id = uuid.uuid4().hex project_id = uuid.uuid4().hex ref = self.new_ref() self.stub_url(httpretty.HEAD, ['projects', project_id, 'groups', group_id, self.collection_key, ref['id']], status=200) self.manager.check(role=ref['id'], project=project_id, group=group_id) @httpretty.activate def test_project_role_revoke(self): user_id = uuid.uuid4().hex project_id = uuid.uuid4().hex ref = self.new_ref() self.stub_url(httpretty.DELETE, ['projects', project_id, 'users', user_id, self.collection_key, ref['id']], status=204) self.manager.revoke(role=ref['id'], project=project_id, user=user_id) @httpretty.activate def test_project_group_role_revoke(self): group_id = uuid.uuid4().hex project_id = uuid.uuid4().hex ref = self.new_ref() self.stub_url(httpretty.DELETE, ['projects', project_id, 'groups', group_id, self.collection_key, ref['id']], status=204) self.manager.revoke(role=ref['id'], project=project_id, group=group_id) @httpretty.activate def test_domain_project_role_grant_fails(self): user_id = uuid.uuid4().hex project_id = uuid.uuid4().hex domain_id = uuid.uuid4().hex ref = self.new_ref() self.assertRaises( exceptions.ValidationError, self.manager.grant, role=ref['id'], domain=domain_id, project=project_id, user=user_id) def test_domain_project_role_list_fails(self): user_id = uuid.uuid4().hex project_id = uuid.uuid4().hex domain_id = uuid.uuid4().hex self.assertRaises( exceptions.ValidationError, self.manager.list, domain=domain_id, project=project_id, user=user_id) def test_domain_project_role_check_fails(self): user_id = uuid.uuid4().hex project_id = uuid.uuid4().hex domain_id = uuid.uuid4().hex ref = self.new_ref() self.assertRaises( exceptions.ValidationError, self.manager.check, role=ref['id'], domain=domain_id, project=project_id, user=user_id) def test_domain_project_role_revoke_fails(self): user_id = uuid.uuid4().hex project_id = uuid.uuid4().hex domain_id = uuid.uuid4().hex ref = self.new_ref() self.assertRaises( exceptions.ValidationError, self.manager.revoke, role=ref['id'], domain=domain_id, project=project_id, user=user_id) def test_user_group_role_grant_fails(self): user_id = uuid.uuid4().hex group_id = uuid.uuid4().hex project_id = uuid.uuid4().hex ref = self.new_ref() self.assertRaises( exceptions.ValidationError, self.manager.grant, role=ref['id'], project=project_id, group=group_id, user=user_id) def test_user_group_role_list_fails(self): user_id = uuid.uuid4().hex group_id = uuid.uuid4().hex project_id = uuid.uuid4().hex self.assertRaises( exceptions.ValidationError, self.manager.list, project=project_id, group=group_id, user=user_id) def test_user_group_role_check_fails(self): user_id = uuid.uuid4().hex group_id = uuid.uuid4().hex project_id = uuid.uuid4().hex ref = self.new_ref() self.assertRaises( exceptions.ValidationError, self.manager.check, role=ref['id'], project=project_id, group=group_id, user=user_id) def test_user_group_role_revoke_fails(self): user_id = uuid.uuid4().hex group_id = uuid.uuid4().hex project_id = uuid.uuid4().hex ref = self.new_ref() self.assertRaises( exceptions.ValidationError, self.manager.revoke, role=ref['id'], project=project_id, group=group_id, user=user_id) python-keystoneclient-0.7.1/keystoneclient/tests/v3/test_policies.py0000664000175400017540000000214612315030451027203 0ustar jenkinsjenkins00000000000000# 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 uuid from keystoneclient.tests.v3 import utils from keystoneclient.v3 import policies class PolicyTests(utils.TestCase, utils.CrudTests): def setUp(self): super(PolicyTests, self).setUp() self.key = 'policy' self.collection_key = 'policies' self.model = policies.Policy self.manager = self.client.policies def new_ref(self, **kwargs): kwargs = super(PolicyTests, self).new_ref(**kwargs) kwargs.setdefault('type', uuid.uuid4().hex) kwargs.setdefault('blob', uuid.uuid4().hex) return kwargs python-keystoneclient-0.7.1/keystoneclient/tests/v3/test_groups.py0000664000175400017540000000426312315030451026715 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import uuid import httpretty from keystoneclient.tests.v3 import utils from keystoneclient.v3 import groups class GroupTests(utils.TestCase, utils.CrudTests): def setUp(self): super(GroupTests, self).setUp() self.key = 'group' self.collection_key = 'groups' self.model = groups.Group self.manager = self.client.groups def new_ref(self, **kwargs): kwargs = super(GroupTests, self).new_ref(**kwargs) kwargs.setdefault('name', uuid.uuid4().hex) return kwargs @httpretty.activate def test_list_groups_for_user(self): user_id = uuid.uuid4().hex ref_list = [self.new_ref(), self.new_ref()] self.stub_entity(httpretty.GET, ['users', user_id, self.collection_key], status=200, entity=ref_list) returned_list = self.manager.list(user=user_id) self.assertEqual(len(ref_list), len(returned_list)) [self.assertIsInstance(r, self.model) for r in returned_list] @httpretty.activate def test_list_groups_for_domain(self): ref_list = [self.new_ref(), self.new_ref()] domain_id = uuid.uuid4().hex self.stub_entity(httpretty.GET, [self.collection_key], status=200, entity=ref_list) returned_list = self.manager.list(domain=domain_id) self.assertTrue(len(ref_list), len(returned_list)) [self.assertIsInstance(r, self.model) for r in returned_list] self.assertEqual(httpretty.last_request().querystring, {'domain_id': [domain_id]}) python-keystoneclient-0.7.1/keystoneclient/tests/v3/test_client.py0000664000175400017540000002051312315030451026650 0ustar jenkinsjenkins00000000000000# 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 copy import json import httpretty from keystoneclient import exceptions from keystoneclient.tests.v3 import client_fixtures from keystoneclient.tests.v3 import utils from keystoneclient.v3 import client class KeystoneClientTest(utils.TestCase): @httpretty.activate def test_unscoped_init(self): self.stub_auth(json=client_fixtures.UNSCOPED_TOKEN) c = client.Client(user_domain_name='exampledomain', username='exampleuser', password='password', auth_url=self.TEST_URL) self.assertIsNotNone(c.auth_ref) self.assertFalse(c.auth_ref.domain_scoped) self.assertFalse(c.auth_ref.project_scoped) self.assertEqual(c.auth_user_id, 'c4da488862bd435c9e6c0275a0d0e49a') @httpretty.activate def test_domain_scoped_init(self): self.stub_auth(json=client_fixtures.DOMAIN_SCOPED_TOKEN) c = client.Client(user_id='c4da488862bd435c9e6c0275a0d0e49a', password='password', domain_name='exampledomain', auth_url=self.TEST_URL) self.assertIsNotNone(c.auth_ref) self.assertTrue(c.auth_ref.domain_scoped) self.assertFalse(c.auth_ref.project_scoped) self.assertEqual(c.auth_user_id, 'c4da488862bd435c9e6c0275a0d0e49a') self.assertEqual(c.auth_domain_id, '8e9283b7ba0b1038840c3842058b86ab') @httpretty.activate def test_project_scoped_init(self): self.stub_auth(json=client_fixtures.PROJECT_SCOPED_TOKEN), c = client.Client(user_id='c4da488862bd435c9e6c0275a0d0e49a', password='password', user_domain_name='exampledomain', project_name='exampleproject', auth_url=self.TEST_URL) self.assertIsNotNone(c.auth_ref) self.assertFalse(c.auth_ref.domain_scoped) self.assertTrue(c.auth_ref.project_scoped) self.assertEqual(c.auth_user_id, 'c4da488862bd435c9e6c0275a0d0e49a') self.assertEqual(c.auth_tenant_id, '225da22d3ce34b15877ea70b2a575f58') @httpretty.activate def test_auth_ref_load(self): self.stub_auth(json=client_fixtures.PROJECT_SCOPED_TOKEN) c = client.Client(user_id='c4da488862bd435c9e6c0275a0d0e49a', password='password', project_id='225da22d3ce34b15877ea70b2a575f58', auth_url=self.TEST_URL) cache = json.dumps(c.auth_ref) new_client = client.Client(auth_ref=json.loads(cache)) self.assertIsNotNone(new_client.auth_ref) self.assertFalse(new_client.auth_ref.domain_scoped) self.assertTrue(new_client.auth_ref.project_scoped) self.assertEqual(new_client.username, 'exampleuser') self.assertIsNone(new_client.password) self.assertEqual(new_client.management_url, 'http://admin:35357/v3') @httpretty.activate def test_auth_ref_load_with_overridden_arguments(self): new_auth_url = 'https://newkeystone.com/v3' self.stub_auth(json=client_fixtures.PROJECT_SCOPED_TOKEN) self.stub_auth(json=client_fixtures.PROJECT_SCOPED_TOKEN, base_url=new_auth_url) c = client.Client(user_id='c4da488862bd435c9e6c0275a0d0e49a', password='password', project_id='225da22d3ce34b15877ea70b2a575f58', auth_url=self.TEST_URL) cache = json.dumps(c.auth_ref) new_client = client.Client(auth_ref=json.loads(cache), auth_url=new_auth_url) self.assertIsNotNone(new_client.auth_ref) self.assertFalse(new_client.auth_ref.domain_scoped) self.assertTrue(new_client.auth_ref.project_scoped) self.assertEqual(new_client.auth_url, new_auth_url) self.assertEqual(new_client.username, 'exampleuser') self.assertIsNone(new_client.password) self.assertEqual(new_client.management_url, 'http://admin:35357/v3') @httpretty.activate def test_trust_init(self): self.stub_auth(json=client_fixtures.TRUST_TOKEN) c = client.Client(user_domain_name='exampledomain', username='exampleuser', password='password', auth_url=self.TEST_URL, trust_id='fe0aef') self.assertIsNotNone(c.auth_ref) self.assertFalse(c.auth_ref.domain_scoped) self.assertFalse(c.auth_ref.project_scoped) self.assertEqual(c.auth_ref.trust_id, 'fe0aef') self.assertTrue(c.auth_ref.trust_scoped) self.assertEqual(c.auth_user_id, '0ca8f6') def test_init_err_no_auth_url(self): self.assertRaises(exceptions.AuthorizationFailure, client.Client, username='exampleuser', password='password') def _management_url_is_updated(self, fixture, **kwargs): second = copy.deepcopy(fixture) first_url = 'http://admin:35357/v3' second_url = "http://secondurl:%d/v3'" for entry in second['token']['catalog']: if entry['type'] == 'identity': entry['endpoints'] = [{ 'url': second_url % 5000, 'region': 'RegionOne', 'interface': 'public' }, { 'url': second_url % 5000, 'region': 'RegionOne', 'interface': 'internal' }, { 'url': second_url % 35357, 'region': 'RegionOne', 'interface': 'admin' }] self.stub_auth(json=fixture) cl = client.Client(username='exampleuser', password='password', auth_url=self.TEST_URL, **kwargs) self.assertEqual(cl.management_url, first_url) self.stub_auth(json=second) cl.authenticate() self.assertEqual(cl.management_url, second_url % 35357) @httpretty.activate def test_management_url_is_updated_with_project(self): self._management_url_is_updated(client_fixtures.PROJECT_SCOPED_TOKEN, project_name='exampleproject') @httpretty.activate def test_management_url_is_updated_with_domain(self): self._management_url_is_updated(client_fixtures.DOMAIN_SCOPED_TOKEN, domain_name='exampledomain') @httpretty.activate def test_client_with_region_name_passes_to_service_catalog(self): # NOTE(jamielennox): this is deprecated behaviour that should be # removed ASAP, however must remain compatible. self.stub_auth(json=client_fixtures.AUTH_RESPONSE_BODY) cl = client.Client(username='exampleuser', password='password', tenant_name='exampleproject', auth_url=self.TEST_URL, region_name='North') self.assertEqual(cl.service_catalog.url_for(service_type='image'), 'http://glance.north.host/glanceapi/public') cl = client.Client(username='exampleuser', password='password', tenant_name='exampleproject', auth_url=self.TEST_URL, region_name='South') self.assertEqual(cl.service_catalog.url_for(service_type='image'), 'http://glance.south.host/glanceapi/public') python-keystoneclient-0.7.1/keystoneclient/tests/v3/utils.py0000664000175400017540000002544612315030451025505 0ustar jenkinsjenkins00000000000000# 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 uuid import httpretty import six from six.moves.urllib import parse as urlparse from keystoneclient.openstack.common import jsonutils from keystoneclient.tests import utils from keystoneclient.v3 import client TestResponse = utils.TestResponse def parameterize(ref): """Rewrites attributes to match the kwarg naming convention in client. >>> parameterize({'project_id': 0}) {'project': 0} """ params = ref.copy() for key in ref: if key[-3:] == '_id': params.setdefault(key[:-3], params.pop(key)) return params class UnauthenticatedTestCase(utils.TestCase): """Class used as base for unauthenticated calls.""" TEST_ROOT_URL = 'http://127.0.0.1:5000/' TEST_URL = '%s%s' % (TEST_ROOT_URL, 'v3') TEST_ROOT_ADMIN_URL = 'http://127.0.0.1:35357/' TEST_ADMIN_URL = '%s%s' % (TEST_ROOT_ADMIN_URL, 'v3') class TestCase(UnauthenticatedTestCase): TEST_ADMIN_IDENTITY_ENDPOINT = "http://127.0.0.1:35357/v3" TEST_SERVICE_CATALOG = [{ "endpoints": [{ "url": "http://cdn.admin-nets.local:8774/v1.0/", "region": "RegionOne", "interface": "public" }, { "url": "http://127.0.0.1:8774/v1.0", "region": "RegionOne", "interface": "internal" }, { "url": "http://cdn.admin-nets.local:8774/v1.0", "region": "RegionOne", "interface": "admin" }], "type": "nova_compat" }, { "endpoints": [{ "url": "http://nova/novapi/public", "region": "RegionOne", "interface": "public" }, { "url": "http://nova/novapi/internal", "region": "RegionOne", "interface": "internal" }, { "url": "http://nova/novapi/admin", "region": "RegionOne", "interface": "admin" }], "type": "compute" }, { "endpoints": [{ "url": "http://glance/glanceapi/public", "region": "RegionOne", "interface": "public" }, { "url": "http://glance/glanceapi/internal", "region": "RegionOne", "interface": "internal" }, { "url": "http://glance/glanceapi/admin", "region": "RegionOne", "interface": "admin" }], "type": "image", "name": "glance" }, { "endpoints": [{ "url": "http://127.0.0.1:5000/v3", "region": "RegionOne", "interface": "public" }, { "url": "http://127.0.0.1:5000/v3", "region": "RegionOne", "interface": "internal" }, { "url": TEST_ADMIN_IDENTITY_ENDPOINT, "region": "RegionOne", "interface": "admin" }], "type": "identity" }, { "endpoints": [{ "url": "http://swift/swiftapi/public", "region": "RegionOne", "interface": "public" }, { "url": "http://swift/swiftapi/internal", "region": "RegionOne", "interface": "internal" }, { "url": "http://swift/swiftapi/admin", "region": "RegionOne", "interface": "admin" }], "type": "object-store" }] def setUp(self): super(TestCase, self).setUp() self.client = client.Client(username=self.TEST_USER, token=self.TEST_TOKEN, tenant_name=self.TEST_TENANT_NAME, auth_url=self.TEST_URL, endpoint=self.TEST_URL) def stub_auth(self, subject_token=None, **kwargs): if not subject_token: subject_token = self.TEST_TOKEN self.stub_url(httpretty.POST, ['auth', 'tokens'], X_Subject_Token=subject_token, **kwargs) class CrudTests(object): key = None collection_key = None model = None manager = None path_prefix = None def new_ref(self, **kwargs): kwargs.setdefault('id', uuid.uuid4().hex) kwargs.setdefault(uuid.uuid4().hex, uuid.uuid4().hex) return kwargs def encode(self, entity): if isinstance(entity, dict): return {self.key: entity} if isinstance(entity, list): return {self.collection_key: entity} raise NotImplementedError('Are you sure you want to encode that?') def stub_entity(self, method, parts=None, entity=None, id=None, **kwargs): if entity: entity = self.encode(entity) kwargs['json'] = entity if not parts: parts = [self.collection_key] if self.path_prefix: parts.insert(0, self.path_prefix) if id: if not parts: parts = [] parts.append(id) self.stub_url(method, parts=parts, **kwargs) def assertEntityRequestBodyIs(self, entity): self.assertRequestBodyIs(json=self.encode(entity)) @httpretty.activate def test_create(self, ref=None, req_ref=None): ref = ref or self.new_ref() manager_ref = ref.copy() manager_ref.pop('id') # req_ref argument allows you to specify a different # signature for the request when the manager does some # conversion before doing the request (e.g converting # from datetime object to timestamp string) req_ref = (req_ref or ref).copy() req_ref.pop('id') self.stub_entity(httpretty.POST, entity=req_ref, status=201) returned = self.manager.create(**parameterize(manager_ref)) self.assertIsInstance(returned, self.model) for attr in req_ref: self.assertEqual( getattr(returned, attr), req_ref[attr], 'Expected different %s' % attr) self.assertEntityRequestBodyIs(req_ref) @httpretty.activate def test_get(self, ref=None): ref = ref or self.new_ref() self.stub_entity(httpretty.GET, id=ref['id'], entity=ref) returned = self.manager.get(ref['id']) self.assertIsInstance(returned, self.model) for attr in ref: self.assertEqual( getattr(returned, attr), ref[attr], 'Expected different %s' % attr) def _get_expected_path(self, expected_path=None): if not expected_path: if self.path_prefix: expected_path = 'v3/%s/%s' % (self.path_prefix, self.collection_key) else: expected_path = 'v3/%s' % self.collection_key return expected_path @httpretty.activate def test_list(self, ref_list=None, expected_path=None, expected_query=None, **filter_kwargs): ref_list = ref_list or [self.new_ref(), self.new_ref()] expected_path = self._get_expected_path(expected_path) httpretty.register_uri(httpretty.GET, urlparse.urljoin(self.TEST_URL, expected_path), body=jsonutils.dumps(self.encode(ref_list))) returned_list = self.manager.list(**filter_kwargs) self.assertEqual(len(ref_list), len(returned_list)) [self.assertIsInstance(r, self.model) for r in returned_list] # register_uri doesn't match the querystring component, so we have to # explicitly test the querystring component passed by the manager qs_args = httpretty.last_request().querystring qs_args_expected = expected_query or filter_kwargs for key, value in six.iteritems(qs_args_expected): self.assertIn(key, qs_args) # The httppretty.querystring value is a list # Note we convert the value to a string, as the query string # is always a string and the filter_kwargs may contain non-string # values, for example a boolean, causing the comaprison to fail. self.assertIn(str(value), qs_args[key]) # Also check that no query string args exist which are not expected for key in qs_args: self.assertIn(key, qs_args_expected) @httpretty.activate def test_list_params(self): ref_list = [self.new_ref()] filter_kwargs = {uuid.uuid4().hex: uuid.uuid4().hex} expected_path = self._get_expected_path() httpretty.register_uri(httpretty.GET, urlparse.urljoin(self.TEST_URL, expected_path), body=jsonutils.dumps(self.encode(ref_list))) self.manager.list(**filter_kwargs) self.assertQueryStringContains(**filter_kwargs) @httpretty.activate def test_find(self, ref=None): ref = ref or self.new_ref() ref_list = [ref] self.stub_entity(httpretty.GET, entity=ref_list) returned = self.manager.find(name=getattr(ref, 'name', None)) self.assertIsInstance(returned, self.model) for attr in ref: self.assertEqual( getattr(returned, attr), ref[attr], 'Expected different %s' % attr) if hasattr(ref, 'name'): self.assertQueryStringIs('name=%s' % ref['name']) else: self.assertQueryStringIs('') @httpretty.activate def test_update(self, ref=None, req_ref=None): ref = ref or self.new_ref() self.stub_entity(httpretty.PATCH, id=ref['id'], entity=ref) # req_ref argument allows you to specify a different # signature for the request when the manager does some # conversion before doing the request (e.g converting # from datetime object to timestamp string) req_ref = (req_ref or ref).copy() req_ref.pop('id') returned = self.manager.update(ref['id'], **parameterize(req_ref)) self.assertIsInstance(returned, self.model) for attr in ref: self.assertEqual( getattr(returned, attr), ref[attr], 'Expected different %s' % attr) self.assertEntityRequestBodyIs(req_ref) @httpretty.activate def test_delete(self, ref=None): ref = ref or self.new_ref() self.stub_entity(httpretty.DELETE, id=ref['id'], status=204) self.manager.delete(ref['id']) python-keystoneclient-0.7.1/keystoneclient/tests/v3/test_domains.py0000664000175400017540000000310712315030451027024 0ustar jenkinsjenkins00000000000000# 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 uuid from keystoneclient.tests.v3 import utils from keystoneclient.v3 import domains class DomainTests(utils.TestCase, utils.CrudTests): def setUp(self): super(DomainTests, self).setUp() self.key = 'domain' self.collection_key = 'domains' self.model = domains.Domain self.manager = self.client.domains def new_ref(self, **kwargs): kwargs = super(DomainTests, self).new_ref(**kwargs) kwargs.setdefault('enabled', True) kwargs.setdefault('name', uuid.uuid4().hex) return kwargs def test_list_filter_name(self): super(DomainTests, self).test_list(name='adomain123') def test_list_filter_enabled(self): super(DomainTests, self).test_list(enabled=True) def test_list_filter_disabled(self): # False is converted to '0' ref bug #1267530 expected_query = {'enabled': '0'} super(DomainTests, self).test_list(expected_query=expected_query, enabled=False) python-keystoneclient-0.7.1/keystoneclient/tests/v3/test_services.py0000664000175400017540000000222612315030451027216 0ustar jenkinsjenkins00000000000000# 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 uuid from keystoneclient.tests.v3 import utils from keystoneclient.v3 import services class ServiceTests(utils.TestCase, utils.CrudTests): def setUp(self): super(ServiceTests, self).setUp() self.key = 'service' self.collection_key = 'services' self.model = services.Service self.manager = self.client.services def new_ref(self, **kwargs): kwargs = super(ServiceTests, self).new_ref(**kwargs) kwargs.setdefault('name', uuid.uuid4().hex) kwargs.setdefault('type', uuid.uuid4().hex) kwargs.setdefault('enabled', True) return kwargs python-keystoneclient-0.7.1/keystoneclient/tests/v3/test_endpoints.py0000664000175400017540000000677012315030451027406 0ustar jenkinsjenkins00000000000000# 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 uuid from keystoneclient import exceptions from keystoneclient.tests.v3 import utils from keystoneclient.v3 import endpoints class EndpointTests(utils.TestCase, utils.CrudTests): def setUp(self): super(EndpointTests, self).setUp() self.key = 'endpoint' self.collection_key = 'endpoints' self.model = endpoints.Endpoint self.manager = self.client.endpoints def new_ref(self, **kwargs): kwargs = super(EndpointTests, self).new_ref(**kwargs) kwargs.setdefault('interface', 'public') kwargs.setdefault('region', uuid.uuid4().hex) kwargs.setdefault('service_id', uuid.uuid4().hex) kwargs.setdefault('url', uuid.uuid4().hex) kwargs.setdefault('enabled', True) return kwargs def test_create_public_interface(self): ref = self.new_ref(interface='public') self.test_create(ref) def test_create_admin_interface(self): ref = self.new_ref(interface='admin') self.test_create(ref) def test_create_internal_interface(self): ref = self.new_ref(interface='internal') self.test_create(ref) def test_create_invalid_interface(self): ref = self.new_ref(interface=uuid.uuid4().hex) self.assertRaises(exceptions.ValidationError, self.manager.create, **utils.parameterize(ref)) def test_update_public_interface(self): ref = self.new_ref(interface='public') self.test_update(ref) def test_update_admin_interface(self): ref = self.new_ref(interface='admin') self.test_update(ref) def test_update_internal_interface(self): ref = self.new_ref(interface='internal') self.test_update(ref) def test_update_invalid_interface(self): ref = self.new_ref(interface=uuid.uuid4().hex) ref['endpoint'] = "fake_endpoint" self.assertRaises(exceptions.ValidationError, self.manager.update, **utils.parameterize(ref)) def test_list_public_interface(self): interface = 'public' expected_path = 'v3/%s?interface=%s' % (self.collection_key, interface) self.test_list(expected_path=expected_path, interface=interface) def test_list_admin_interface(self): interface = 'admin' expected_path = 'v3/%s?interface=%s' % (self.collection_key, interface) self.test_list(expected_path=expected_path, interface=interface) def test_list_internal_interface(self): interface = 'admin' expected_path = 'v3/%s?interface=%s' % (self.collection_key, interface) self.test_list(expected_path=expected_path, interface=interface) def test_list_invalid_interface(self): interface = uuid.uuid4().hex expected_path = 'v3/%s?interface=%s' % (self.collection_key, interface) self.assertRaises(exceptions.ValidationError, self.manager.list, expected_path=expected_path, interface=interface) python-keystoneclient-0.7.1/keystoneclient/tests/v3/test_projects.py0000664000175400017540000000432512315030451027226 0ustar jenkinsjenkins00000000000000# 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 uuid import httpretty from keystoneclient.tests.v3 import utils from keystoneclient.v3 import projects class ProjectTests(utils.TestCase, utils.CrudTests): def setUp(self): super(ProjectTests, self).setUp() self.key = 'project' self.collection_key = 'projects' self.model = projects.Project self.manager = self.client.projects def new_ref(self, **kwargs): kwargs = super(ProjectTests, self).new_ref(**kwargs) kwargs.setdefault('domain_id', uuid.uuid4().hex) kwargs.setdefault('enabled', True) kwargs.setdefault('name', uuid.uuid4().hex) return kwargs @httpretty.activate def test_list_projects_for_user(self): ref_list = [self.new_ref(), self.new_ref()] user_id = uuid.uuid4().hex self.stub_entity(httpretty.GET, ['users', user_id, self.collection_key], entity=ref_list) returned_list = self.manager.list(user=user_id) self.assertEqual(len(ref_list), len(returned_list)) [self.assertIsInstance(r, self.model) for r in returned_list] @httpretty.activate def test_list_projects_for_domain(self): ref_list = [self.new_ref(), self.new_ref()] domain_id = uuid.uuid4().hex self.stub_entity(httpretty.GET, [self.collection_key], entity=ref_list) returned_list = self.manager.list(domain=domain_id) self.assertEqual(len(ref_list), len(returned_list)) [self.assertIsInstance(r, self.model) for r in returned_list] self.assertEqual(httpretty.last_request().querystring, {'domain_id': [domain_id]}) python-keystoneclient-0.7.1/keystoneclient/tests/v3/test_auth.py0000664000175400017540000003461112315030451026337 0ustar jenkinsjenkins00000000000000# 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 httpretty import six from keystoneclient import exceptions from keystoneclient.openstack.common import jsonutils from keystoneclient.tests.v3 import utils from keystoneclient.v3 import client class AuthenticateAgainstKeystoneTests(utils.TestCase): def setUp(self): super(AuthenticateAgainstKeystoneTests, self).setUp() self.TEST_RESPONSE_DICT = { "token": { "methods": [ "token", "password" ], "expires_at": "2020-01-01T00:00:10.000123Z", "project": { "domain": { "id": self.TEST_DOMAIN_ID, "name": self.TEST_DOMAIN_NAME }, "id": self.TEST_TENANT_ID, "name": self.TEST_TENANT_NAME }, "user": { "domain": { "id": self.TEST_DOMAIN_ID, "name": self.TEST_DOMAIN_NAME }, "id": self.TEST_USER, "name": self.TEST_USER }, "issued_at": "2013-05-29T16:55:21.468960Z", "catalog": self.TEST_SERVICE_CATALOG }, } self.TEST_REQUEST_BODY = { "auth": { "identity": { "methods": ["password"], "password": { "user": { "domain": { "name": self.TEST_DOMAIN_NAME }, "name": self.TEST_USER, "password": self.TEST_TOKEN } } }, "scope": { "project": { "id": self.TEST_TENANT_ID }, } } } self.TEST_REQUEST_HEADERS = { 'Content-Type': 'application/json', 'User-Agent': 'python-keystoneclient' } self.TEST_RESPONSE_HEADERS = { 'X-Subject-Token': self.TEST_TOKEN } @httpretty.activate def test_authenticate_success(self): TEST_TOKEN = "abcdef" ident = self.TEST_REQUEST_BODY['auth']['identity'] del ident['password']['user']['domain'] del ident['password']['user']['name'] ident['password']['user']['id'] = self.TEST_USER self.stub_auth(json=self.TEST_RESPONSE_DICT, subject_token=TEST_TOKEN) cs = client.Client(user_id=self.TEST_USER, password=self.TEST_TOKEN, project_id=self.TEST_TENANT_ID, auth_url=self.TEST_URL) self.assertEqual(cs.auth_token, TEST_TOKEN) self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY) @httpretty.activate def test_authenticate_failure(self): ident = self.TEST_REQUEST_BODY['auth']['identity'] ident['password']['user']['password'] = 'bad_key' error = {"unauthorized": {"message": "Unauthorized", "code": "401"}} self.stub_auth(status=401, json=error) # Workaround for issue with assertRaises on python2.6 # where with assertRaises(exceptions.Unauthorized): doesn't work # right def client_create_wrapper(): client.Client(user_domain_name=self.TEST_DOMAIN_NAME, username=self.TEST_USER, password="bad_key", project_id=self.TEST_TENANT_ID, auth_url=self.TEST_URL) self.assertRaises(exceptions.Unauthorized, client_create_wrapper) self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY) @httpretty.activate def test_auth_redirect(self): self.stub_auth(status=305, body='Use proxy', location=self.TEST_ADMIN_URL + '/auth/tokens') self.stub_auth(json=self.TEST_RESPONSE_DICT, base_url=self.TEST_ADMIN_URL) cs = client.Client(user_domain_name=self.TEST_DOMAIN_NAME, username=self.TEST_USER, password=self.TEST_TOKEN, project_id=self.TEST_TENANT_ID, auth_url=self.TEST_URL) self.assertEqual(cs.management_url, self.TEST_RESPONSE_DICT["token"]["catalog"][3] ['endpoints'][2]["url"]) self.assertEqual(cs.auth_token, self.TEST_RESPONSE_HEADERS["X-Subject-Token"]) @httpretty.activate def test_authenticate_success_domain_username_password_scoped(self): self.stub_auth(json=self.TEST_RESPONSE_DICT) cs = client.Client(user_domain_name=self.TEST_DOMAIN_NAME, username=self.TEST_USER, password=self.TEST_TOKEN, project_id=self.TEST_TENANT_ID, auth_url=self.TEST_URL) self.assertEqual(cs.management_url, self.TEST_RESPONSE_DICT["token"]["catalog"][3] ['endpoints'][2]["url"]) self.assertEqual(cs.auth_token, self.TEST_RESPONSE_HEADERS["X-Subject-Token"]) @httpretty.activate def test_authenticate_success_userid_password_domain_scoped(self): ident = self.TEST_REQUEST_BODY['auth']['identity'] del ident['password']['user']['domain'] del ident['password']['user']['name'] ident['password']['user']['id'] = self.TEST_USER scope = self.TEST_REQUEST_BODY['auth']['scope'] del scope['project'] scope['domain'] = {} scope['domain']['id'] = self.TEST_DOMAIN_ID token = self.TEST_RESPONSE_DICT['token'] del token['project'] token['domain'] = {} token['domain']['id'] = self.TEST_DOMAIN_ID token['domain']['name'] = self.TEST_DOMAIN_NAME self.stub_auth(json=self.TEST_RESPONSE_DICT) cs = client.Client(user_id=self.TEST_USER, password=self.TEST_TOKEN, domain_id=self.TEST_DOMAIN_ID, auth_url=self.TEST_URL) self.assertEqual(cs.auth_domain_id, self.TEST_DOMAIN_ID) self.assertEqual(cs.management_url, self.TEST_RESPONSE_DICT["token"]["catalog"][3] ['endpoints'][2]["url"]) self.assertEqual(cs.auth_token, self.TEST_RESPONSE_HEADERS["X-Subject-Token"]) self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY) @httpretty.activate def test_authenticate_success_userid_password_project_scoped(self): ident = self.TEST_REQUEST_BODY['auth']['identity'] del ident['password']['user']['domain'] del ident['password']['user']['name'] ident['password']['user']['id'] = self.TEST_USER self.stub_auth(json=self.TEST_RESPONSE_DICT) cs = client.Client(user_id=self.TEST_USER, password=self.TEST_TOKEN, project_id=self.TEST_TENANT_ID, auth_url=self.TEST_URL) self.assertEqual(cs.auth_tenant_id, self.TEST_TENANT_ID) self.assertEqual(cs.management_url, self.TEST_RESPONSE_DICT["token"]["catalog"][3] ['endpoints'][2]["url"]) self.assertEqual(cs.auth_token, self.TEST_RESPONSE_HEADERS["X-Subject-Token"]) self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY) @httpretty.activate def test_authenticate_success_password_unscoped(self): del self.TEST_RESPONSE_DICT['token']['catalog'] del self.TEST_REQUEST_BODY['auth']['scope'] self.stub_auth(json=self.TEST_RESPONSE_DICT) cs = client.Client(user_domain_name=self.TEST_DOMAIN_NAME, username=self.TEST_USER, password=self.TEST_TOKEN, auth_url=self.TEST_URL) self.assertEqual(cs.auth_token, self.TEST_RESPONSE_HEADERS["X-Subject-Token"]) self.assertFalse('catalog' in cs.service_catalog.catalog) self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY) @httpretty.activate def test_auth_url_token_authentication(self): fake_token = 'fake_token' fake_url = '/fake-url' fake_resp = {'result': True} self.stub_auth(json=self.TEST_RESPONSE_DICT) self.stub_url('GET', [fake_url], json=fake_resp, base_url=self.TEST_ADMIN_IDENTITY_ENDPOINT) cl = client.Client(auth_url=self.TEST_URL, token=fake_token) body = httpretty.last_request().body if six.PY3: body = body.decode('utf-8') body = jsonutils.loads(body) self.assertEqual(body['auth']['identity']['token']['id'], fake_token) resp, body = cl.get(fake_url) self.assertEqual(fake_resp, body) self.assertEqual(httpretty.last_request().headers.get('X-Auth-Token'), self.TEST_TOKEN) @httpretty.activate def test_authenticate_success_token_domain_scoped(self): ident = self.TEST_REQUEST_BODY['auth']['identity'] del ident['password'] ident['methods'] = ['token'] ident['token'] = {} ident['token']['id'] = self.TEST_TOKEN scope = self.TEST_REQUEST_BODY['auth']['scope'] del scope['project'] scope['domain'] = {} scope['domain']['id'] = self.TEST_DOMAIN_ID token = self.TEST_RESPONSE_DICT['token'] del token['project'] token['domain'] = {} token['domain']['id'] = self.TEST_DOMAIN_ID token['domain']['name'] = self.TEST_DOMAIN_NAME self.TEST_REQUEST_HEADERS['X-Auth-Token'] = self.TEST_TOKEN self.stub_auth(json=self.TEST_RESPONSE_DICT) cs = client.Client(token=self.TEST_TOKEN, domain_id=self.TEST_DOMAIN_ID, auth_url=self.TEST_URL) self.assertEqual(cs.auth_domain_id, self.TEST_DOMAIN_ID) self.assertEqual(cs.management_url, self.TEST_RESPONSE_DICT["token"]["catalog"][3] ['endpoints'][2]["url"]) self.assertEqual(cs.auth_token, self.TEST_RESPONSE_HEADERS["X-Subject-Token"]) self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY) @httpretty.activate def test_authenticate_success_token_project_scoped(self): ident = self.TEST_REQUEST_BODY['auth']['identity'] del ident['password'] ident['methods'] = ['token'] ident['token'] = {} ident['token']['id'] = self.TEST_TOKEN self.TEST_REQUEST_HEADERS['X-Auth-Token'] = self.TEST_TOKEN self.stub_auth(json=self.TEST_RESPONSE_DICT) cs = client.Client(token=self.TEST_TOKEN, project_id=self.TEST_TENANT_ID, auth_url=self.TEST_URL) self.assertEqual(cs.auth_tenant_id, self.TEST_TENANT_ID) self.assertEqual(cs.management_url, self.TEST_RESPONSE_DICT["token"]["catalog"][3] ['endpoints'][2]["url"]) self.assertEqual(cs.auth_token, self.TEST_RESPONSE_HEADERS["X-Subject-Token"]) self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY) @httpretty.activate def test_authenticate_success_token_unscoped(self): ident = self.TEST_REQUEST_BODY['auth']['identity'] del ident['password'] ident['methods'] = ['token'] ident['token'] = {} ident['token']['id'] = self.TEST_TOKEN del self.TEST_REQUEST_BODY['auth']['scope'] del self.TEST_RESPONSE_DICT['token']['catalog'] self.TEST_REQUEST_HEADERS['X-Auth-Token'] = self.TEST_TOKEN self.stub_auth(json=self.TEST_RESPONSE_DICT) cs = client.Client(token=self.TEST_TOKEN, auth_url=self.TEST_URL) self.assertEqual(cs.auth_token, self.TEST_RESPONSE_HEADERS["X-Subject-Token"]) self.assertFalse('catalog' in cs.service_catalog.catalog) self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY) @httpretty.activate def test_allow_override_of_auth_token(self): fake_url = '/fake-url' fake_token = 'fake_token' fake_resp = {'result': True} self.stub_auth(json=self.TEST_RESPONSE_DICT) self.stub_url('GET', [fake_url], json=fake_resp, base_url=self.TEST_ADMIN_IDENTITY_ENDPOINT) cl = client.Client(username='exampleuser', password='password', tenant_name='exampleproject', auth_url=self.TEST_URL) self.assertEqual(cl.auth_token, self.TEST_TOKEN) # the token returned from the authentication will be used resp, body = cl.get(fake_url) self.assertEqual(fake_resp, body) self.assertEqual(httpretty.last_request().headers.get('X-Auth-Token'), self.TEST_TOKEN) # then override that token and the new token shall be used cl.auth_token = fake_token resp, body = cl.get(fake_url) self.assertEqual(fake_resp, body) self.assertEqual(httpretty.last_request().headers.get('X-Auth-Token'), fake_token) # if we clear that overriden token then we fall back to the original del cl.auth_token resp, body = cl.get(fake_url) self.assertEqual(fake_resp, body) self.assertEqual(httpretty.last_request().headers.get('X-Auth-Token'), self.TEST_TOKEN) python-keystoneclient-0.7.1/keystoneclient/tests/v3/test_users.py0000664000175400017540000002207012315030451026533 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import uuid import httpretty from keystoneclient import exceptions from keystoneclient.tests.v3 import utils from keystoneclient.v3 import users class UserTests(utils.TestCase, utils.CrudTests): def setUp(self): super(UserTests, self).setUp() self.key = 'user' self.collection_key = 'users' self.model = users.User self.manager = self.client.users def new_ref(self, **kwargs): kwargs = super(UserTests, self).new_ref(**kwargs) kwargs.setdefault('description', uuid.uuid4().hex) kwargs.setdefault('domain_id', uuid.uuid4().hex) kwargs.setdefault('enabled', True) kwargs.setdefault('name', uuid.uuid4().hex) kwargs.setdefault('default_project_id', uuid.uuid4().hex) return kwargs @httpretty.activate def test_add_user_to_group(self): group_id = uuid.uuid4().hex ref = self.new_ref() self.stub_url(httpretty.PUT, ['groups', group_id, self.collection_key, ref['id']], status=204) self.manager.add_to_group(user=ref['id'], group=group_id) self.assertRaises(exceptions.ValidationError, self.manager.remove_from_group, user=ref['id'], group=None) @httpretty.activate def test_list_users_in_group(self): group_id = uuid.uuid4().hex ref_list = [self.new_ref(), self.new_ref()] self.stub_entity(httpretty.GET, ['groups', group_id, self.collection_key], entity=ref_list) returned_list = self.manager.list(group=group_id) self.assertEqual(len(ref_list), len(returned_list)) [self.assertIsInstance(r, self.model) for r in returned_list] @httpretty.activate def test_check_user_in_group(self): group_id = uuid.uuid4().hex ref = self.new_ref() self.stub_url(httpretty.HEAD, ['groups', group_id, self.collection_key, ref['id']], status=204) self.manager.check_in_group(user=ref['id'], group=group_id) self.assertRaises(exceptions.ValidationError, self.manager.check_in_group, user=ref['id'], group=None) @httpretty.activate def test_remove_user_from_group(self): group_id = uuid.uuid4().hex ref = self.new_ref() self.stub_url(httpretty.DELETE, ['groups', group_id, self.collection_key, ref['id']], status=204) self.manager.remove_from_group(user=ref['id'], group=group_id) self.assertRaises(exceptions.ValidationError, self.manager.remove_from_group, user=ref['id'], group=None) @httpretty.activate def test_create_with_project(self): # Can create a user with the deprecated project option rather than # default_project_id. ref = self.new_ref() self.stub_entity(httpretty.POST, [self.collection_key], status=201, entity=ref) req_ref = ref.copy() req_ref.pop('id') param_ref = req_ref.copy() # Use deprecated project_id rather than new default_project_id. param_ref['project_id'] = param_ref.pop('default_project_id') params = utils.parameterize(param_ref) returned = self.manager.create(**params) self.assertIsInstance(returned, self.model) for attr in ref: self.assertEqual( getattr(returned, attr), ref[attr], 'Expected different %s' % attr) self.assertEntityRequestBodyIs(req_ref) @httpretty.activate def test_create_with_project_and_default_project(self): # Can create a user with the deprecated project and default_project_id. # The backend call should only pass the default_project_id. ref = self.new_ref() self.stub_entity(httpretty.POST, [self.collection_key], status=201, entity=ref) req_ref = ref.copy() req_ref.pop('id') param_ref = req_ref.copy() # Add the deprecated project_id in the call, the value will be ignored. param_ref['project_id'] = 'project' params = utils.parameterize(param_ref) returned = self.manager.create(**params) self.assertIsInstance(returned, self.model) for attr in ref: self.assertEqual( getattr(returned, attr), ref[attr], 'Expected different %s' % attr) self.assertEntityRequestBodyIs(req_ref) @httpretty.activate def test_update_with_project(self): # Can update a user with the deprecated project option rather than # default_project_id. ref = self.new_ref() req_ref = ref.copy() req_ref.pop('id') param_ref = req_ref.copy() self.stub_entity(httpretty.PATCH, [self.collection_key, ref['id']], status=200, entity=ref) # Use deprecated project_id rather than new default_project_id. param_ref['project_id'] = param_ref.pop('default_project_id') params = utils.parameterize(param_ref) returned = self.manager.update(ref['id'], **params) self.assertIsInstance(returned, self.model) for attr in ref: self.assertEqual( getattr(returned, attr), ref[attr], 'Expected different %s' % attr) self.assertEntityRequestBodyIs(req_ref) @httpretty.activate def test_update_with_project_and_default_project(self, ref=None): ref = self.new_ref() req_ref = ref.copy() req_ref.pop('id') param_ref = req_ref.copy() self.stub_entity(httpretty.PATCH, [self.collection_key, ref['id']], status=200, entity=ref) # Add the deprecated project_id in the call, the value will be ignored. param_ref['project_id'] = 'project' params = utils.parameterize(param_ref) returned = self.manager.update(ref['id'], **params) self.assertIsInstance(returned, self.model) for attr in ref: self.assertEqual( getattr(returned, attr), ref[attr], 'Expected different %s' % attr) self.assertEntityRequestBodyIs(req_ref) @httpretty.activate def test_update_password(self): old_password = uuid.uuid4().hex new_password = uuid.uuid4().hex self.stub_url(httpretty.POST, [self.collection_key, self.TEST_USER, 'password']) self.client.user_id = self.TEST_USER self.manager.update_password(old_password, new_password) exp_req_body = { 'user': { 'password': new_password, 'original_password': old_password } } self.assertEqual('/v3/users/test/password', httpretty.last_request().path) self.assertRequestBodyIs(json=exp_req_body) def test_update_password_with_bad_inputs(self): old_password = uuid.uuid4().hex new_password = uuid.uuid4().hex # users can't unset their password self.assertRaises(exceptions.ValidationError, self.manager.update_password, old_password, None) self.assertRaises(exceptions.ValidationError, self.manager.update_password, old_password, '') # users can't start with empty passwords self.assertRaises(exceptions.ValidationError, self.manager.update_password, None, new_password) self.assertRaises(exceptions.ValidationError, self.manager.update_password, '', new_password) # this wouldn't result in any change anyway self.assertRaises(exceptions.ValidationError, self.manager.update_password, None, None) self.assertRaises(exceptions.ValidationError, self.manager.update_password, '', '') password = uuid.uuid4().hex self.assertRaises(exceptions.ValidationError, self.manager.update_password, password, password) python-keystoneclient-0.7.1/keystoneclient/tests/v3/test_credentials.py0000664000175400017540000000375412315030451027677 0ustar jenkinsjenkins00000000000000# 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 uuid from keystoneclient.tests.v3 import utils from keystoneclient.v3 import credentials class CredentialTests(utils.TestCase, utils.CrudTests): def setUp(self): super(CredentialTests, self).setUp() self.key = 'credential' self.collection_key = 'credentials' self.model = credentials.Credential self.manager = self.client.credentials def new_ref(self, **kwargs): kwargs = super(CredentialTests, self).new_ref(**kwargs) kwargs.setdefault('blob', uuid.uuid4().hex) kwargs.setdefault('project_id', uuid.uuid4().hex) kwargs.setdefault('type', uuid.uuid4().hex) kwargs.setdefault('user_id', uuid.uuid4().hex) return kwargs @staticmethod def _ref_data_not_blob(ref): ret_ref = ref.copy() ret_ref['data'] = ref['blob'] del ret_ref['blob'] return ret_ref def test_create_data_not_blob(self): # Test create operation with previous, deprecated "data" argument, # which should be translated into "blob" at the API call level req_ref = self.new_ref() api_ref = self._ref_data_not_blob(req_ref) self.test_create(api_ref, req_ref) def test_update_data_not_blob(self): # Likewise test update operation with data instead of blob argument req_ref = self.new_ref() api_ref = self._ref_data_not_blob(req_ref) self.test_update(api_ref, req_ref) python-keystoneclient-0.7.1/keystoneclient/tests/v3/test_discover.py0000664000175400017540000000732612315030451027217 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import json import httpretty from keystoneclient.generic import client from keystoneclient.tests.v3 import utils class DiscoverKeystoneTests(utils.UnauthenticatedTestCase): def setUp(self): super(DiscoverKeystoneTests, self).setUp() self.TEST_RESPONSE_DICT = { "versions": { "values": [{"id": "v3.0", "status": "beta", "updated": "2013-03-06T00:00:00Z", "links": [ {"rel": "self", "href": "http://127.0.0.1:5000/v3.0/", }, {"rel": "describedby", "type": "text/html", "href": "http://docs.openstack.org/api/" "openstack-identity-service/3/" "content/", }, {"rel": "describedby", "type": "application/pdf", "href": "http://docs.openstack.org/api/" "openstack-identity-service/3/" "identity-dev-guide-3.pdf", }, ]}, {"id": "v2.0", "status": "beta", "updated": "2013-03-06T00:00:00Z", "links": [ {"rel": "self", "href": "http://127.0.0.1:5000/v2.0/", }, {"rel": "describedby", "type": "text/html", "href": "http://docs.openstack.org/api/" "openstack-identity-service/2.0/" "content/", }, {"rel": "describedby", "type": "application/pdf", "href": "http://docs.openstack.org/api/" "openstack-identity-service/2.0/" "identity-dev-guide-2.0.pdf", } ]}], }, } self.TEST_REQUEST_HEADERS = { 'User-Agent': 'python-keystoneclient', 'Accept': 'application/json', } @httpretty.activate def test_get_version_local(self): httpretty.register_uri(httpretty.GET, "http://localhost:35357/", status=300, body=json.dumps(self.TEST_RESPONSE_DICT)) cs = client.Client() versions = cs.discover() self.assertIsInstance(versions, dict) self.assertIn('message', versions) self.assertIn('v3.0', versions) self.assertEqual( versions['v3.0']['url'], self.TEST_RESPONSE_DICT['versions']['values'][0]['links'][0] ['href']) self.assertEqual( versions['v2.0']['url'], self.TEST_RESPONSE_DICT['versions']['values'][1]['links'][0] ['href']) python-keystoneclient-0.7.1/keystoneclient/tests/v3/test_service_catalog.py0000664000175400017540000001717612315030451030537 0ustar jenkinsjenkins00000000000000# 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 copy from keystoneclient import access from keystoneclient import exceptions from keystoneclient.tests.v3 import client_fixtures from keystoneclient.tests.v3 import utils class ServiceCatalogTest(utils.TestCase): def setUp(self): super(ServiceCatalogTest, self).setUp() self.AUTH_RESPONSE_BODY = copy.deepcopy( client_fixtures.AUTH_RESPONSE_BODY) self.RESPONSE = utils.TestResponse({ "headers": client_fixtures.AUTH_RESPONSE_HEADERS }) self.north_endpoints = {'public': 'http://glance.north.host/glanceapi/public', 'internal': 'http://glance.north.host/glanceapi/internal', 'admin': 'http://glance.north.host/glanceapi/admin'} self.south_endpoints = {'public': 'http://glance.south.host/glanceapi/public', 'internal': 'http://glance.south.host/glanceapi/internal', 'admin': 'http://glance.south.host/glanceapi/admin'} def test_building_a_service_catalog(self): auth_ref = access.AccessInfo.factory(self.RESPONSE, self.AUTH_RESPONSE_BODY) sc = auth_ref.service_catalog self.assertEqual(sc.url_for(service_type='compute'), "https://compute.north.host/novapi/public") self.assertEqual(sc.url_for(service_type='compute', endpoint_type='internal'), "https://compute.north.host/novapi/internal") self.assertRaises(exceptions.EndpointNotFound, sc.url_for, "region", "South", service_type='compute') def test_service_catalog_endpoints(self): auth_ref = access.AccessInfo.factory(self.RESPONSE, self.AUTH_RESPONSE_BODY) sc = auth_ref.service_catalog public_ep = sc.get_endpoints(service_type='compute', endpoint_type='public') self.assertEqual(public_ep['compute'][0]['region'], 'North') self.assertEqual(public_ep['compute'][0]['url'], "https://compute.north.host/novapi/public") def test_service_catalog_regions(self): self.AUTH_RESPONSE_BODY['token']['region_name'] = "North" auth_ref = access.AccessInfo.factory(self.RESPONSE, self.AUTH_RESPONSE_BODY) sc = auth_ref.service_catalog url = sc.url_for(service_type='image', endpoint_type='public') self.assertEqual(url, "http://glance.north.host/glanceapi/public") self.AUTH_RESPONSE_BODY['token']['region_name'] = "South" auth_ref = access.AccessInfo.factory(self.RESPONSE, self.AUTH_RESPONSE_BODY) sc = auth_ref.service_catalog url = sc.url_for(service_type='image', endpoint_type='internal') self.assertEqual(url, "http://glance.south.host/glanceapi/internal") def test_service_catalog_empty(self): self.AUTH_RESPONSE_BODY['token']['catalog'] = [] auth_ref = access.AccessInfo.factory(self.RESPONSE, self.AUTH_RESPONSE_BODY) self.assertRaises(exceptions.EmptyCatalog, auth_ref.service_catalog.url_for, service_type='image', endpoint_type='internalURL') def test_service_catalog_get_endpoints_region_names(self): auth_ref = access.AccessInfo.factory(None, self.AUTH_RESPONSE_BODY) sc = auth_ref.service_catalog endpoints = sc.get_endpoints(service_type='image', region_name='North') self.assertEqual(len(endpoints), 1) for endpoint in endpoints['image']: self.assertEqual(endpoint['url'], self.north_endpoints[endpoint['interface']]) endpoints = sc.get_endpoints(service_type='image', region_name='South') self.assertEqual(len(endpoints), 1) for endpoint in endpoints['image']: self.assertEqual(endpoint['url'], self.south_endpoints[endpoint['interface']]) endpoints = sc.get_endpoints(service_type='compute') self.assertEqual(len(endpoints['compute']), 3) endpoints = sc.get_endpoints(service_type='compute', region_name='North') self.assertEqual(len(endpoints['compute']), 3) endpoints = sc.get_endpoints(service_type='compute', region_name='West') self.assertEqual(len(endpoints['compute']), 0) def test_service_catalog_url_for_region_names(self): auth_ref = access.AccessInfo.factory(None, self.AUTH_RESPONSE_BODY) sc = auth_ref.service_catalog url = sc.url_for(service_type='image', region_name='North') self.assertEqual(url, self.north_endpoints['public']) url = sc.url_for(service_type='image', region_name='South') self.assertEqual(url, self.south_endpoints['public']) self.assertRaises(exceptions.EndpointNotFound, sc.url_for, service_type='image', region_name='West') def test_servcie_catalog_get_url_region_names(self): auth_ref = access.AccessInfo.factory(None, self.AUTH_RESPONSE_BODY) sc = auth_ref.service_catalog urls = sc.get_urls(service_type='image') self.assertEqual(len(urls), 2) urls = sc.get_urls(service_type='image', region_name='North') self.assertEqual(len(urls), 1) self.assertEqual(urls[0], self.north_endpoints['public']) urls = sc.get_urls(service_type='image', region_name='South') self.assertEqual(len(urls), 1) self.assertEqual(urls[0], self.south_endpoints['public']) urls = sc.get_urls(service_type='image', region_name='West') self.assertIsNone(urls) def test_service_catalog_param_overrides_body_region(self): self.AUTH_RESPONSE_BODY['token']['region_name'] = "North" auth_ref = access.AccessInfo.factory(None, self.AUTH_RESPONSE_BODY) sc = auth_ref.service_catalog url = sc.url_for(service_type='image') self.assertEqual(url, self.north_endpoints['public']) url = sc.url_for(service_type='image', region_name='South') self.assertEqual(url, self.south_endpoints['public']) endpoints = sc.get_endpoints(service_type='image') self.assertEqual(len(endpoints['image']), 3) for endpoint in endpoints['image']: self.assertEqual(endpoint['url'], self.north_endpoints[endpoint['interface']]) endpoints = sc.get_endpoints(service_type='image', region_name='South') self.assertEqual(len(endpoints['image']), 3) for endpoint in endpoints['image']: self.assertEqual(endpoint['url'], self.south_endpoints[endpoint['interface']]) python-keystoneclient-0.7.1/keystoneclient/tests/v3/test_access.py0000664000175400017540000001361212315030451026635 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import datetime from keystoneclient import access from keystoneclient.openstack.common import timeutils from keystoneclient.tests.v3 import client_fixtures from keystoneclient.tests.v3 import utils TOKEN_RESPONSE = utils.TestResponse({ "headers": client_fixtures.AUTH_RESPONSE_HEADERS }) UNSCOPED_TOKEN = client_fixtures.UNSCOPED_TOKEN DOMAIN_SCOPED_TOKEN = client_fixtures.DOMAIN_SCOPED_TOKEN PROJECT_SCOPED_TOKEN = client_fixtures.PROJECT_SCOPED_TOKEN class AccessInfoTest(utils.TestCase): def test_building_unscoped_accessinfo(self): auth_ref = access.AccessInfo.factory(resp=TOKEN_RESPONSE, body=UNSCOPED_TOKEN) self.assertTrue(auth_ref) self.assertIn('methods', auth_ref) self.assertIn('catalog', auth_ref) self.assertFalse(auth_ref['catalog']) self.assertEqual(auth_ref.auth_token, '3e2813b7ba0b4006840c3825860b86ed') self.assertEqual(auth_ref.username, 'exampleuser') self.assertEqual(auth_ref.user_id, 'c4da488862bd435c9e6c0275a0d0e49a') self.assertEqual(auth_ref.role_names, []) self.assertIsNone(auth_ref.project_name) self.assertIsNone(auth_ref.project_id) self.assertIsNone(auth_ref.auth_url) self.assertIsNone(auth_ref.management_url) self.assertFalse(auth_ref.domain_scoped) self.assertFalse(auth_ref.project_scoped) self.assertEqual(auth_ref.user_domain_id, '4e6893b7ba0b4006840c3845660b86ed') self.assertEqual(auth_ref.user_domain_name, 'exampledomain') self.assertIsNone(auth_ref.project_domain_id) self.assertIsNone(auth_ref.project_domain_name) self.assertEqual(auth_ref.expires, timeutils.parse_isotime( UNSCOPED_TOKEN['token']['expires_at'])) def test_will_expire_soon(self): expires = timeutils.utcnow() + datetime.timedelta(minutes=5) UNSCOPED_TOKEN['token']['expires_at'] = expires.isoformat() auth_ref = access.AccessInfo.factory(resp=TOKEN_RESPONSE, body=UNSCOPED_TOKEN) self.assertFalse(auth_ref.will_expire_soon(stale_duration=120)) self.assertTrue(auth_ref.will_expire_soon(stale_duration=300)) self.assertFalse(auth_ref.will_expire_soon()) def test_building_domain_scoped_accessinfo(self): auth_ref = access.AccessInfo.factory(resp=TOKEN_RESPONSE, body=DOMAIN_SCOPED_TOKEN) self.assertTrue(auth_ref) self.assertIn('methods', auth_ref) self.assertIn('catalog', auth_ref) self.assertTrue(auth_ref['catalog']) self.assertEqual(auth_ref.auth_token, '3e2813b7ba0b4006840c3825860b86ed') self.assertEqual(auth_ref.username, 'exampleuser') self.assertEqual(auth_ref.user_id, 'c4da488862bd435c9e6c0275a0d0e49a') self.assertEqual(auth_ref.role_names, ['admin', 'member']) self.assertEqual(auth_ref.domain_name, 'anotherdomain') self.assertEqual(auth_ref.domain_id, '8e9283b7ba0b1038840c3842058b86ab') self.assertIsNone(auth_ref.project_name) self.assertIsNone(auth_ref.project_id) self.assertEqual(auth_ref.user_domain_id, '4e6893b7ba0b4006840c3845660b86ed') self.assertEqual(auth_ref.user_domain_name, 'exampledomain') self.assertIsNone(auth_ref.project_domain_id) self.assertIsNone(auth_ref.project_domain_name) self.assertTrue(auth_ref.domain_scoped) self.assertFalse(auth_ref.project_scoped) def test_building_project_scoped_accessinfo(self): auth_ref = access.AccessInfo.factory(resp=TOKEN_RESPONSE, body=PROJECT_SCOPED_TOKEN) self.assertTrue(auth_ref) self.assertIn('methods', auth_ref) self.assertIn('catalog', auth_ref) self.assertTrue(auth_ref['catalog']) self.assertEqual(auth_ref.auth_token, '3e2813b7ba0b4006840c3825860b86ed') self.assertEqual(auth_ref.username, 'exampleuser') self.assertEqual(auth_ref.user_id, 'c4da488862bd435c9e6c0275a0d0e49a') self.assertEqual(auth_ref.role_names, ['admin', 'member']) self.assertIsNone(auth_ref.domain_name) self.assertIsNone(auth_ref.domain_id) self.assertEqual(auth_ref.project_name, 'exampleproject') self.assertEqual(auth_ref.project_id, '225da22d3ce34b15877ea70b2a575f58') self.assertEqual(auth_ref.tenant_name, auth_ref.project_name) self.assertEqual(auth_ref.tenant_id, auth_ref.project_id) self.assertEqual(auth_ref.auth_url, ('http://public.com:5000/v3',)) self.assertEqual(auth_ref.management_url, ('http://admin:35357/v3',)) self.assertEqual(auth_ref.project_domain_id, '4e6893b7ba0b4006840c3845660b86ed') self.assertEqual(auth_ref.project_domain_name, 'exampledomain') self.assertEqual(auth_ref.user_domain_id, '4e6893b7ba0b4006840c3845660b86ed') self.assertEqual(auth_ref.user_domain_name, 'exampledomain') self.assertFalse(auth_ref.domain_scoped) self.assertTrue(auth_ref.project_scoped) python-keystoneclient-0.7.1/keystoneclient/tests/v3/test_trusts.py0000664000175400017540000000754312315030451026746 0ustar jenkinsjenkins00000000000000# 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 uuid from keystoneclient import exceptions from keystoneclient.openstack.common import timeutils from keystoneclient.tests.v3 import utils from keystoneclient.v3.contrib import trusts class TrustTests(utils.TestCase, utils.CrudTests): def setUp(self): super(TrustTests, self).setUp() self.key = 'trust' self.collection_key = 'trusts' self.model = trusts.Trust self.manager = self.client.trusts self.path_prefix = 'OS-TRUST' def new_ref(self, **kwargs): kwargs = super(TrustTests, self).new_ref(**kwargs) kwargs.setdefault('project_id', uuid.uuid4().hex) return kwargs def test_create(self): ref = self.new_ref() ref['trustor_user_id'] = uuid.uuid4().hex ref['trustee_user_id'] = uuid.uuid4().hex ref['impersonation'] = False super(TrustTests, self).test_create(ref=ref) def test_create_roles(self): ref = self.new_ref() ref['trustor_user_id'] = uuid.uuid4().hex ref['trustee_user_id'] = uuid.uuid4().hex ref['impersonation'] = False req_ref = ref.copy() # Note the TrustManager takes a list of role_names, and converts # internally to the slightly odd list-of-dict API format, so we # have to pass the expected request data to allow correct stubbing ref['role_names'] = ['atestrole'] req_ref['roles'] = [{'name': 'atestrole'}] super(TrustTests, self).test_create(ref=ref, req_ref=req_ref) def test_create_expires(self): ref = self.new_ref() ref['trustor_user_id'] = uuid.uuid4().hex ref['trustee_user_id'] = uuid.uuid4().hex ref['impersonation'] = False ref['expires_at'] = timeutils.parse_isotime( '2013-03-04T12:00:01.000000Z') req_ref = ref.copy() # Note the TrustManager takes a datetime.datetime object for # expires_at, and converts it internally into an iso format datestamp req_ref['expires_at'] = '2013-03-04T12:00:01.000000Z' super(TrustTests, self).test_create(ref=ref, req_ref=req_ref) def test_create_imp(self): ref = self.new_ref() ref['trustor_user_id'] = uuid.uuid4().hex ref['trustee_user_id'] = uuid.uuid4().hex ref['impersonation'] = True super(TrustTests, self).test_create(ref=ref) def test_create_roles_imp(self): ref = self.new_ref() ref['trustor_user_id'] = uuid.uuid4().hex ref['trustee_user_id'] = uuid.uuid4().hex ref['impersonation'] = True req_ref = ref.copy() ref['role_names'] = ['atestrole'] req_ref['roles'] = [{'name': 'atestrole'}] super(TrustTests, self).test_create(ref=ref, req_ref=req_ref) def test_list_filter_trustor(self): expected_query = {'trustor_user_id': '12345'} super(TrustTests, self).test_list(expected_query=expected_query, trustor_user='12345') def test_list_filter_trustee(self): expected_query = {'trustee_user_id': '12345'} super(TrustTests, self).test_list(expected_query=expected_query, trustee_user='12345') def test_update(self): # Update not supported for the OS-TRUST API self.assertRaises(exceptions.HTTPNotImplemented, self.manager.update) python-keystoneclient-0.7.1/keystoneclient/tests/v3/__init__.py0000664000175400017540000000000012315030451026057 0ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/tests/client_fixtures.py0000664000175400017540000004411112315030450027211 0ustar jenkinsjenkins00000000000000# Copyright 2013 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import fixtures import six import testresources from keystoneclient.common import cms from keystoneclient.openstack.common import jsonutils from keystoneclient.openstack.common import timeutils from keystoneclient import utils TESTDIR = os.path.dirname(os.path.abspath(__file__)) ROOTDIR = os.path.normpath(os.path.join(TESTDIR, '..', '..')) CERTDIR = os.path.join(ROOTDIR, 'examples', 'pki', 'certs') CMSDIR = os.path.join(ROOTDIR, 'examples', 'pki', 'cms') KEYDIR = os.path.join(ROOTDIR, 'examples', 'pki', 'private') class Examples(fixtures.Fixture): """Example tokens and certs loaded from the examples directory. To use this class correctly, the module needs to override the test suite class to use testresources.OptimisingTestSuite (otherwise the files will be read on every test). This is done by defining a load_tests function in the module, like this: def load_tests(loader, tests, pattern): return testresources.OptimisingTestSuite(tests) (see http://docs.python.org/2/library/unittest.html#load-tests-protocol ) """ def setUp(self): super(Examples, self).setUp() # The data for several tests are signed using openssl and are stored in # files in the signing subdirectory. In order to keep the values # consistent between the tests and the signed documents, we read them # in for use in the tests. with open(os.path.join(CMSDIR, 'auth_token_scoped.pem')) as f: self.SIGNED_TOKEN_SCOPED = cms.cms_to_token(f.read()) with open(os.path.join(CMSDIR, 'auth_token_unscoped.pem')) as f: self.SIGNED_TOKEN_UNSCOPED = cms.cms_to_token(f.read()) with open(os.path.join(CMSDIR, 'auth_v3_token_scoped.pem')) as f: self.SIGNED_v3_TOKEN_SCOPED = cms.cms_to_token(f.read()) with open(os.path.join(CMSDIR, 'auth_token_revoked.pem')) as f: self.REVOKED_TOKEN = cms.cms_to_token(f.read()) with open(os.path.join(CMSDIR, 'auth_token_scoped_expired.pem')) as f: self.SIGNED_TOKEN_SCOPED_EXPIRED = cms.cms_to_token(f.read()) with open(os.path.join(CMSDIR, 'auth_v3_token_revoked.pem')) as f: self.REVOKED_v3_TOKEN = cms.cms_to_token(f.read()) with open(os.path.join(CMSDIR, 'revocation_list.json')) as f: self.REVOCATION_LIST = jsonutils.loads(f.read()) with open(os.path.join(CMSDIR, 'revocation_list.pem')) as f: self.SIGNED_REVOCATION_LIST = jsonutils.dumps({'signed': f.read()}) self.SIGNING_CERT_FILE = os.path.join(CERTDIR, 'signing_cert.pem') with open(self.SIGNING_CERT_FILE) as f: self.SIGNING_CERT = f.read() self.KERBEROS_BIND = 'USER@REALM' self.SIGNING_KEY_FILE = os.path.join(KEYDIR, 'signing_key.pem') with open(self.SIGNING_KEY_FILE) as f: self.SIGNING_KEY = f.read() self.SIGNING_CA_FILE = os.path.join(CERTDIR, 'cacert.pem') with open(self.SIGNING_CA_FILE) as f: self.SIGNING_CA = f.read() self.UUID_TOKEN_DEFAULT = "ec6c0710ec2f471498484c1b53ab4f9d" self.UUID_TOKEN_NO_SERVICE_CATALOG = '8286720fbe4941e69fa8241723bb02df' self.UUID_TOKEN_UNSCOPED = '731f903721c14827be7b2dc912af7776' self.UUID_TOKEN_BIND = '3fc54048ad64405c98225ce0897af7c5' self.UUID_TOKEN_UNKNOWN_BIND = '8885fdf4d42e4fb9879e6379fa1eaf48' self.VALID_DIABLO_TOKEN = 'b0cf19b55dbb4f20a6ee18e6c6cf1726' self.v3_UUID_TOKEN_DEFAULT = '5603457654b346fdbb93437bfe76f2f1' self.v3_UUID_TOKEN_UNSCOPED = 'd34835fdaec447e695a0a024d84f8d79' self.v3_UUID_TOKEN_DOMAIN_SCOPED = 'e8a7b63aaa4449f38f0c5c05c3581792' self.v3_UUID_TOKEN_BIND = '2f61f73e1c854cbb9534c487f9bd63c2' self.v3_UUID_TOKEN_UNKNOWN_BIND = '7ed9781b62cd4880b8d8c6788ab1d1e2' revoked_token = self.REVOKED_TOKEN if isinstance(revoked_token, six.text_type): revoked_token = revoked_token.encode('utf-8') self.REVOKED_TOKEN_HASH = utils.hash_signed_token(revoked_token) self.REVOKED_TOKEN_LIST = ( {'revoked': [{'id': self.REVOKED_TOKEN_HASH, 'expires': timeutils.utcnow()}]}) self.REVOKED_TOKEN_LIST_JSON = jsonutils.dumps(self.REVOKED_TOKEN_LIST) revoked_v3_token = self.REVOKED_v3_TOKEN if isinstance(revoked_v3_token, six.text_type): revoked_v3_token = revoked_v3_token.encode('utf-8') self.REVOKED_v3_TOKEN_HASH = utils.hash_signed_token(revoked_v3_token) self.REVOKED_v3_TOKEN_LIST = ( {'revoked': [{'id': self.REVOKED_v3_TOKEN_HASH, 'expires': timeutils.utcnow()}]}) self.REVOKED_v3_TOKEN_LIST_JSON = jsonutils.dumps( self.REVOKED_v3_TOKEN_LIST) self.SIGNED_TOKEN_SCOPED_KEY = cms.cms_hash_token( self.SIGNED_TOKEN_SCOPED) self.SIGNED_TOKEN_UNSCOPED_KEY = cms.cms_hash_token( self.SIGNED_TOKEN_UNSCOPED) self.SIGNED_v3_TOKEN_SCOPED_KEY = cms.cms_hash_token( self.SIGNED_v3_TOKEN_SCOPED) self.INVALID_SIGNED_TOKEN = ( "MIIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC" "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD" "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "0000000000000000000000000000000000000000000000000000000000000000" "1111111111111111111111111111111111111111111111111111111111111111" "2222222222222222222222222222222222222222222222222222222222222222" "3333333333333333333333333333333333333333333333333333333333333333" "4444444444444444444444444444444444444444444444444444444444444444" "5555555555555555555555555555555555555555555555555555555555555555" "6666666666666666666666666666666666666666666666666666666666666666" "7777777777777777777777777777777777777777777777777777777777777777" "8888888888888888888888888888888888888888888888888888888888888888" "9999999999999999999999999999999999999999999999999999999999999999" "0000000000000000000000000000000000000000000000000000000000000000") # JSON responses keyed by token ID self.TOKEN_RESPONSES = { self.UUID_TOKEN_DEFAULT: { 'access': { 'token': { 'id': self.UUID_TOKEN_DEFAULT, 'expires': '2020-01-01T00:00:10.000123Z', 'tenant': { 'id': 'tenant_id1', 'name': 'tenant_name1', }, }, 'user': { 'id': 'user_id1', 'name': 'user_name1', 'roles': [ {'name': 'role1'}, {'name': 'role2'}, ], }, 'serviceCatalog': {} }, }, self.VALID_DIABLO_TOKEN: { 'access': { 'token': { 'id': self.VALID_DIABLO_TOKEN, 'expires': '2020-01-01T00:00:10.000123Z', 'tenantId': 'tenant_id1', }, 'user': { 'id': 'user_id1', 'name': 'user_name1', 'roles': [ {'name': 'role1'}, {'name': 'role2'}, ], }, }, }, self.UUID_TOKEN_UNSCOPED: { 'access': { 'token': { 'id': self.UUID_TOKEN_UNSCOPED, 'expires': '2020-01-01T00:00:10.000123Z', }, 'user': { 'id': 'user_id1', 'name': 'user_name1', 'roles': [ {'name': 'role1'}, {'name': 'role2'}, ], }, }, }, self.UUID_TOKEN_NO_SERVICE_CATALOG: { 'access': { 'token': { 'id': 'valid-token', 'expires': '2020-01-01T00:00:10.000123Z', 'tenant': { 'id': 'tenant_id1', 'name': 'tenant_name1', }, }, 'user': { 'id': 'user_id1', 'name': 'user_name1', 'roles': [ {'name': 'role1'}, {'name': 'role2'}, ], } }, }, self.UUID_TOKEN_BIND: { 'access': { 'token': { 'bind': {'kerberos': self.KERBEROS_BIND}, 'id': self.UUID_TOKEN_BIND, 'expires': '2020-01-01T00:00:10.000123Z', 'tenant': { 'id': 'tenant_id1', 'name': 'tenant_name1', }, }, 'user': { 'id': 'user_id1', 'name': 'user_name1', 'roles': [ {'name': 'role1'}, {'name': 'role2'}, ], }, 'serviceCatalog': {} }, }, self.UUID_TOKEN_UNKNOWN_BIND: { 'access': { 'token': { 'bind': {'FOO': 'BAR'}, 'id': self.UUID_TOKEN_UNKNOWN_BIND, 'expires': '2020-01-01T00:00:10.000123Z', 'tenant': { 'id': 'tenant_id1', 'name': 'tenant_name1', }, }, 'user': { 'id': 'user_id1', 'name': 'user_name1', 'roles': [ {'name': 'role1'}, {'name': 'role2'}, ], }, 'serviceCatalog': {} }, }, self.v3_UUID_TOKEN_DEFAULT: { 'token': { 'expires_at': '2020-01-01T00:00:10.000123Z', 'methods': ['password'], 'user': { 'id': 'user_id1', 'name': 'user_name1', 'domain': { 'id': 'domain_id1', 'name': 'domain_name1' } }, 'project': { 'id': 'tenant_id1', 'name': 'tenant_name1', 'domain': { 'id': 'domain_id1', 'name': 'domain_name1' } }, 'roles': [ {'name': 'role1', 'id': 'Role1'}, {'name': 'role2', 'id': 'Role2'}, ], 'catalog': {} } }, self.v3_UUID_TOKEN_UNSCOPED: { 'token': { 'expires_at': '2020-01-01T00:00:10.000123Z', 'methods': ['password'], 'user': { 'id': 'user_id1', 'name': 'user_name1', 'domain': { 'id': 'domain_id1', 'name': 'domain_name1' } } } }, self.v3_UUID_TOKEN_DOMAIN_SCOPED: { 'token': { 'expires_at': '2020-01-01T00:00:10.000123Z', 'methods': ['password'], 'user': { 'id': 'user_id1', 'name': 'user_name1', 'domain': { 'id': 'domain_id1', 'name': 'domain_name1' } }, 'domain': { 'id': 'domain_id1', 'name': 'domain_name1', }, 'roles': [ {'name': 'role1', 'id': 'Role1'}, {'name': 'role2', 'id': 'Role2'}, ], 'catalog': {} } }, self.SIGNED_TOKEN_SCOPED_KEY: { 'access': { 'token': { 'id': self.SIGNED_TOKEN_SCOPED_KEY, }, 'user': { 'id': 'user_id1', 'name': 'user_name1', 'tenantId': 'tenant_id1', 'tenantName': 'tenant_name1', 'roles': [ {'name': 'role1'}, {'name': 'role2'}, ], }, }, }, self.SIGNED_TOKEN_UNSCOPED_KEY: { 'access': { 'token': { 'id': self.SIGNED_TOKEN_UNSCOPED_KEY, }, 'user': { 'id': 'user_id1', 'name': 'user_name1', 'roles': [ {'name': 'role1'}, {'name': 'role2'}, ], }, }, }, self.SIGNED_v3_TOKEN_SCOPED_KEY: { 'token': { 'expires': '2020-01-01T00:00:10.000123Z', 'methods': ['password'], 'user': { 'id': 'user_id1', 'name': 'user_name1', 'domain': { 'id': 'domain_id1', 'name': 'domain_name1' } }, 'project': { 'id': 'tenant_id1', 'name': 'tenant_name1', 'domain': { 'id': 'domain_id1', 'name': 'domain_name1' } }, 'roles': [ {'name': 'role1'}, {'name': 'role2'} ], 'catalog': {} } }, self.v3_UUID_TOKEN_BIND: { 'token': { 'bind': {'kerberos': self.KERBEROS_BIND}, 'methods': ['password'], 'expires_at': '2020-01-01T00:00:10.000123Z', 'user': { 'id': 'user_id1', 'name': 'user_name1', 'domain': { 'id': 'domain_id1', 'name': 'domain_name1' } }, 'project': { 'id': 'tenant_id1', 'name': 'tenant_name1', 'domain': { 'id': 'domain_id1', 'name': 'domain_name1' } }, 'roles': [ {'name': 'role1', 'id': 'Role1'}, {'name': 'role2', 'id': 'Role2'}, ], 'catalog': {} } }, self.v3_UUID_TOKEN_UNKNOWN_BIND: { 'token': { 'bind': {'FOO': 'BAR'}, 'expires_at': '2020-01-01T00:00:10.000123Z', 'methods': ['password'], 'user': { 'id': 'user_id1', 'name': 'user_name1', 'domain': { 'id': 'domain_id1', 'name': 'domain_name1' } }, 'project': { 'id': 'tenant_id1', 'name': 'tenant_name1', 'domain': { 'id': 'domain_id1', 'name': 'domain_name1' } }, 'roles': [ {'name': 'role1', 'id': 'Role1'}, {'name': 'role2', 'id': 'Role2'}, ], 'catalog': {} } }, } self.JSON_TOKEN_RESPONSES = dict([(k, jsonutils.dumps(v)) for k, v in six.iteritems(self.TOKEN_RESPONSES)]) EXAMPLES_RESOURCE = testresources.FixtureResource(Examples()) python-keystoneclient-0.7.1/keystoneclient/tests/test_memcache_crypt.py0000664000175400017540000001010112315030450030014 0ustar jenkinsjenkins00000000000000# 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 six import testtools from keystoneclient.middleware import memcache_crypt class MemcacheCryptPositiveTests(testtools.TestCase): def _setup_keys(self, strategy): return memcache_crypt.derive_keys(b'token', b'secret', strategy) def test_constant_time_compare(self): # make sure it works as a compare, the "constant time" aspect # isn't appropriate to test in unittests ctc = memcache_crypt.constant_time_compare self.assertTrue(ctc('abcd', 'abcd')) self.assertTrue(ctc('', '')) self.assertFalse(ctc('abcd', 'efgh')) self.assertFalse(ctc('abc', 'abcd')) self.assertFalse(ctc('abc', 'abc\x00')) self.assertFalse(ctc('', 'abc')) # For Python 3, we want to test these functions with both str and bytes # as input. if six.PY3: self.assertTrue(ctc(b'abcd', b'abcd')) self.assertTrue(ctc(b'', b'')) self.assertFalse(ctc(b'abcd', b'efgh')) self.assertFalse(ctc(b'abc', b'abcd')) self.assertFalse(ctc(b'abc', b'abc\x00')) self.assertFalse(ctc(b'', b'abc')) def test_derive_keys(self): keys = self._setup_keys(b'strategy') self.assertEqual(len(keys['ENCRYPTION']), len(keys['CACHE_KEY'])) self.assertEqual(len(keys['CACHE_KEY']), len(keys['MAC'])) self.assertNotEqual(keys['ENCRYPTION'], keys['MAC']) self.assertIn('strategy', keys.keys()) def test_key_strategy_diff(self): k1 = self._setup_keys(b'MAC') k2 = self._setup_keys(b'ENCRYPT') self.assertNotEqual(k1, k2) def test_sign_data(self): keys = self._setup_keys(b'MAC') sig = memcache_crypt.sign_data(keys['MAC'], b'data') self.assertEqual(len(sig), memcache_crypt.DIGEST_LENGTH_B64) def test_encryption(self): keys = self._setup_keys(b'ENCRYPT') # what you put in is what you get out for data in [b'data', b'1234567890123456', b'\x00\xFF' * 13 ] + [six.int2byte(x % 256) * x for x in range(768)]: crypt = memcache_crypt.encrypt_data(keys['ENCRYPTION'], data) decrypt = memcache_crypt.decrypt_data(keys['ENCRYPTION'], crypt) self.assertEqual(data, decrypt) self.assertRaises(memcache_crypt.DecryptError, memcache_crypt.decrypt_data, keys['ENCRYPTION'], crypt[:-1]) def test_protect_wrappers(self): data = b'My Pretty Little Data' for strategy in [b'MAC', b'ENCRYPT']: keys = self._setup_keys(strategy) protected = memcache_crypt.protect_data(keys, data) self.assertNotEqual(protected, data) if strategy == b'ENCRYPT': self.assertNotIn(data, protected) unprotected = memcache_crypt.unprotect_data(keys, protected) self.assertEqual(data, unprotected) self.assertRaises(memcache_crypt.InvalidMacError, memcache_crypt.unprotect_data, keys, protected[:-1]) self.assertIsNone(memcache_crypt.unprotect_data(keys, None)) def test_no_pycrypt(self): aes = memcache_crypt.AES memcache_crypt.AES = None self.assertRaises(memcache_crypt.CryptoUnavailableError, memcache_crypt.encrypt_data, 'token', 'secret', 'data') memcache_crypt.AES = aes python-keystoneclient-0.7.1/keystoneclient/tests/test_https.py0000664000175400017540000000747212315030450026214 0ustar jenkinsjenkins00000000000000# 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 import requests from keystoneclient import httpclient from keystoneclient.tests import utils FAKE_RESPONSE = utils.TestResponse({ "status_code": 200, "text": '{"hi": "there"}', }) REQUEST_URL = 'https://127.0.0.1:5000/hi' RESPONSE_BODY = '{"hi": "there"}' def get_client(): cl = httpclient.HTTPClient(username="username", password="password", tenant_id="tenant", auth_url="auth_test", cacert="ca.pem", key="key.pem", cert="cert.pem") return cl def get_authed_client(): cl = get_client() cl.management_url = "https://127.0.0.1:5000" cl.auth_token = "token" return cl class ClientTest(utils.TestCase): def setUp(self): super(ClientTest, self).setUp() self.request_patcher = mock.patch.object(requests, 'request', self.mox.CreateMockAnything()) self.request_patcher.start() self.addCleanup(self.request_patcher.stop) @mock.patch.object(requests, 'request') def test_get(self, MOCK_REQUEST): MOCK_REQUEST.return_value = FAKE_RESPONSE cl = get_authed_client() resp, body = cl.get("/hi") # this may become too tightly couple later mock_args, mock_kwargs = MOCK_REQUEST.call_args self.assertEqual(mock_args[0], 'GET') self.assertEqual(mock_args[1], REQUEST_URL) self.assertEqual(mock_kwargs['headers']['X-Auth-Token'], 'token') self.assertEqual(mock_kwargs['cert'], ('cert.pem', 'key.pem')) self.assertEqual(mock_kwargs['verify'], 'ca.pem') # Automatic JSON parsing self.assertEqual(body, {"hi": "there"}) @mock.patch.object(requests, 'request') def test_post(self, MOCK_REQUEST): MOCK_REQUEST.return_value = FAKE_RESPONSE cl = get_authed_client() cl.post("/hi", body=[1, 2, 3]) # this may become too tightly couple later mock_args, mock_kwargs = MOCK_REQUEST.call_args self.assertEqual(mock_args[0], 'POST') self.assertEqual(mock_args[1], REQUEST_URL) self.assertEqual(mock_kwargs['data'], '[1, 2, 3]') self.assertEqual(mock_kwargs['headers']['X-Auth-Token'], 'token') self.assertEqual(mock_kwargs['cert'], ('cert.pem', 'key.pem')) self.assertEqual(mock_kwargs['verify'], 'ca.pem') @mock.patch.object(requests, 'request') def test_post_auth(self, MOCK_REQUEST): MOCK_REQUEST.return_value = FAKE_RESPONSE cl = httpclient.HTTPClient( username="username", password="password", tenant_id="tenant", auth_url="auth_test", cacert="ca.pem", key="key.pem", cert="cert.pem") cl.management_url = "https://127.0.0.1:5000" cl.auth_token = "token" cl.post("/hi", body=[1, 2, 3]) # this may become too tightly couple later mock_args, mock_kwargs = MOCK_REQUEST.call_args self.assertEqual(mock_args[0], 'POST') self.assertEqual(mock_args[1], REQUEST_URL) self.assertEqual(mock_kwargs['data'], '[1, 2, 3]') self.assertEqual(mock_kwargs['headers']['X-Auth-Token'], 'token') self.assertEqual(mock_kwargs['cert'], ('cert.pem', 'key.pem')) self.assertEqual(mock_kwargs['verify'], 'ca.pem') python-keystoneclient-0.7.1/keystoneclient/tests/test_discovery.py0000664000175400017540000005032712315030450027056 0ustar jenkinsjenkins00000000000000# 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 httpretty import six from testtools import matchers from keystoneclient import client from keystoneclient import discover from keystoneclient import exceptions from keystoneclient.openstack.common import jsonutils from keystoneclient.tests import utils from keystoneclient.v2_0 import client as v2_client from keystoneclient.v3 import client as v3_client BASE_HOST = 'http://keystone.example.com' BASE_URL = "%s:5000/" % BASE_HOST UPDATED = '2013-03-06T00:00:00Z' TEST_SERVICE_CATALOG = [{ "endpoints": [{ "adminURL": "%s:8774/v1.0" % BASE_HOST, "region": "RegionOne", "internalURL": "%s://127.0.0.1:8774/v1.0" % BASE_HOST, "publicURL": "%s:8774/v1.0/" % BASE_HOST }], "type": "nova_compat", "name": "nova_compat" }, { "endpoints": [{ "adminURL": "http://nova/novapi/admin", "region": "RegionOne", "internalURL": "http://nova/novapi/internal", "publicURL": "http://nova/novapi/public" }], "type": "compute", "name": "nova" }, { "endpoints": [{ "adminURL": "http://glance/glanceapi/admin", "region": "RegionOne", "internalURL": "http://glance/glanceapi/internal", "publicURL": "http://glance/glanceapi/public" }], "type": "image", "name": "glance" }, { "endpoints": [{ "adminURL": "%s:35357/v2.0" % BASE_HOST, "region": "RegionOne", "internalURL": "%s:5000/v2.0" % BASE_HOST, "publicURL": "%s:5000/v2.0" % BASE_HOST }], "type": "identity", "name": "keystone" }, { "endpoints": [{ "adminURL": "http://swift/swiftapi/admin", "region": "RegionOne", "internalURL": "http://swift/swiftapi/internal", "publicURL": "http://swift/swiftapi/public" }], "type": "object-store", "name": "swift" }] V2_URL = "%sv2.0" % BASE_URL V2_DESCRIBED_BY_HTML = {'href': 'http://docs.openstack.org/api/' 'openstack-identity-service/2.0/content/', 'rel': 'describedby', 'type': 'text/html'} V2_DESCRIBED_BY_PDF = {'href': 'http://docs.openstack.org/api/openstack-ident' 'ity-service/2.0/identity-dev-guide-2.0.pdf', 'rel': 'describedby', 'type': 'application/pdf'} V2_VERSION = {'id': 'v2.0', 'links': [{'href': V2_URL, 'rel': 'self'}, V2_DESCRIBED_BY_HTML, V2_DESCRIBED_BY_PDF], 'status': 'stable', 'updated': UPDATED} V2_AUTH_RESPONSE = jsonutils.dumps({ "access": { "token": { "expires": "2020-01-01T00:00:10.000123Z", "id": 'fakeToken', "tenant": { "id": '1' }, }, "user": { "id": 'test' }, "serviceCatalog": TEST_SERVICE_CATALOG, }, }) V3_URL = "%sv3" % BASE_URL V3_MEDIA_TYPES = [{'base': 'application/json', 'type': 'application/vnd.openstack.identity-v3+json'}, {'base': 'application/xml', 'type': 'application/vnd.openstack.identity-v3+xml'}] V3_VERSION = {'id': 'v3.0', 'links': [{'href': V3_URL, 'rel': 'self'}], 'media-types': V3_MEDIA_TYPES, 'status': 'stable', 'updated': UPDATED} V3_TOKEN = u'3e2813b7ba0b4006840c3825860b86ed', V3_AUTH_RESPONSE = jsonutils.dumps({ "token": { "methods": [ "token", "password" ], "expires_at": "2020-01-01T00:00:10.000123Z", "project": { "domain": { "id": '1', "name": 'test-domain' }, "id": '1', "name": 'test-project' }, "user": { "domain": { "id": '1', "name": 'test-domain' }, "id": '1', "name": 'test-user' }, "issued_at": "2013-05-29T16:55:21.468960Z", }, }) CINDER_EXAMPLES = { "versions": [ { "status": "CURRENT", "updated": "2012-01-04T11:33:21Z", "id": "v1.0", "links": [ { "href": "%sv1/" % BASE_URL, "rel": "self" } ] }, { "status": "CURRENT", "updated": "2012-11-21T11:33:21Z", "id": "v2.0", "links": [ { "href": "%sv2/" % BASE_URL, "rel": "self" } ] } ] } GLANCE_EXAMPLES = { "versions": [ { "status": "CURRENT", "id": "v2.2", "links": [ { "href": "%sv2/" % BASE_URL, "rel": "self" } ] }, { "status": "SUPPORTED", "id": "v2.1", "links": [ { "href": "%sv2/" % BASE_URL, "rel": "self" } ] }, { "status": "SUPPORTED", "id": "v2.0", "links": [ { "href": "%sv2/" % BASE_URL, "rel": "self" } ] }, { "status": "CURRENT", "id": "v1.1", "links": [ { "href": "%sv1/" % BASE_URL, "rel": "self" } ] }, { "status": "SUPPORTED", "id": "v1.0", "links": [ { "href": "%sv1/" % BASE_URL, "rel": "self" } ] } ] } def _create_version_list(versions): return jsonutils.dumps({'versions': {'values': versions}}) def _create_single_version(version): return jsonutils.dumps({'version': version}) V3_VERSION_LIST = _create_version_list([V3_VERSION, V2_VERSION]) V2_VERSION_LIST = _create_version_list([V2_VERSION]) V3_VERSION_ENTRY = _create_single_version(V3_VERSION) V2_VERSION_ENTRY = _create_single_version(V2_VERSION) @httpretty.activate class AvailableVersionsTests(utils.TestCase): def test_available_versions_basics(self): examples = {'keystone': V3_VERSION_LIST, 'cinder': jsonutils.dumps(CINDER_EXAMPLES), 'glance': jsonutils.dumps(GLANCE_EXAMPLES)} for path, ex in six.iteritems(examples): url = "%s%s" % (BASE_URL, path) httpretty.register_uri(httpretty.GET, url, status=300, body=ex) versions = discover.available_versions(url) for v in versions: for n in ('id', 'status', 'links'): msg = '%s missing from %s version data' % (n, path) self.assertThat(v, matchers.Annotate(msg, matchers.Contains(n))) def test_available_versions_individual(self): httpretty.register_uri(httpretty.GET, V3_URL, status=200, body=V3_VERSION_ENTRY) versions = discover.available_versions(V3_URL) for v in versions: self.assertEqual(v['id'], 'v3.0') self.assertEqual(v['status'], 'stable') self.assertIn('media-types', v) self.assertIn('links', v) def test_available_keystone_data(self): httpretty.register_uri(httpretty.GET, BASE_URL, status=300, body=V3_VERSION_LIST) versions = discover.available_versions(BASE_URL) self.assertEqual(2, len(versions)) for v in versions: self.assertIn(v['id'], ('v2.0', 'v3.0')) self.assertEqual(v['updated'], UPDATED) self.assertEqual(v['status'], 'stable') if v['id'] == 'v3.0': self.assertEqual(v['media-types'], V3_MEDIA_TYPES) def test_available_cinder_data(self): body = jsonutils.dumps(CINDER_EXAMPLES) httpretty.register_uri(httpretty.GET, BASE_URL, status=300, body=body) versions = discover.available_versions(BASE_URL) self.assertEqual(2, len(versions)) for v in versions: self.assertEqual(v['status'], 'CURRENT') if v['id'] == 'v1.0': self.assertEqual(v['updated'], '2012-01-04T11:33:21Z') elif v['id'] == 'v2.0': self.assertEqual(v['updated'], '2012-11-21T11:33:21Z') else: self.fail("Invalid version found") def test_available_glance_data(self): body = jsonutils.dumps(GLANCE_EXAMPLES) httpretty.register_uri(httpretty.GET, BASE_URL, status=200, body=body) versions = discover.available_versions(BASE_URL) self.assertEqual(5, len(versions)) for v in versions: if v['id'] in ('v2.2', 'v1.1'): self.assertEqual(v['status'], 'CURRENT') elif v['id'] in ('v2.1', 'v2.0', 'v1.0'): self.assertEqual(v['status'], 'SUPPORTED') else: self.fail("Invalid version found") @httpretty.activate class ClientDiscoveryTests(utils.TestCase): def assertCreatesV3(self, **kwargs): httpretty.register_uri(httpretty.POST, "%s/auth/tokens" % V3_URL, body=V3_AUTH_RESPONSE, X_Subject_Token=V3_TOKEN) kwargs.setdefault('username', 'foo') kwargs.setdefault('password', 'bar') keystone = client.Client(**kwargs) self.assertIsInstance(keystone, v3_client.Client) return keystone def assertCreatesV2(self, **kwargs): httpretty.register_uri(httpretty.POST, "%s/tokens" % V2_URL, body=V2_AUTH_RESPONSE) kwargs.setdefault('username', 'foo') kwargs.setdefault('password', 'bar') keystone = client.Client(**kwargs) self.assertIsInstance(keystone, v2_client.Client) return keystone def assertVersionNotAvailable(self, **kwargs): kwargs.setdefault('username', 'foo') kwargs.setdefault('password', 'bar') self.assertRaises(exceptions.VersionNotAvailable, client.Client, **kwargs) def assertDiscoveryFailure(self, **kwargs): kwargs.setdefault('username', 'foo') kwargs.setdefault('password', 'bar') self.assertRaises(exceptions.DiscoveryFailure, client.Client, **kwargs) def test_discover_v3(self): httpretty.register_uri(httpretty.GET, BASE_URL, status=300, body=V3_VERSION_LIST) self.assertCreatesV3(auth_url=BASE_URL) def test_discover_v2(self): httpretty.register_uri(httpretty.GET, BASE_URL, status=300, body=V2_VERSION_LIST) httpretty.register_uri(httpretty.POST, "%s/tokens" % V2_URL, body=V2_AUTH_RESPONSE) self.assertCreatesV2(auth_url=BASE_URL) def test_discover_endpoint_v2(self): httpretty.register_uri(httpretty.GET, BASE_URL, status=300, body=V2_VERSION_LIST) self.assertCreatesV2(endpoint=BASE_URL, token='fake-token') def test_discover_endpoint_v3(self): httpretty.register_uri(httpretty.GET, BASE_URL, status=300, body=V3_VERSION_LIST) self.assertCreatesV3(endpoint=BASE_URL, token='fake-token') def test_discover_invalid_major_version(self): httpretty.register_uri(httpretty.GET, BASE_URL, status=300, body=V3_VERSION_LIST) self.assertVersionNotAvailable(auth_url=BASE_URL, version=5) def test_discover_200_response_fails(self): httpretty.register_uri(httpretty.GET, BASE_URL, status=200, body='ok') self.assertDiscoveryFailure(auth_url=BASE_URL) def test_discover_minor_greater_than_available_fails(self): httpretty.register_uri(httpretty.GET, BASE_URL, status=300, body=V3_VERSION_LIST) self.assertVersionNotAvailable(endpoint=BASE_URL, version=3.4) def test_discover_individual_version_v2(self): httpretty.register_uri(httpretty.GET, V2_URL, status=200, body=V2_VERSION_ENTRY) self.assertCreatesV2(auth_url=V2_URL) def test_discover_individual_version_v3(self): httpretty.register_uri(httpretty.GET, V3_URL, status=200, body=V3_VERSION_ENTRY) self.assertCreatesV3(auth_url=V3_URL) def test_discover_individual_endpoint_v2(self): httpretty.register_uri(httpretty.GET, V2_URL, status=200, body=V2_VERSION_ENTRY) self.assertCreatesV2(endpoint=V2_URL, token='fake-token') def test_discover_individual_endpoint_v3(self): httpretty.register_uri(httpretty.GET, V3_URL, status=200, body=V3_VERSION_ENTRY) self.assertCreatesV3(endpoint=V3_URL, token='fake-token') def test_discover_fail_to_create_bad_individual_version(self): httpretty.register_uri(httpretty.GET, V2_URL, status=200, body=V2_VERSION_ENTRY) httpretty.register_uri(httpretty.GET, V3_URL, status=200, body=V3_VERSION_ENTRY) self.assertVersionNotAvailable(auth_url=V2_URL, version=3) self.assertVersionNotAvailable(auth_url=V3_URL, version=2) def test_discover_unstable_versions(self): v3_unstable_version = V3_VERSION.copy() v3_unstable_version['status'] = 'beta' version_list = _create_version_list([v3_unstable_version, V2_VERSION]) httpretty.register_uri(httpretty.GET, BASE_URL, status=300, body=version_list) self.assertCreatesV2(auth_url=BASE_URL) self.assertVersionNotAvailable(auth_url=BASE_URL, version=3) self.assertCreatesV3(auth_url=BASE_URL, unstable=True) def test_discover_forwards_original_ip(self): httpretty.register_uri(httpretty.GET, BASE_URL, status=300, body=V3_VERSION_LIST) ip = '192.168.1.1' self.assertCreatesV3(auth_url=BASE_URL, original_ip=ip) self.assertThat(httpretty.last_request().headers['forwarded'], matchers.Contains(ip)) def test_discover_bad_args(self): self.assertRaises(exceptions.DiscoveryFailure, client.Client) def test_discover_bad_response(self): httpretty.register_uri(httpretty.GET, BASE_URL, status=300, body=jsonutils.dumps({'FOO': 'BAR'})) self.assertDiscoveryFailure(auth_url=BASE_URL) def test_discovery_ignore_invalid(self): resp = [{'id': '3.99', # without a leading v 'links': [{'href': V3_URL, 'rel': 'self'}], 'media-types': V3_MEDIA_TYPES, 'status': 'stable', 'updated': UPDATED}, {'id': 'v3.0', 'links': [1, 2, 3, 4], # invalid links 'media-types': V3_MEDIA_TYPES, 'status': 'stable', 'updated': UPDATED}] httpretty.register_uri(httpretty.GET, BASE_URL, status=300, body=_create_version_list(resp)) self.assertDiscoveryFailure(auth_url=BASE_URL) def test_ignore_entry_without_links(self): v3 = V3_VERSION.copy() v3['links'] = [] httpretty.register_uri(httpretty.GET, BASE_URL, status=300, body=_create_version_list([v3, V2_VERSION])) self.assertCreatesV2(auth_url=BASE_URL) def test_ignore_entry_without_status(self): v3 = V3_VERSION.copy() del v3['status'] httpretty.register_uri(httpretty.GET, BASE_URL, status=300, body=_create_version_list([v3, V2_VERSION])) self.assertCreatesV2(auth_url=BASE_URL) def test_greater_version_than_required(self): resp = [{'id': 'v3.6', 'links': [{'href': V3_URL, 'rel': 'self'}], 'media-types': V3_MEDIA_TYPES, 'status': 'stable', 'updated': UPDATED}] httpretty.register_uri(httpretty.GET, BASE_URL, status=200, body=_create_version_list(resp)) self.assertCreatesV3(auth_url=BASE_URL, version=(3, 4)) def test_lesser_version_than_required(self): resp = [{'id': 'v3.4', 'links': [{'href': V3_URL, 'rel': 'self'}], 'media-types': V3_MEDIA_TYPES, 'status': 'stable', 'updated': UPDATED}] httpretty.register_uri(httpretty.GET, BASE_URL, status=200, body=_create_version_list(resp)) self.assertVersionNotAvailable(auth_url=BASE_URL, version=(3, 6)) def test_bad_response(self): httpretty.register_uri(httpretty.GET, BASE_URL, status=300, body="Ugly Duckling") self.assertDiscoveryFailure(auth_url=BASE_URL) def test_pass_client_arguments(self): httpretty.register_uri(httpretty.GET, BASE_URL, status=300, body=V2_VERSION_LIST) kwargs = {'original_ip': '100', 'use_keyring': False, 'stale_duration': 15} cl = self.assertCreatesV2(auth_url=BASE_URL, **kwargs) self.assertEqual(cl.original_ip, '100') self.assertEqual(cl.stale_duration, 15) self.assertFalse(cl.use_keyring) def test_overriding_stored_kwargs(self): httpretty.register_uri(httpretty.GET, BASE_URL, status=300, body=V3_VERSION_LIST) httpretty.register_uri(httpretty.POST, "%s/auth/tokens" % V3_URL, body=V3_AUTH_RESPONSE, X_Subject_Token=V3_TOKEN) disc = discover.Discover(auth_url=BASE_URL, debug=False, username='foo') client = disc.create_client(debug=True, password='bar') self.assertIsInstance(client, v3_client.Client) self.assertTrue(client.debug_log) self.assertFalse(disc._client_kwargs['debug']) self.assertEqual(client.username, 'foo') self.assertEqual(client.password, 'bar') class DiscoverUtils(utils.TestCase): def test_version_number(self): def assertVersion(inp, out): self.assertEqual(discover._normalize_version_number(inp), out) def versionRaises(inp): self.assertRaises(TypeError, discover._normalize_version_number, inp) assertVersion('v1.2', (1, 2)) assertVersion('v11', (11, 0)) assertVersion('1.2', (1, 2)) assertVersion('1.5.1', (1, 5, 1)) assertVersion('1', (1, 0)) assertVersion(1, (1, 0)) assertVersion(5.2, (5, 2)) assertVersion((6, 1), (6, 1)) assertVersion([1, 4], (1, 4)) versionRaises('hello') versionRaises('1.a') versionRaises('vaccuum') def test_keystone_version_objects(self): v31s = discover._KeystoneVersion((3, 1), 'stable') v20s = discover._KeystoneVersion((2, 0), 'stable') v30s = discover._KeystoneVersion((3, 0), 'stable') v31a = discover._KeystoneVersion((3, 1), 'alpha') v31b = discover._KeystoneVersion((3, 1), 'beta') self.assertTrue(v31s > v30s) self.assertTrue(v30s > v20s) self.assertTrue(v31s > v31a) self.assertFalse(v31s < v31a) self.assertTrue(v31b > v31a) self.assertTrue(v31a < v31b) self.assertTrue(v31b > v30s) self.assertNotEqual(v31s, v31b) self.assertEqual(v31s, discover._KeystoneVersion((3, 1), 'stable')) python-keystoneclient-0.7.1/keystoneclient/tests/test_ec2utils.py0000664000175400017540000002571012315030450026577 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from __future__ import unicode_literals import testtools from keystoneclient.contrib.ec2 import utils class Ec2SignerTest(testtools.TestCase): def setUp(self): super(Ec2SignerTest, self).setUp() self.access = '966afbde20b84200ae4e62e09acf46b2' self.secret = '89cdf9e94e2643cab35b8b8ac5a51f83' self.signer = utils.Ec2Signer(self.secret) def test_v4_creds_header(self): auth_str = 'AWS4-HMAC-SHA256 blah' credentials = {'host': '127.0.0.1', 'verb': 'GET', 'path': '/v1/', 'params': {}, 'headers': {'Authorization': auth_str}} self.assertTrue(self.signer._v4_creds(credentials)) def test_v4_creds_param(self): credentials = {'host': '127.0.0.1', 'verb': 'GET', 'path': '/v1/', 'params': {'X-Amz-Algorithm': 'AWS4-HMAC-SHA256'}, 'headers': {}} self.assertTrue(self.signer._v4_creds(credentials)) def test_v4_creds_false(self): credentials = {'host': '127.0.0.1', 'verb': 'GET', 'path': '/v1/', 'params': {'SignatureVersion': '0', 'AWSAccessKeyId': self.access, 'Timestamp': '2012-11-27T11:47:02Z', 'Action': 'Foo'}} self.assertFalse(self.signer._v4_creds(credentials)) def test_generate_0(self): """Test generate function for v0 signature.""" credentials = {'host': '127.0.0.1', 'verb': 'GET', 'path': '/v1/', 'params': {'SignatureVersion': '0', 'AWSAccessKeyId': self.access, 'Timestamp': '2012-11-27T11:47:02Z', 'Action': 'Foo'}} signature = self.signer.generate(credentials) expected = 'SmXQEZAUdQw5glv5mX8mmixBtas=' self.assertEqual(signature, expected) def test_generate_1(self): """Test generate function for v1 signature.""" credentials = {'host': '127.0.0.1', 'verb': 'GET', 'path': '/v1/', 'params': {'SignatureVersion': '1', 'AWSAccessKeyId': self.access}} signature = self.signer.generate(credentials) expected = 'VRnoQH/EhVTTLhwRLfuL7jmFW9c=' self.assertEqual(signature, expected) def test_generate_v2_SHA256(self): """Test generate function for v2 signature, SHA256.""" credentials = {'host': '127.0.0.1', 'verb': 'GET', 'path': '/v1/', 'params': {'SignatureVersion': '2', 'AWSAccessKeyId': self.access}} signature = self.signer.generate(credentials) expected = 'odsGmT811GffUO0Eu13Pq+xTzKNIjJ6NhgZU74tYX/w=' self.assertEqual(signature, expected) def test_generate_v2_SHA1(self): """Test generate function for v2 signature, SHA1.""" credentials = {'host': '127.0.0.1', 'verb': 'GET', 'path': '/v1/', 'params': {'SignatureVersion': '2', 'AWSAccessKeyId': self.access}} self.signer.hmac_256 = None signature = self.signer.generate(credentials) expected = 'ZqCxMI4ZtTXWI175743mJ0hy/Gc=' self.assertEqual(signature, expected) def test_generate_v4(self): """Test v4 generator with data from AWS docs example. see: http://docs.aws.amazon.com/general/latest/gr/ sigv4-create-canonical-request.html and http://docs.aws.amazon.com/general/latest/gr/ sigv4-signed-request-examples.html """ # Create a new signer object with the AWS example key secret = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY' signer = utils.Ec2Signer(secret) body_hash = ('b6359072c78d70ebee1e81adcbab4f0' '1bf2c23245fa365ef83fe8f1f955085e2') auth_str = ('AWS4-HMAC-SHA256 ' 'Credential=AKIAIOSFODNN7EXAMPLE/20110909/' 'us-east-1/iam/aws4_request,' 'SignedHeaders=content-type;host;x-amz-date,') headers = {'Content-type': 'application/x-www-form-urlencoded; charset=utf-8', 'X-Amz-Date': '20110909T233600Z', 'Host': 'iam.amazonaws.com', 'Authorization': auth_str} # Note the example in the AWS docs is inconsistent, previous # examples specify no query string, but the final POST example # does, apparently incorrectly since an empty parameter list # aligns all steps and the final signature with the examples params = {} credentials = {'host': 'iam.amazonaws.com', 'verb': 'POST', 'path': '/', 'params': params, 'headers': headers, 'body_hash': body_hash} signature = signer.generate(credentials) expected = ('ced6826de92d2bdeed8f846f0bf508e8' '559e98e4b0199114b84c54174deb456c') self.assertEqual(signature, expected) def test_generate_v4_port(self): """Test v4 generator with host:port format.""" # Create a new signer object with the AWS example key secret = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY' signer = utils.Ec2Signer(secret) body_hash = ('b6359072c78d70ebee1e81adcbab4f0' '1bf2c23245fa365ef83fe8f1f955085e2') auth_str = ('AWS4-HMAC-SHA256 ' 'Credential=AKIAIOSFODNN7EXAMPLE/20110909/' 'us-east-1/iam/aws4_request,' 'SignedHeaders=content-type;host;x-amz-date,') headers = {'Content-type': 'application/x-www-form-urlencoded; charset=utf-8', 'X-Amz-Date': '20110909T233600Z', 'Host': 'foo:8000', 'Authorization': auth_str} # Note the example in the AWS docs is inconsistent, previous # examples specify no query string, but the final POST example # does, apparently incorrectly since an empty parameter list # aligns all steps and the final signature with the examples params = {} credentials = {'host': 'foo:8000', 'verb': 'POST', 'path': '/', 'params': params, 'headers': headers, 'body_hash': body_hash} signature = signer.generate(credentials) expected = ('26dd92ea79aaa49f533d13b1055acdc' 'd7d7321460d64621f96cc79c4f4d4ab2b') self.assertEqual(signature, expected) def test_generate_v4_port_strip(self): """Test v4 generator with host:port format, but for an old (<2.9.3) version of boto, where the port should be stripped to match boto behavior. """ # Create a new signer object with the AWS example key secret = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY' signer = utils.Ec2Signer(secret) body_hash = ('b6359072c78d70ebee1e81adcbab4f0' '1bf2c23245fa365ef83fe8f1f955085e2') auth_str = ('AWS4-HMAC-SHA256 ' 'Credential=AKIAIOSFODNN7EXAMPLE/20110909/' 'us-east-1/iam/aws4_request,' 'SignedHeaders=content-type;host;x-amz-date,') headers = {'Content-type': 'application/x-www-form-urlencoded; charset=utf-8', 'X-Amz-Date': '20110909T233600Z', 'Host': 'foo:8000', 'Authorization': auth_str, 'User-Agent': 'Boto/2.9.2 (linux2)'} # Note the example in the AWS docs is inconsistent, previous # examples specify no query string, but the final POST example # does, apparently incorrectly since an empty parameter list # aligns all steps and the final signature with the examples params = {} credentials = {'host': 'foo:8000', 'verb': 'POST', 'path': '/', 'params': params, 'headers': headers, 'body_hash': body_hash} signature = signer.generate(credentials) expected = ('9a4b2276a5039ada3b90f72ea8ec1745' '14b92b909fb106b22ad910c5d75a54f4') self.assertEqual(expected, signature) def test_generate_v4_port_nostrip(self): """Test v4 generator with host:port format, but for an new (>=2.9.3) version of boto, where the port should not be stripped. """ # Create a new signer object with the AWS example key secret = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY' signer = utils.Ec2Signer(secret) body_hash = ('b6359072c78d70ebee1e81adcbab4f0' '1bf2c23245fa365ef83fe8f1f955085e2') auth_str = ('AWS4-HMAC-SHA256 ' 'Credential=AKIAIOSFODNN7EXAMPLE/20110909/' 'us-east-1/iam/aws4_request,' 'SignedHeaders=content-type;host;x-amz-date,') headers = {'Content-type': 'application/x-www-form-urlencoded; charset=utf-8', 'X-Amz-Date': '20110909T233600Z', 'Host': 'foo:8000', 'Authorization': auth_str, 'User-Agent': 'Boto/2.9.3 (linux2)'} # Note the example in the AWS docs is inconsistent, previous # examples specify no query string, but the final POST example # does, apparently incorrectly since an empty parameter list # aligns all steps and the final signature with the examples params = {} credentials = {'host': 'foo:8000', 'verb': 'POST', 'path': '/', 'params': params, 'headers': headers, 'body_hash': body_hash} signature = signer.generate(credentials) expected = ('26dd92ea79aaa49f533d13b1055acdc' 'd7d7321460d64621f96cc79c4f4d4ab2b') self.assertEqual(expected, signature) python-keystoneclient-0.7.1/keystoneclient/tests/fakes.py0000664000175400017540000001033412315030450025073 0ustar jenkinsjenkins00000000000000# 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. """ A fake server that "responds" to API methods with pre-canned responses. All of these responses come from the spec, so if for some reason the spec's wrong the tests might raise AssertionError. I've indicated in comments the places where actual behavior differs from the spec. """ from keystoneclient import access def assert_has_keys(dict, required=[], optional=[]): keys = dict.keys() for k in required: try: assert k in keys except AssertionError: extra_keys = set(keys).difference(set(required + optional)) raise AssertionError("found unexpected keys: %s" % list(extra_keys)) class FakeClient(object): def assert_called(self, method, url, body=None, pos=-1): """Assert than an API method was just called.""" expected = (method, url) called = self.callstack[pos][0:2] assert self.callstack, ("Expected %s %s but no calls were made." % expected) assert expected == called, ("Expected %s %s; got %s %s" % (expected + called)) if body is not None: assert self.callstack[pos][2] == body def assert_called_anytime(self, method, url, body=None): """Assert than an API method was called anytime in the test.""" expected = (method, url) assert self.callstack, ("Expected %s %s but no calls were made." % expected) found = False for entry in self.callstack: if expected == entry[0:2]: found = True break assert found, ('Expected %s; got %s' % (expected, self.callstack)) if body is not None: if entry[2] != body: raise AssertionError('%s != %s' % (entry[2], body)) self.callstack = [] def clear_callstack(self): self.callstack = [] def authenticate(self, cl_obj): cl_obj.user_id = '1' cl_obj.auth_user_id = '1' cl_obj.project_id = '1' cl_obj.auth_tenant_id = '1' cl_obj.auth_ref = access.AccessInfo.factory(None, { "access": { "token": { "expires": "2012-02-05T00:00:00", "id": "887665443383838", "tenant": { "id": "1", "name": "customer-x" } }, "serviceCatalog": [{ "endpoints": [{ "adminURL": "http://swift.admin-nets.local:8080/", "region": "RegionOne", "internalURL": "http://127.0.0.1:8080/v1/AUTH_1", "publicURL": "http://swift.publicinternets.com/v1/AUTH_1" }], "type": "object-store", "name": "swift" }, { "endpoints": [{ "adminURL": "http://cdn.admin-nets.local/v1.1/1", "region": "RegionOne", "internalURL": "http://127.0.0.1:7777/v1.1/1", "publicURL": "http://cdn.publicinternets.com/v1.1/1" }], "type": "object-store", "name": "cdn" }], "user": { "id": "1", "roles": [{ "tenantId": "1", "id": "3", "name": "Member" }], "name": "joeuser" } } }) python-keystoneclient-0.7.1/keystoneclient/tests/utils.py0000664000175400017540000001105112315030450025137 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging import sys import time import fixtures import httpretty import mock from mox3 import mox import requests import six from six.moves.urllib import parse as urlparse import testtools from keystoneclient.openstack.common import jsonutils class TestCase(testtools.TestCase): TEST_DOMAIN_ID = '1' TEST_DOMAIN_NAME = 'aDomain' TEST_TENANT_ID = '1' TEST_TENANT_NAME = 'aTenant' TEST_TOKEN = 'aToken' TEST_TRUST_ID = 'aTrust' TEST_USER = 'test' TEST_ROOT_URL = 'http://127.0.0.1:5000/' def setUp(self): super(TestCase, self).setUp() self.mox = mox.Mox() self.logger = self.useFixture(fixtures.FakeLogger(level=logging.DEBUG)) self.time_patcher = mock.patch.object(time, 'time', lambda: 1234) self.time_patcher.start() def tearDown(self): self.time_patcher.stop() self.mox.UnsetStubs() self.mox.VerifyAll() super(TestCase, self).tearDown() def stub_url(self, method, parts=None, base_url=None, json=None, **kwargs): if not base_url: base_url = self.TEST_URL if json: kwargs['body'] = jsonutils.dumps(json) kwargs['content_type'] = 'application/json' if parts: url = '/'.join([p.strip('/') for p in [base_url] + parts]) else: url = base_url httpretty.register_uri(method, url, **kwargs) def assertRequestBodyIs(self, body=None, json=None): last_request_body = httpretty.last_request().body if six.PY3: last_request_body = last_request_body.decode('utf-8') if json: val = jsonutils.loads(last_request_body) self.assertEqual(json, val) elif body: self.assertEqual(body, last_request_body) def assertQueryStringIs(self, qs=''): """Verify the QueryString matches what is expected. The qs parameter should be of the format \'foo=bar&abc=xyz\' """ expected = urlparse.parse_qs(qs) self.assertEqual(expected, httpretty.last_request().querystring) def assertQueryStringContains(self, **kwargs): qs = httpretty.last_request().querystring for k, v in six.iteritems(kwargs): self.assertIn(k, qs) self.assertIn(v, qs[k]) def assertRequestHeaderEqual(self, name, val): """Verify that the last request made contains a header and its value The request must have already been made and httpretty must have been activated for the request. """ headers = httpretty.last_request().headers self.assertEqual(headers.get(name), val) if tuple(sys.version_info)[0:2] < (2, 7): def assertDictEqual(self, d1, d2, msg=None): # Simple version taken from 2.7 self.assertIsInstance(d1, dict, 'First argument is not a dictionary') self.assertIsInstance(d2, dict, 'Second argument is not a dictionary') if d1 != d2: if msg: self.fail(msg) else: standardMsg = '%r != %r' % (d1, d2) self.fail(standardMsg) TestCase.assertDictEqual = assertDictEqual class TestResponse(requests.Response): """Class used to wrap requests.Response and provide some convenience to initialize with a dict. """ def __init__(self, data): self._text = None super(TestResponse, self).__init__() if isinstance(data, dict): self.status_code = data.get('status_code', 200) headers = data.get('headers') if headers: self.headers.update(headers) # Fake the text attribute to streamline Response creation # _content is defined by requests.Response self._content = data.get('text') else: self.status_code = data def __eq__(self, other): return self.__dict__ == other.__dict__ @property def text(self): return self.content python-keystoneclient-0.7.1/keystoneclient/tests/test_s3_token_middleware.py0000664000175400017540000002344412315030450030771 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import httpretty import mock import requests import six import testtools import webob from keystoneclient.middleware import s3_token from keystoneclient.openstack.common import jsonutils from keystoneclient.tests import utils GOOD_RESPONSE = {'access': {'token': {'id': 'TOKEN_ID', 'tenant': {'id': 'TENANT_ID'}}}} class FakeApp(object): """This represents a WSGI app protected by the auth_token middleware.""" def __call__(self, env, start_response): resp = webob.Response() resp.environ = env return resp(env, start_response) class S3TokenMiddlewareTestBase(utils.TestCase): TEST_PROTOCOL = 'https' TEST_HOST = 'fakehost' TEST_PORT = 35357 TEST_URL = '%s://%s:%d/v2.0/s3tokens' % (TEST_PROTOCOL, TEST_HOST, TEST_PORT) def setUp(self): super(S3TokenMiddlewareTestBase, self).setUp() self.conf = { 'auth_host': self.TEST_HOST, 'auth_port': self.TEST_PORT, 'auth_protocol': self.TEST_PROTOCOL, } httpretty.reset() httpretty.enable() self.addCleanup(httpretty.disable) def start_fake_response(self, status, headers): self.response_status = int(status.split(' ', 1)[0]) self.response_headers = dict(headers) class S3TokenMiddlewareTestGood(S3TokenMiddlewareTestBase): def setUp(self): super(S3TokenMiddlewareTestGood, self).setUp() self.middleware = s3_token.S3Token(FakeApp(), self.conf) httpretty.register_uri(httpretty.POST, self.TEST_URL, status=201, body=jsonutils.dumps(GOOD_RESPONSE)) # Ignore the request and pass to the next middleware in the # pipeline if no path has been specified. def test_no_path_request(self): req = webob.Request.blank('/') self.middleware(req.environ, self.start_fake_response) self.assertEqual(self.response_status, 200) # Ignore the request and pass to the next middleware in the # pipeline if no Authorization header has been specified def test_without_authorization(self): req = webob.Request.blank('/v1/AUTH_cfa/c/o') self.middleware(req.environ, self.start_fake_response) self.assertEqual(self.response_status, 200) def test_without_auth_storage_token(self): req = webob.Request.blank('/v1/AUTH_cfa/c/o') req.headers['Authorization'] = 'badboy' self.middleware(req.environ, self.start_fake_response) self.assertEqual(self.response_status, 200) def test_authorized(self): req = webob.Request.blank('/v1/AUTH_cfa/c/o') req.headers['Authorization'] = 'access:signature' req.headers['X-Storage-Token'] = 'token' req.get_response(self.middleware) self.assertTrue(req.path.startswith('/v1/AUTH_TENANT_ID')) self.assertEqual(req.headers['X-Auth-Token'], 'TOKEN_ID') def test_authorized_http(self): self.middleware = ( s3_token.filter_factory({'auth_protocol': 'http', 'auth_host': self.TEST_HOST, 'auth_port': self.TEST_PORT})(FakeApp())) req = webob.Request.blank('/v1/AUTH_cfa/c/o') req.headers['Authorization'] = 'access:signature' req.headers['X-Storage-Token'] = 'token' req.get_response(self.middleware) self.assertTrue(req.path.startswith('/v1/AUTH_TENANT_ID')) self.assertEqual(req.headers['X-Auth-Token'], 'TOKEN_ID') def test_authorization_nova_toconnect(self): req = webob.Request.blank('/v1/AUTH_swiftint/c/o') req.headers['Authorization'] = 'access:FORCED_TENANT_ID:signature' req.headers['X-Storage-Token'] = 'token' req.get_response(self.middleware) path = req.environ['PATH_INFO'] self.assertTrue(path.startswith('/v1/AUTH_FORCED_TENANT_ID')) @mock.patch.object(requests, 'post') def test_insecure(self, MOCK_REQUEST): self.middleware = ( s3_token.filter_factory({'insecure': True})(FakeApp())) text_return_value = jsonutils.dumps(GOOD_RESPONSE) if six.PY3: text_return_value = text_return_value.encode() MOCK_REQUEST.return_value = utils.TestResponse({ 'status_code': 201, 'text': text_return_value}) req = webob.Request.blank('/v1/AUTH_cfa/c/o') req.headers['Authorization'] = 'access:signature' req.headers['X-Storage-Token'] = 'token' req.get_response(self.middleware) self.assertTrue(MOCK_REQUEST.called) mock_args, mock_kwargs = MOCK_REQUEST.call_args self.assertIs(mock_kwargs['verify'], False) class S3TokenMiddlewareTestBad(S3TokenMiddlewareTestBase): def setUp(self): super(S3TokenMiddlewareTestBad, self).setUp() self.middleware = s3_token.S3Token(FakeApp(), self.conf) def test_unauthorized_token(self): ret = {"error": {"message": "EC2 access key not found.", "code": 401, "title": "Unauthorized"}} httpretty.register_uri(httpretty.POST, self.TEST_URL, status=403, body=jsonutils.dumps(ret)) req = webob.Request.blank('/v1/AUTH_cfa/c/o') req.headers['Authorization'] = 'access:signature' req.headers['X-Storage-Token'] = 'token' resp = req.get_response(self.middleware) s3_denied_req = self.middleware.deny_request('AccessDenied') self.assertEqual(resp.body, s3_denied_req.body) self.assertEqual(resp.status_int, s3_denied_req.status_int) def test_bogus_authorization(self): req = webob.Request.blank('/v1/AUTH_cfa/c/o') req.headers['Authorization'] = 'badboy' req.headers['X-Storage-Token'] = 'token' resp = req.get_response(self.middleware) self.assertEqual(resp.status_int, 400) s3_invalid_req = self.middleware.deny_request('InvalidURI') self.assertEqual(resp.body, s3_invalid_req.body) self.assertEqual(resp.status_int, s3_invalid_req.status_int) def test_fail_to_connect_to_keystone(self): with mock.patch.object(self.middleware, '_json_request') as o: s3_invalid_req = self.middleware.deny_request('InvalidURI') o.side_effect = s3_token.ServiceError(s3_invalid_req) req = webob.Request.blank('/v1/AUTH_cfa/c/o') req.headers['Authorization'] = 'access:signature' req.headers['X-Storage-Token'] = 'token' resp = req.get_response(self.middleware) self.assertEqual(resp.body, s3_invalid_req.body) self.assertEqual(resp.status_int, s3_invalid_req.status_int) def test_bad_reply(self): httpretty.register_uri(httpretty.POST, self.TEST_URL, status=201, body="") req = webob.Request.blank('/v1/AUTH_cfa/c/o') req.headers['Authorization'] = 'access:signature' req.headers['X-Storage-Token'] = 'token' resp = req.get_response(self.middleware) s3_invalid_req = self.middleware.deny_request('InvalidURI') self.assertEqual(resp.body, s3_invalid_req.body) self.assertEqual(resp.status_int, s3_invalid_req.status_int) class S3TokenMiddlewareTestUtil(testtools.TestCase): def test_split_path_failed(self): self.assertRaises(ValueError, s3_token.split_path, '') self.assertRaises(ValueError, s3_token.split_path, '/') self.assertRaises(ValueError, s3_token.split_path, '//') self.assertRaises(ValueError, s3_token.split_path, '//a') self.assertRaises(ValueError, s3_token.split_path, '/a/c') self.assertRaises(ValueError, s3_token.split_path, '//c') self.assertRaises(ValueError, s3_token.split_path, '/a/c/') self.assertRaises(ValueError, s3_token.split_path, '/a//') self.assertRaises(ValueError, s3_token.split_path, '/a', 2) self.assertRaises(ValueError, s3_token.split_path, '/a', 2, 3) self.assertRaises(ValueError, s3_token.split_path, '/a', 2, 3, True) self.assertRaises(ValueError, s3_token.split_path, '/a/c/o/r', 3, 3) self.assertRaises(ValueError, s3_token.split_path, '/a', 5, 4) def test_split_path_success(self): self.assertEqual(s3_token.split_path('/a'), ['a']) self.assertEqual(s3_token.split_path('/a/'), ['a']) self.assertEqual(s3_token.split_path('/a/c', 2), ['a', 'c']) self.assertEqual(s3_token.split_path('/a/c/o', 3), ['a', 'c', 'o']) self.assertEqual(s3_token.split_path('/a/c/o/r', 3, 3, True), ['a', 'c', 'o/r']) self.assertEqual(s3_token.split_path('/a/c', 2, 3, True), ['a', 'c', None]) self.assertEqual(s3_token.split_path('/a/c/', 2), ['a', 'c']) self.assertEqual(s3_token.split_path('/a/c/', 2, 3), ['a', 'c', '']) def test_split_path_invalid_path(self): try: s3_token.split_path('o\nn e', 2) except ValueError as err: self.assertEqual(str(err), 'Invalid path: o%0An%20e') try: s3_token.split_path('o\nn e', 2, 3, True) except ValueError as err: self.assertEqual(str(err), 'Invalid path: o%0An%20e') python-keystoneclient-0.7.1/keystoneclient/tests/test_cms.py0000664000175400017540000001115012315030450025620 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import subprocess import mock import testresources from keystoneclient.common import cms from keystoneclient import exceptions from keystoneclient.tests import client_fixtures from keystoneclient.tests import utils class CMSTest(utils.TestCase, testresources.ResourcedTestCase): """Unit tests for the keystoneclient.common.cms module.""" resources = [('examples', client_fixtures.EXAMPLES_RESOURCE)] def test_cms_verify(self): self.assertRaises(exceptions.CertificateConfigError, cms.cms_verify, 'data', 'no_exist_cert_file', 'no_exist_ca_file') def test_token_to_cms_to_token(self): with open(os.path.join(client_fixtures.CMSDIR, 'auth_token_scoped.pem')) as f: AUTH_TOKEN_SCOPED_CMS = f.read() self.assertEqual(cms.token_to_cms(self.examples.SIGNED_TOKEN_SCOPED), AUTH_TOKEN_SCOPED_CMS) tok = cms.cms_to_token(cms.token_to_cms( self.examples.SIGNED_TOKEN_SCOPED)) self.assertEqual(tok, self.examples.SIGNED_TOKEN_SCOPED) def test_ans1_token(self): self.assertTrue(cms.is_ans1_token(self.examples.SIGNED_TOKEN_SCOPED)) self.assertFalse(cms.is_ans1_token('FOOBAR')) def test_cms_sign_token_no_files(self): self.assertRaises(subprocess.CalledProcessError, cms.cms_sign_token, self.examples.SIGNED_TOKEN_SCOPED, '/no/such/file', '/no/such/key') def test_cms_sign_token_success(self): self.assertTrue( cms.cms_sign_token(self.examples.SIGNED_TOKEN_SCOPED, self.examples.SIGNING_CERT_FILE, self.examples.SIGNING_KEY_FILE)) def test_cms_verify_token_no_files(self): self.assertRaises(exceptions.CertificateConfigError, cms.cms_verify, self.examples.SIGNED_TOKEN_SCOPED, '/no/such/file', '/no/such/key') def test_cms_verify_token_no_oserror(self): import errno def raise_OSError(*args): e = OSError() e.errno = errno.EPIPE raise e with mock.patch('subprocess.Popen.communicate', new=raise_OSError): try: cms.cms_verify("x", '/no/such/file', '/no/such/key') except subprocess.CalledProcessError as e: self.assertIn('/no/such/file', e.output) self.assertIn('Hit OSError ', e.output) else: self.fail('Expected subprocess.CalledProcessError') def test_cms_verify_token_scoped(self): cms_content = cms.token_to_cms(self.examples.SIGNED_TOKEN_SCOPED) self.assertTrue(cms.cms_verify(cms_content, self.examples.SIGNING_CERT_FILE, self.examples.SIGNING_CA_FILE)) def test_cms_verify_token_scoped_expired(self): cms_content = cms.token_to_cms( self.examples.SIGNED_TOKEN_SCOPED_EXPIRED) self.assertTrue(cms.cms_verify(cms_content, self.examples.SIGNING_CERT_FILE, self.examples.SIGNING_CA_FILE)) def test_cms_verify_token_unscoped(self): cms_content = cms.token_to_cms(self.examples.SIGNED_TOKEN_UNSCOPED) self.assertTrue(cms.cms_verify(cms_content, self.examples.SIGNING_CERT_FILE, self.examples.SIGNING_CA_FILE)) def test_cms_verify_token_v3_scoped(self): cms_content = cms.token_to_cms(self.examples.SIGNED_v3_TOKEN_SCOPED) self.assertTrue(cms.cms_verify(cms_content, self.examples.SIGNING_CERT_FILE, self.examples.SIGNING_CA_FILE)) def load_tests(loader, tests, pattern): return testresources.OptimisingTestSuite(tests) python-keystoneclient-0.7.1/keystoneclient/tests/test_auth_token_middleware.py0000664000175400017540000021112512315030450031400 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import calendar import datetime import os import shutil import stat import sys import tempfile import time import uuid import fixtures import httpretty import iso8601 import mock import testresources import testtools import webob from keystoneclient.common import cms from keystoneclient import exceptions from keystoneclient.middleware import auth_token from keystoneclient.openstack.common import jsonutils from keystoneclient.openstack.common import memorycache from keystoneclient.openstack.common import timeutils from keystoneclient.tests import client_fixtures EXPECTED_V2_DEFAULT_ENV_RESPONSE = { 'HTTP_X_IDENTITY_STATUS': 'Confirmed', 'HTTP_X_TENANT_ID': 'tenant_id1', 'HTTP_X_TENANT_NAME': 'tenant_name1', 'HTTP_X_USER_ID': 'user_id1', 'HTTP_X_USER_NAME': 'user_name1', 'HTTP_X_ROLES': 'role1,role2', 'HTTP_X_USER': 'user_name1', # deprecated (diablo-compat) 'HTTP_X_TENANT': 'tenant_name1', # deprecated (diablo-compat) 'HTTP_X_ROLE': 'role1,role2', # deprecated (diablo-compat) } BASE_HOST = 'https://keystone.example.com:1234' BASE_URI = '%s/testadmin' % BASE_HOST FAKE_ADMIN_TOKEN_ID = 'admin_token2' FAKE_ADMIN_TOKEN = jsonutils.dumps( {'access': {'token': {'id': FAKE_ADMIN_TOKEN_ID, 'expires': '2022-10-03T16:58:01Z'}}}) VERSION_LIST_v3 = jsonutils.dumps({ "versions": { "values": [ { "id": "v3.0", "status": "stable", "updated": "2013-03-06T00:00:00Z", "links": [{'href': '%s/v3' % BASE_URI, 'rel': 'self'}] }, { "id": "v2.0", "status": "stable", "updated": "2011-11-19T00:00:00Z", "links": [{'href': '%s/v2.0' % BASE_URI, 'rel': 'self'}] } ] } }) VERSION_LIST_v2 = jsonutils.dumps({ "versions": { "values": [ { "id": "v2.0", "status": "stable", "updated": "2011-11-19T00:00:00Z", "links": [] } ] } }) ERROR_TOKEN = '7ae290c2a06244c4b41692eb4e9225f2' MEMCACHED_SERVERS = ['localhost:11211'] MEMCACHED_AVAILABLE = None def memcached_available(): """Do a sanity check against memcached. Returns ``True`` if the following conditions are met (otherwise, returns ``False``): - ``python-memcached`` is installed - a usable ``memcached`` instance is available via ``MEMCACHED_SERVERS`` - the client is able to set and get a key/value pair """ global MEMCACHED_AVAILABLE if MEMCACHED_AVAILABLE is None: try: import memcache c = memcache.Client(MEMCACHED_SERVERS) c.set('ping', 'pong', time=1) MEMCACHED_AVAILABLE = c.get('ping') == 'pong' except ImportError: MEMCACHED_AVAILABLE = False return MEMCACHED_AVAILABLE class NoModuleFinder(object): """Disallow further imports of 'module'.""" def __init__(self, module): self.module = module def find_module(self, fullname, path): if fullname == self.module or fullname.startswith(self.module + '.'): raise ImportError def cleanup_revoked_file(filename): try: os.remove(filename) except OSError: pass class DisableModuleFixture(fixtures.Fixture): """A fixture to provide support for unloading/disabling modules.""" def __init__(self, module, *args, **kw): super(DisableModuleFixture, self).__init__(*args, **kw) self.module = module self._finders = [] self._cleared_modules = {} def tearDown(self): super(DisableModuleFixture, self).tearDown() for finder in self._finders: sys.meta_path.remove(finder) sys.modules.update(self._cleared_modules) def clear_module(self): cleared_modules = {} for fullname in sys.modules.keys(): if (fullname == self.module or fullname.startswith(self.module + '.')): cleared_modules[fullname] = sys.modules.pop(fullname) return cleared_modules def setUp(self): """Ensure ImportError for the specified module.""" super(DisableModuleFixture, self).setUp() # Clear 'module' references in sys.modules self._cleared_modules.update(self.clear_module()) finder = NoModuleFinder(self.module) self._finders.append(finder) sys.meta_path.insert(0, finder) class TimezoneFixture(fixtures.Fixture): @staticmethod def supported(): # tzset is only supported on Unix. return hasattr(time, 'tzset') def __init__(self, new_tz): super(TimezoneFixture, self).__init__() self.tz = new_tz self.old_tz = os.environ.get('TZ') def setUp(self): super(TimezoneFixture, self).setUp() if not self.supported(): raise NotImplementedError('timezone override is not supported.') os.environ['TZ'] = self.tz time.tzset() self.addCleanup(self.cleanup) def cleanup(self): if self.old_tz is not None: os.environ['TZ'] = self.old_tz elif 'TZ' in os.environ: del os.environ['TZ'] time.tzset() class FakeApp(object): """This represents a WSGI app protected by the auth_token middleware.""" SUCCESS = b'SUCCESS' def __init__(self, expected_env=None): self.expected_env = dict(EXPECTED_V2_DEFAULT_ENV_RESPONSE) if expected_env: self.expected_env.update(expected_env) def __call__(self, env, start_response): for k, v in self.expected_env.items(): assert env[k] == v, '%s != %s' % (env[k], v) resp = webob.Response() resp.body = FakeApp.SUCCESS return resp(env, start_response) class v3FakeApp(FakeApp): """This represents a v3 WSGI app protected by the auth_token middleware.""" def __init__(self, expected_env=None): # with v3 additions, these are for the DEFAULT TOKEN v3_default_env_additions = { 'HTTP_X_PROJECT_ID': 'tenant_id1', 'HTTP_X_PROJECT_NAME': 'tenant_name1', 'HTTP_X_PROJECT_DOMAIN_ID': 'domain_id1', 'HTTP_X_PROJECT_DOMAIN_NAME': 'domain_name1', 'HTTP_X_USER_DOMAIN_ID': 'domain_id1', 'HTTP_X_USER_DOMAIN_NAME': 'domain_name1' } if expected_env: v3_default_env_additions.update(expected_env) super(v3FakeApp, self).__init__(v3_default_env_additions) class BaseAuthTokenMiddlewareTest(testtools.TestCase): """Base test class for auth_token middleware. All the tests allow for running with auth_token configured for receiving v2 or v3 tokens, with the choice being made by passing configuration data into Setup(). The base class will, by default, run all the tests expecting v2 token formats. Child classes can override this to specify, for instance, v3 format. """ def setUp(self, expected_env=None, auth_version=None, fake_app=None): testtools.TestCase.setUp(self) self.expected_env = expected_env or dict() self.fake_app = fake_app or FakeApp self.middleware = None self.conf = { 'auth_host': 'keystone.example.com', 'auth_port': 1234, 'auth_protocol': 'https', 'auth_admin_prefix': '/testadmin', 'signing_dir': client_fixtures.CERTDIR, 'auth_version': auth_version, 'auth_uri': 'https://keystone.example.com:1234', } self.auth_version = auth_version self.response_status = None self.response_headers = None def set_middleware(self, fake_app=None, expected_env=None, conf=None): """Configure the class ready to call the auth_token middleware. Set up the various fake items needed to run the middleware. Individual tests that need to further refine these can call this function to override the class defaults. """ if conf: self.conf.update(conf) if not fake_app: fake_app = self.fake_app if expected_env: self.expected_env.update(expected_env) self.middleware = auth_token.AuthProtocol(fake_app(self.expected_env), self.conf) self.middleware._iso8601 = iso8601 with tempfile.NamedTemporaryFile(dir=self.middleware.signing_dirname, delete=False) as f: pass self.middleware.revoked_file_name = f.name self.addCleanup(cleanup_revoked_file, self.middleware.revoked_file_name) self.middleware.token_revocation_list = jsonutils.dumps( {"revoked": [], "extra": "success"}) def start_fake_response(self, status, headers): self.response_status = int(status.split(' ', 1)[0]) self.response_headers = dict(headers) def assertLastPath(self, path): if path: self.assertEqual(path, httpretty.last_request().path) else: self.assertIsInstance(httpretty.last_request(), httpretty.core.HTTPrettyRequestEmpty) if tuple(sys.version_info)[0:2] < (2, 7): # 2.6 doesn't have the assert dict equals so make sure that it exists class AdjustedBaseAuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest): def assertIsInstance(self, obj, cls, msg=None): """Same as self.assertTrue(isinstance(obj, cls)), with a nicer default message. """ if not isinstance(obj, cls): standardMsg = '%s is not an instance of %r' % (obj, cls) self.fail(self._formatMessage(msg, standardMsg)) def assertDictEqual(self, d1, d2, msg=None): # Simple version taken from 2.7 self.assertIsInstance(d1, dict, 'First argument is not a dictionary') self.assertIsInstance(d2, dict, 'Second argument is not a dictionary') if d1 != d2: if msg: self.fail(msg) else: standardMsg = '%r != %r' % (d1, d2) self.fail(standardMsg) BaseAuthTokenMiddlewareTest = AdjustedBaseAuthTokenMiddlewareTest class MultiStepAuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest, testresources.ResourcedTestCase): resources = [('examples', client_fixtures.EXAMPLES_RESOURCE)] @httpretty.activate def test_fetch_revocation_list_with_expire(self): self.set_middleware() # Get a token, then try to retrieve revocation list and get a 401. # Get a new token, try to retrieve revocation list and return 200. httpretty.register_uri(httpretty.POST, "%s/v2.0/tokens" % BASE_URI, body=FAKE_ADMIN_TOKEN) responses = [httpretty.Response(body='', status=401), httpretty.Response( body=self.examples.SIGNED_REVOCATION_LIST)] httpretty.register_uri(httpretty.GET, "%s/v2.0/tokens/revoked" % BASE_URI, responses=responses) fetched_list = jsonutils.loads(self.middleware.fetch_revocation_list()) self.assertEqual(fetched_list, self.examples.REVOCATION_LIST) # Check that 4 requests have been made self.assertEqual(len(httpretty.httpretty.latest_requests), 4) class DiabloAuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest, testresources.ResourcedTestCase): resources = [('examples', client_fixtures.EXAMPLES_RESOURCE)] """Auth Token middleware should understand Diablo keystone responses.""" def setUp(self): # pre-diablo only had Tenant ID, which was also the Name expected_env = { 'HTTP_X_TENANT_ID': 'tenant_id1', 'HTTP_X_TENANT_NAME': 'tenant_id1', # now deprecated (diablo-compat) 'HTTP_X_TENANT': 'tenant_id1', } super(DiabloAuthTokenMiddlewareTest, self).setUp( expected_env=expected_env) httpretty.reset() httpretty.enable() self.addCleanup(httpretty.disable) httpretty.register_uri(httpretty.GET, "%s/" % BASE_URI, body=VERSION_LIST_v2, status=300) httpretty.register_uri(httpretty.POST, "%s/v2.0/tokens" % BASE_URI, body=FAKE_ADMIN_TOKEN) self.token_id = self.examples.VALID_DIABLO_TOKEN token_response = self.examples.JSON_TOKEN_RESPONSES[self.token_id] httpretty.register_uri(httpretty.GET, "%s/v2.0/tokens/%s" % (BASE_URI, self.token_id), body=token_response) self.set_middleware() def test_valid_diablo_response(self): req = webob.Request.blank('/') req.headers['X-Auth-Token'] = self.token_id self.middleware(req.environ, self.start_fake_response) self.assertEqual(self.response_status, 200) self.assertIn('keystone.token_info', req.environ) class NoMemcacheAuthToken(BaseAuthTokenMiddlewareTest): def setUp(self): super(NoMemcacheAuthToken, self).setUp() self.useFixture(DisableModuleFixture('memcache')) def test_nomemcache(self): conf = { 'admin_token': 'admin_token1', 'auth_host': 'keystone.example.com', 'auth_port': 1234, 'memcached_servers': MEMCACHED_SERVERS, 'auth_uri': 'https://keystone.example.com:1234', } auth_token.AuthProtocol(FakeApp(), conf) def test_not_use_cache_from_env(self): env = {'swift.cache': 'CACHE_TEST'} conf = { 'memcached_servers': MEMCACHED_SERVERS } self.set_middleware(conf=conf) self.middleware._init_cache(env) with self.middleware._cache_pool.reserve() as cache: self.assertNotEqual(cache, 'CACHE_TEST') def test_multiple_context_managers_share_single_client(self): env = {} conf = { 'memcached_servers': MEMCACHED_SERVERS } self.set_middleware(conf=conf) self.middleware._init_cache(env) caches = [] with self.middleware._cache_pool.reserve() as cache: caches.append(cache) with self.middleware._cache_pool.reserve() as cache: caches.append(cache) self.assertIs(caches[0], caches[1]) self.assertEqual(set(caches), set(self.middleware._cache_pool)) def test_nested_context_managers_create_multiple_clients(self): env = {} conf = { 'memcached_servers': MEMCACHED_SERVERS } self.set_middleware(conf=conf) self.middleware._init_cache(env) with self.middleware._cache_pool.reserve() as outer_cache: with self.middleware._cache_pool.reserve() as inner_cache: self.assertNotEqual(outer_cache, inner_cache) self.assertEqual( set([inner_cache, outer_cache]), set(self.middleware._cache_pool)) class CommonAuthTokenMiddlewareTest(object): def test_init_does_not_call_http(self): conf = { 'revocation_cache_time': 1 } self.set_middleware(conf=conf) self.assertLastPath(None) def test_init_by_ipv6Addr_auth_host(self): conf = { 'auth_host': '2001:2013:1:f101::1', 'auth_port': 1234, 'auth_protocol': 'http', 'auth_uri': None, } self.set_middleware(conf=conf) expected_auth_uri = 'http://[2001:2013:1:f101::1]:1234' self.assertEqual(expected_auth_uri, self.middleware.auth_uri) def assert_valid_request_200(self, token, with_catalog=True): req = webob.Request.blank('/') req.headers['X-Auth-Token'] = token body = self.middleware(req.environ, self.start_fake_response) self.assertEqual(self.response_status, 200) if with_catalog: self.assertTrue(req.headers.get('X-Service-Catalog')) else: self.assertNotIn('X-Service-Catalog', req.headers) self.assertEqual(body, [FakeApp.SUCCESS]) self.assertIn('keystone.token_info', req.environ) def test_valid_uuid_request(self): self.assert_valid_request_200(self.token_dict['uuid_token_default']) self.assert_valid_last_url(self.token_dict['uuid_token_default']) def test_valid_signed_request(self): self.assert_valid_request_200( self.token_dict['signed_token_scoped']) self.assertEqual(self.middleware.conf['auth_admin_prefix'], "/testadmin") #ensure that signed requests do not generate HTTP traffic self.assertLastPath(None) def test_revoked_token_receives_401(self): self.middleware.token_revocation_list = self.get_revocation_list_json() req = webob.Request.blank('/') req.headers['X-Auth-Token'] = self.token_dict['revoked_token'] self.middleware(req.environ, self.start_fake_response) self.assertEqual(self.response_status, 401) def get_revocation_list_json(self, token_ids=None): if token_ids is None: token_ids = [self.token_dict['revoked_token_hash']] revocation_list = {'revoked': [{'id': x, 'expires': timeutils.utcnow()} for x in token_ids]} return jsonutils.dumps(revocation_list) def test_is_signed_token_revoked_returns_false(self): #explicitly setting an empty revocation list here to document intent self.middleware.token_revocation_list = jsonutils.dumps( {"revoked": [], "extra": "success"}) result = self.middleware.is_signed_token_revoked( self.token_dict['revoked_token']) self.assertFalse(result) def test_is_signed_token_revoked_returns_true(self): self.middleware.token_revocation_list = self.get_revocation_list_json() result = self.middleware.is_signed_token_revoked( self.token_dict['revoked_token']) self.assertTrue(result) def test_verify_signed_token_raises_exception_for_revoked_token(self): self.middleware.token_revocation_list = self.get_revocation_list_json() self.assertRaises(auth_token.InvalidUserToken, self.middleware.verify_signed_token, self.token_dict['revoked_token']) def test_verify_signed_token_succeeds_for_unrevoked_token(self): self.middleware.token_revocation_list = self.get_revocation_list_json() self.middleware.verify_signed_token( self.token_dict['signed_token_scoped']) def test_verify_signing_dir_create_while_missing(self): tmp_name = uuid.uuid4().hex test_parent_signing_dir = "/tmp/%s" % tmp_name self.middleware.signing_dirname = "/tmp/%s/%s" % ((tmp_name,) * 2) self.middleware.signing_cert_file_name = "%s/test.pem" % \ self.middleware.signing_dirname self.middleware.verify_signing_dir() # NOTE(wu_wenxiang): Verify if the signing dir was created as expected. self.assertTrue(os.path.isdir(self.middleware.signing_dirname)) self.assertTrue(os.access(self.middleware.signing_dirname, os.W_OK)) self.assertEqual(os.stat(self.middleware.signing_dirname).st_uid, os.getuid()) self.assertEqual( stat.S_IMODE(os.stat(self.middleware.signing_dirname).st_mode), stat.S_IRWXU) shutil.rmtree(test_parent_signing_dir) def test_cert_file_missing(self): self.assertFalse(self.middleware.cert_file_missing( "openstack: /tmp/haystack: No such file or directory", "/tmp/needle")) self.assertTrue(self.middleware.cert_file_missing( "openstack: /not/exist: No such file or directory", "/not/exist")) def test_get_token_revocation_list_fetched_time_returns_min(self): self.middleware.token_revocation_list_fetched_time = None self.middleware.revoked_file_name = '' self.assertEqual(self.middleware.token_revocation_list_fetched_time, datetime.datetime.min) def test_get_token_revocation_list_fetched_time_returns_mtime(self): self.middleware.token_revocation_list_fetched_time = None mtime = os.path.getmtime(self.middleware.revoked_file_name) fetched_time = datetime.datetime.utcfromtimestamp(mtime) self.assertEqual(fetched_time, self.middleware.token_revocation_list_fetched_time) @testtools.skipUnless(TimezoneFixture.supported(), 'TimezoneFixture not supported') def test_get_token_revocation_list_fetched_time_returns_utc(self): with TimezoneFixture('UTC-1'): self.middleware.token_revocation_list = jsonutils.dumps( self.examples.REVOCATION_LIST) self.middleware.token_revocation_list_fetched_time = None fetched_time = self.middleware.token_revocation_list_fetched_time self.assertTrue(timeutils.is_soon(fetched_time, 1)) def test_get_token_revocation_list_fetched_time_returns_value(self): expected = self.middleware._token_revocation_list_fetched_time self.assertEqual(self.middleware.token_revocation_list_fetched_time, expected) def test_get_revocation_list_returns_fetched_list(self): # auth_token uses v2 to fetch this, so don't allow the v3 # tests to override the fake http connection self.middleware.token_revocation_list_fetched_time = None os.remove(self.middleware.revoked_file_name) self.assertEqual(self.middleware.token_revocation_list, self.examples.REVOCATION_LIST) def test_get_revocation_list_returns_current_list_from_memory(self): self.assertEqual(self.middleware.token_revocation_list, self.middleware._token_revocation_list) def test_get_revocation_list_returns_current_list_from_disk(self): in_memory_list = self.middleware.token_revocation_list self.middleware._token_revocation_list = None self.assertEqual(self.middleware.token_revocation_list, in_memory_list) def test_invalid_revocation_list_raises_service_error(self): httpretty.register_uri(httpretty.GET, "%s/v2.0/tokens/revoked" % BASE_URI, body="{}", status=200) self.assertRaises(auth_token.ServiceError, self.middleware.fetch_revocation_list) def test_fetch_revocation_list(self): # auth_token uses v2 to fetch this, so don't allow the v3 # tests to override the fake http connection fetched_list = jsonutils.loads(self.middleware.fetch_revocation_list()) self.assertEqual(fetched_list, self.examples.REVOCATION_LIST) def test_request_invalid_uuid_token(self): # remember because we are testing the middleware we stub the connection # to the keystone server, but this is not what gets returned invalid_uri = "%s/v2.0/tokens/invalid-token" % BASE_URI httpretty.register_uri(httpretty.GET, invalid_uri, body="", status=404) req = webob.Request.blank('/') req.headers['X-Auth-Token'] = 'invalid-token' self.middleware(req.environ, self.start_fake_response) self.assertEqual(self.response_status, 401) self.assertEqual(self.response_headers['WWW-Authenticate'], "Keystone uri='https://keystone.example.com:1234'") def test_request_invalid_signed_token(self): req = webob.Request.blank('/') req.headers['X-Auth-Token'] = self.examples.INVALID_SIGNED_TOKEN self.middleware(req.environ, self.start_fake_response) self.assertEqual(self.response_status, 401) self.assertEqual(self.response_headers['WWW-Authenticate'], "Keystone uri='https://keystone.example.com:1234'") def test_request_no_token(self): req = webob.Request.blank('/') self.middleware(req.environ, self.start_fake_response) self.assertEqual(self.response_status, 401) self.assertEqual(self.response_headers['WWW-Authenticate'], "Keystone uri='https://keystone.example.com:1234'") def test_request_no_token_log_message(self): class FakeLog(object): def __init__(self): self.msg = None self.debugmsg = None def warn(self, msg=None, *args, **kwargs): self.msg = msg def debug(self, msg=None, *args, **kwargs): self.debugmsg = msg self.middleware.LOG = FakeLog() self.middleware.delay_auth_decision = False self.assertRaises(auth_token.InvalidUserToken, self.middleware._get_user_token_from_header, {}) self.assertIsNotNone(self.middleware.LOG.msg) self.assertIsNotNone(self.middleware.LOG.debugmsg) def test_request_no_token_http(self): req = webob.Request.blank('/', environ={'REQUEST_METHOD': 'HEAD'}) self.set_middleware() body = self.middleware(req.environ, self.start_fake_response) self.assertEqual(self.response_status, 401) self.assertEqual(self.response_headers['WWW-Authenticate'], "Keystone uri='https://keystone.example.com:1234'") self.assertEqual(body, ['']) def test_request_blank_token(self): req = webob.Request.blank('/') req.headers['X-Auth-Token'] = '' self.middleware(req.environ, self.start_fake_response) self.assertEqual(self.response_status, 401) self.assertEqual(self.response_headers['WWW-Authenticate'], "Keystone uri='https://keystone.example.com:1234'") def _get_cached_token(self, token): token_id = cms.cms_hash_token(token) # NOTE(vish): example tokens are expired so skip the expiration check. return self.middleware._cache_get(token_id, ignore_expires=True) def test_memcache(self): # NOTE(jamielennox): it appears that httpretty can mess with the # memcache socket. Just disable it as it's not required here anyway. httpretty.disable() req = webob.Request.blank('/') token = self.token_dict['signed_token_scoped'] req.headers['X-Auth-Token'] = token self.middleware(req.environ, self.start_fake_response) self.assertIsNotNone(self._get_cached_token(token)) def test_expired(self): httpretty.disable() req = webob.Request.blank('/') token = self.token_dict['signed_token_scoped_expired'] req.headers['X-Auth-Token'] = token self.middleware(req.environ, self.start_fake_response) self.assertEqual(self.response_status, 401) def test_memcache_set_invalid_uuid(self): invalid_uri = "%s/v2.0/tokens/invalid-token" % BASE_URI httpretty.register_uri(httpretty.GET, invalid_uri, body="", status=404) req = webob.Request.blank('/') token = 'invalid-token' req.headers['X-Auth-Token'] = token self.middleware(req.environ, self.start_fake_response) self.assertRaises(auth_token.InvalidUserToken, self._get_cached_token, token) def test_memcache_set_invalid_signed(self): req = webob.Request.blank('/') token = self.token_dict['signed_token_scoped_expired'] req.headers['X-Auth-Token'] = token self.middleware(req.environ, self.start_fake_response) self.assertRaises(auth_token.InvalidUserToken, self._get_cached_token, token) def test_memcache_set_expired(self, extra_conf={}, extra_environ={}): httpretty.disable() token_cache_time = 10 conf = { 'token_cache_time': token_cache_time, 'signing_dir': client_fixtures.CERTDIR, } conf.update(extra_conf) self.set_middleware(conf=conf) req = webob.Request.blank('/') token = self.token_dict['signed_token_scoped'] req.headers['X-Auth-Token'] = token req.environ.update(extra_environ) timeutils_utcnow = 'keystoneclient.openstack.common.timeutils.utcnow' now = datetime.datetime.utcnow() with mock.patch(timeutils_utcnow) as mock_utcnow: mock_utcnow.return_value = now self.middleware(req.environ, self.start_fake_response) self.assertIsNotNone(self._get_cached_token(token)) expired = now + datetime.timedelta(seconds=token_cache_time) with mock.patch(timeutils_utcnow) as mock_utcnow: mock_utcnow.return_value = expired self.assertIsNone(self._get_cached_token(token)) def test_swift_memcache_set_expired(self): extra_conf = {'cache': 'swift.cache'} extra_environ = {'swift.cache': memorycache.Client()} self.test_memcache_set_expired(extra_conf, extra_environ) def test_use_cache_from_env(self): env = {'swift.cache': 'CACHE_TEST'} conf = { 'cache': 'swift.cache', 'memcached_servers': MEMCACHED_SERVERS } self.set_middleware(conf=conf) self.middleware._init_cache(env) with self.middleware._cache_pool.reserve() as cache: self.assertEqual(cache, 'CACHE_TEST') def test_will_expire_soon(self): tenseconds = datetime.datetime.utcnow() + datetime.timedelta( seconds=10) self.assertTrue(auth_token.will_expire_soon(tenseconds)) fortyseconds = datetime.datetime.utcnow() + datetime.timedelta( seconds=40) self.assertFalse(auth_token.will_expire_soon(fortyseconds)) def test_token_is_v2_accepts_v2(self): token = self.examples.UUID_TOKEN_DEFAULT token_response = self.examples.TOKEN_RESPONSES[token] self.assertTrue(auth_token._token_is_v2(token_response)) def test_token_is_v2_rejects_v3(self): token = self.examples.v3_UUID_TOKEN_DEFAULT token_response = self.examples.TOKEN_RESPONSES[token] self.assertFalse(auth_token._token_is_v2(token_response)) def test_token_is_v3_rejects_v2(self): token = self.examples.UUID_TOKEN_DEFAULT token_response = self.examples.TOKEN_RESPONSES[token] self.assertFalse(auth_token._token_is_v3(token_response)) def test_token_is_v3_accepts_v3(self): token = self.examples.v3_UUID_TOKEN_DEFAULT token_response = self.examples.TOKEN_RESPONSES[token] self.assertTrue(auth_token._token_is_v3(token_response)) @testtools.skipUnless(memcached_available(), 'memcached not available') def test_encrypt_cache_data(self): httpretty.disable() conf = { 'memcached_servers': MEMCACHED_SERVERS, 'memcache_security_strategy': 'encrypt', 'memcache_secret_key': 'mysecret' } self.set_middleware(conf=conf) token = b'my_token' some_time_later = timeutils.utcnow() + datetime.timedelta(hours=4) expires = timeutils.strtime(some_time_later) data = ('this_data', expires) self.middleware._init_cache({}) self.middleware._cache_store(token, data) self.assertEqual(self.middleware._cache_get(token), data[0]) @testtools.skipUnless(memcached_available(), 'memcached not available') def test_sign_cache_data(self): httpretty.disable() conf = { 'memcached_servers': MEMCACHED_SERVERS, 'memcache_security_strategy': 'mac', 'memcache_secret_key': 'mysecret' } self.set_middleware(conf=conf) token = b'my_token' some_time_later = timeutils.utcnow() + datetime.timedelta(hours=4) expires = timeutils.strtime(some_time_later) data = ('this_data', expires) self.middleware._init_cache({}) self.middleware._cache_store(token, data) self.assertEqual(self.middleware._cache_get(token), data[0]) @testtools.skipUnless(memcached_available(), 'memcached not available') def test_no_memcache_protection(self): httpretty.disable() conf = { 'memcached_servers': MEMCACHED_SERVERS, 'memcache_secret_key': 'mysecret' } self.set_middleware(conf=conf) token = 'my_token' some_time_later = timeutils.utcnow() + datetime.timedelta(hours=4) expires = timeutils.strtime(some_time_later) data = ('this_data', expires) self.middleware._init_cache({}) self.middleware._cache_store(token, data) self.assertEqual(self.middleware._cache_get(token), data[0]) def test_assert_valid_memcache_protection_config(self): # test missing memcache_secret_key conf = { 'memcached_servers': MEMCACHED_SERVERS, 'memcache_security_strategy': 'Encrypt' } self.assertRaises(auth_token.ConfigurationError, self.set_middleware, conf=conf) # test invalue memcache_security_strategy conf = { 'memcached_servers': MEMCACHED_SERVERS, 'memcache_security_strategy': 'whatever' } self.assertRaises(auth_token.ConfigurationError, self.set_middleware, conf=conf) # test missing memcache_secret_key conf = { 'memcached_servers': MEMCACHED_SERVERS, 'memcache_security_strategy': 'mac' } self.assertRaises(auth_token.ConfigurationError, self.set_middleware, conf=conf) conf = { 'memcached_servers': MEMCACHED_SERVERS, 'memcache_security_strategy': 'Encrypt', 'memcache_secret_key': '' } self.assertRaises(auth_token.ConfigurationError, self.set_middleware, conf=conf) conf = { 'memcached_servers': MEMCACHED_SERVERS, 'memcache_security_strategy': 'mAc', 'memcache_secret_key': '' } self.assertRaises(auth_token.ConfigurationError, self.set_middleware, conf=conf) def test_config_revocation_cache_timeout(self): conf = { 'revocation_cache_time': 24, 'auth_uri': 'https://keystone.example.com:1234', } middleware = auth_token.AuthProtocol(self.fake_app, conf) self.assertEqual(middleware.token_revocation_list_cache_timeout, datetime.timedelta(seconds=24)) def test_http_error_not_cached_token(self): """Test to don't cache token as invalid on network errors. We use UUID tokens since they are the easiest one to reach get_http_connection. """ req = webob.Request.blank('/') req.headers['X-Auth-Token'] = ERROR_TOKEN self.middleware.http_request_max_retries = 0 self.middleware(req.environ, self.start_fake_response) self.assertIsNone(self._get_cached_token(ERROR_TOKEN)) self.assert_valid_last_url(ERROR_TOKEN) def test_http_request_max_retries(self): times_retry = 10 req = webob.Request.blank('/') req.headers['X-Auth-Token'] = ERROR_TOKEN conf = {'http_request_max_retries': times_retry} self.set_middleware(conf=conf) with mock.patch('time.sleep') as mock_obj: self.middleware(req.environ, self.start_fake_response) self.assertEqual(mock_obj.call_count, times_retry) def test_nocatalog(self): conf = { 'include_service_catalog': False } self.set_middleware(conf=conf) self.assert_valid_request_200(self.token_dict['uuid_token_default'], with_catalog=False) def assert_kerberos_bind(self, token, bind_level, use_kerberos=True, success=True): conf = { 'enforce_token_bind': bind_level, 'auth_version': self.auth_version, } self.set_middleware(conf=conf) req = webob.Request.blank('/') req.headers['X-Auth-Token'] = token if use_kerberos: if use_kerberos is True: req.environ['REMOTE_USER'] = self.examples.KERBEROS_BIND else: req.environ['REMOTE_USER'] = use_kerberos req.environ['AUTH_TYPE'] = 'Negotiate' body = self.middleware(req.environ, self.start_fake_response) if success: self.assertEqual(self.response_status, 200) self.assertEqual(body, [FakeApp.SUCCESS]) self.assertIn('keystone.token_info', req.environ) self.assert_valid_last_url(token) else: self.assertEqual(self.response_status, 401) self.assertEqual(self.response_headers['WWW-Authenticate'], "Keystone uri='https://keystone.example.com:1234'" ) def test_uuid_bind_token_disabled_with_kerb_user(self): for use_kerberos in [True, False]: self.assert_kerberos_bind(self.token_dict['uuid_token_bind'], bind_level='disabled', use_kerberos=use_kerberos, success=True) def test_uuid_bind_token_disabled_with_incorrect_ticket(self): self.assert_kerberos_bind(self.token_dict['uuid_token_bind'], bind_level='kerberos', use_kerberos='ronald@MCDONALDS.COM', success=False) def test_uuid_bind_token_permissive_with_kerb_user(self): self.assert_kerberos_bind(self.token_dict['uuid_token_bind'], bind_level='permissive', use_kerberos=True, success=True) def test_uuid_bind_token_permissive_without_kerb_user(self): self.assert_kerberos_bind(self.token_dict['uuid_token_bind'], bind_level='permissive', use_kerberos=False, success=False) def test_uuid_bind_token_permissive_with_unknown_bind(self): token = self.token_dict['uuid_token_unknown_bind'] for use_kerberos in [True, False]: self.assert_kerberos_bind(token, bind_level='permissive', use_kerberos=use_kerberos, success=True) def test_uuid_bind_token_permissive_with_incorrect_ticket(self): self.assert_kerberos_bind(self.token_dict['uuid_token_bind'], bind_level='kerberos', use_kerberos='ronald@MCDONALDS.COM', success=False) def test_uuid_bind_token_strict_with_kerb_user(self): self.assert_kerberos_bind(self.token_dict['uuid_token_bind'], bind_level='strict', use_kerberos=True, success=True) def test_uuid_bind_token_strict_with_kerbout_user(self): self.assert_kerberos_bind(self.token_dict['uuid_token_bind'], bind_level='strict', use_kerberos=False, success=False) def test_uuid_bind_token_strict_with_unknown_bind(self): token = self.token_dict['uuid_token_unknown_bind'] for use_kerberos in [True, False]: self.assert_kerberos_bind(token, bind_level='strict', use_kerberos=use_kerberos, success=False) def test_uuid_bind_token_required_with_kerb_user(self): self.assert_kerberos_bind(self.token_dict['uuid_token_bind'], bind_level='required', use_kerberos=True, success=True) def test_uuid_bind_token_required_without_kerb_user(self): self.assert_kerberos_bind(self.token_dict['uuid_token_bind'], bind_level='required', use_kerberos=False, success=False) def test_uuid_bind_token_required_with_unknown_bind(self): token = self.token_dict['uuid_token_unknown_bind'] for use_kerberos in [True, False]: self.assert_kerberos_bind(token, bind_level='required', use_kerberos=use_kerberos, success=False) def test_uuid_bind_token_required_without_bind(self): for use_kerberos in [True, False]: self.assert_kerberos_bind(self.token_dict['uuid_token_default'], bind_level='required', use_kerberos=use_kerberos, success=False) def test_uuid_bind_token_named_kerberos_with_kerb_user(self): self.assert_kerberos_bind(self.token_dict['uuid_token_bind'], bind_level='kerberos', use_kerberos=True, success=True) def test_uuid_bind_token_named_kerberos_without_kerb_user(self): self.assert_kerberos_bind(self.token_dict['uuid_token_bind'], bind_level='kerberos', use_kerberos=False, success=False) def test_uuid_bind_token_named_kerberos_with_unknown_bind(self): token = self.token_dict['uuid_token_unknown_bind'] for use_kerberos in [True, False]: self.assert_kerberos_bind(token, bind_level='kerberos', use_kerberos=use_kerberos, success=False) def test_uuid_bind_token_named_kerberos_without_bind(self): for use_kerberos in [True, False]: self.assert_kerberos_bind(self.token_dict['uuid_token_default'], bind_level='kerberos', use_kerberos=use_kerberos, success=False) def test_uuid_bind_token_named_kerberos_with_incorrect_ticket(self): self.assert_kerberos_bind(self.token_dict['uuid_token_bind'], bind_level='kerberos', use_kerberos='ronald@MCDONALDS.COM', success=False) def test_uuid_bind_token_with_unknown_named_FOO(self): token = self.token_dict['uuid_token_bind'] for use_kerberos in [True, False]: self.assert_kerberos_bind(token, bind_level='FOO', use_kerberos=use_kerberos, success=False) class CertDownloadMiddlewareTest(BaseAuthTokenMiddlewareTest, testresources.ResourcedTestCase): resources = [('examples', client_fixtures.EXAMPLES_RESOURCE)] def setUp(self): super(CertDownloadMiddlewareTest, self).setUp() self.base_dir = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, self.base_dir) self.cert_dir = os.path.join(self.base_dir, 'certs') os.makedirs(self.cert_dir, stat.S_IRWXU) conf = { 'signing_dir': self.cert_dir, } self.set_middleware(conf=conf) httpretty.enable() self.addCleanup(httpretty.disable) # Usually we supply a signed_dir with pre-installed certificates, # so invocation of /usr/bin/openssl succeeds. This time we give it # an empty directory, so it fails. def test_request_no_token_dummy(self): cms._ensure_subprocess() httpretty.register_uri(httpretty.GET, "%s/v2.0/certificates/ca" % BASE_URI, status=404) httpretty.register_uri(httpretty.GET, "%s/v2.0/certificates/signing" % BASE_URI, status=404) self.assertRaises(exceptions.CertificateConfigError, self.middleware.verify_signed_token, self.examples.SIGNED_TOKEN_SCOPED) def test_fetch_signing_cert(self): data = 'FAKE CERT' httpretty.register_uri(httpretty.GET, "%s/v2.0/certificates/signing" % BASE_URI, body=data) self.middleware.fetch_signing_cert() with open(self.middleware.signing_cert_file_name, 'r') as f: self.assertEqual(f.read(), data) self.assertEqual("/testadmin/v2.0/certificates/signing", httpretty.last_request().path) def test_fetch_signing_ca(self): data = 'FAKE CA' httpretty.register_uri(httpretty.GET, "%s/v2.0/certificates/ca" % BASE_URI, body=data) self.middleware.fetch_ca_cert() with open(self.middleware.signing_ca_file_name, 'r') as f: self.assertEqual(f.read(), data) self.assertEqual("/testadmin/v2.0/certificates/ca", httpretty.last_request().path) def test_prefix_trailing_slash(self): self.conf['auth_admin_prefix'] = '/newadmin/' httpretty.register_uri(httpretty.GET, "%s/newadmin/v2.0/certificates/ca" % BASE_HOST, body='FAKECA') httpretty.register_uri(httpretty.GET, "%s/newadmin/v2.0/certificates/signing" % BASE_HOST, body='FAKECERT') self.set_middleware(conf=self.conf) self.middleware.fetch_ca_cert() self.assertEqual('/newadmin/v2.0/certificates/ca', httpretty.last_request().path) self.middleware.fetch_signing_cert() self.assertEqual('/newadmin/v2.0/certificates/signing', httpretty.last_request().path) def test_without_prefix(self): self.conf['auth_admin_prefix'] = '' httpretty.register_uri(httpretty.GET, "%s/v2.0/certificates/ca" % BASE_HOST, body='FAKECA') httpretty.register_uri(httpretty.GET, "%s/v2.0/certificates/signing" % BASE_HOST, body='FAKECERT') self.set_middleware(conf=self.conf) self.middleware.fetch_ca_cert() self.assertEqual('/v2.0/certificates/ca', httpretty.last_request().path) self.middleware.fetch_signing_cert() self.assertEqual('/v2.0/certificates/signing', httpretty.last_request().path) def network_error_response(method, uri, headers): raise auth_token.NetworkError("Network connection error.") class v2AuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest, CommonAuthTokenMiddlewareTest, testresources.ResourcedTestCase): """v2 token specific tests. There are some differences between how the auth-token middleware handles v2 and v3 tokens over and above the token formats, namely: - A v3 keystone server will auto scope a token to a user's default project if no scope is specified. A v2 server assumes that the auth-token middleware will do that. - A v2 keystone server may issue a token without a catalog, even with a tenant The tests below were originally part of the generic AuthTokenMiddlewareTest class, but now, since they really are v2 specifc, they are included here. """ resources = [('examples', client_fixtures.EXAMPLES_RESOURCE)] def setUp(self): super(v2AuthTokenMiddlewareTest, self).setUp() self.token_dict = { 'uuid_token_default': self.examples.UUID_TOKEN_DEFAULT, 'uuid_token_unscoped': self.examples.UUID_TOKEN_UNSCOPED, 'uuid_token_bind': self.examples.UUID_TOKEN_BIND, 'uuid_token_unknown_bind': self.examples.UUID_TOKEN_UNKNOWN_BIND, 'signed_token_scoped': self.examples.SIGNED_TOKEN_SCOPED, 'signed_token_scoped_expired': self.examples.SIGNED_TOKEN_SCOPED_EXPIRED, 'revoked_token': self.examples.REVOKED_TOKEN, 'revoked_token_hash': self.examples.REVOKED_TOKEN_HASH } httpretty.reset() httpretty.enable() self.addCleanup(httpretty.disable) httpretty.register_uri(httpretty.GET, "%s/" % BASE_URI, body=VERSION_LIST_v2, status=300) httpretty.register_uri(httpretty.POST, "%s/v2.0/tokens" % BASE_URI, body=FAKE_ADMIN_TOKEN) httpretty.register_uri(httpretty.GET, "%s/v2.0/tokens/revoked" % BASE_URI, body=self.examples.SIGNED_REVOCATION_LIST, status=200) for token in (self.examples.UUID_TOKEN_DEFAULT, self.examples.UUID_TOKEN_UNSCOPED, self.examples.UUID_TOKEN_BIND, self.examples.UUID_TOKEN_UNKNOWN_BIND, self.examples.UUID_TOKEN_NO_SERVICE_CATALOG): httpretty.register_uri(httpretty.GET, "%s/v2.0/tokens/%s" % (BASE_URI, token), body= self.examples.JSON_TOKEN_RESPONSES[token]) httpretty.register_uri(httpretty.GET, '%s/v2.0/tokens/%s' % (BASE_URI, ERROR_TOKEN), body=network_error_response) self.set_middleware() def assert_unscoped_default_tenant_auto_scopes(self, token): """Unscoped v2 requests with a default tenant should "auto-scope." The implied scope is the user's tenant ID. """ req = webob.Request.blank('/') req.headers['X-Auth-Token'] = token body = self.middleware(req.environ, self.start_fake_response) self.assertEqual(self.response_status, 200) self.assertEqual(body, [FakeApp.SUCCESS]) self.assertIn('keystone.token_info', req.environ) def assert_valid_last_url(self, token_id): self.assertLastPath("/testadmin/v2.0/tokens/%s" % token_id) def test_default_tenant_uuid_token(self): self.assert_unscoped_default_tenant_auto_scopes( self.examples.UUID_TOKEN_DEFAULT) def test_default_tenant_signed_token(self): self.assert_unscoped_default_tenant_auto_scopes( self.examples.SIGNED_TOKEN_SCOPED) def assert_unscoped_token_receives_401(self, token): """Unscoped requests with no default tenant ID should be rejected.""" req = webob.Request.blank('/') req.headers['X-Auth-Token'] = token self.middleware(req.environ, self.start_fake_response) self.assertEqual(self.response_status, 401) self.assertEqual(self.response_headers['WWW-Authenticate'], "Keystone uri='https://keystone.example.com:1234'") def test_unscoped_uuid_token_receives_401(self): self.assert_unscoped_token_receives_401( self.examples.UUID_TOKEN_UNSCOPED) def test_unscoped_pki_token_receives_401(self): self.assert_unscoped_token_receives_401( self.examples.SIGNED_TOKEN_UNSCOPED) def test_request_prevent_service_catalog_injection(self): req = webob.Request.blank('/') req.headers['X-Service-Catalog'] = '[]' req.headers['X-Auth-Token'] = \ self.examples.UUID_TOKEN_NO_SERVICE_CATALOG body = self.middleware(req.environ, self.start_fake_response) self.assertEqual(self.response_status, 200) self.assertFalse(req.headers.get('X-Service-Catalog')) self.assertEqual(body, [FakeApp.SUCCESS]) class CrossVersionAuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest, testresources.ResourcedTestCase): resources = [('examples', client_fixtures.EXAMPLES_RESOURCE)] @httpretty.activate def test_valid_uuid_request_forced_to_2_0(self): """Test forcing auth_token to use lower api version. By installing the v3 http hander, auth_token will be get a version list that looks like a v3 server - from which it would normally chose v3.0 as the auth version. However, here we specify v2.0 in the configuration - which should force auth_token to use that version instead. """ conf = { 'signing_dir': client_fixtures.CERTDIR, 'auth_version': 'v2.0' } httpretty.register_uri(httpretty.GET, "%s/" % BASE_URI, body=VERSION_LIST_v3, status=300) httpretty.register_uri(httpretty.POST, "%s/v2.0/tokens" % BASE_URI, body=FAKE_ADMIN_TOKEN) token = self.examples.UUID_TOKEN_DEFAULT httpretty.register_uri(httpretty.GET, "%s/v2.0/tokens/%s" % (BASE_URI, token), body= self.examples.JSON_TOKEN_RESPONSES[token]) self.set_middleware(conf=conf) # This tests will only work is auth_token has chosen to use the # lower, v2, api version req = webob.Request.blank('/') req.headers['X-Auth-Token'] = self.examples.UUID_TOKEN_DEFAULT self.middleware(req.environ, self.start_fake_response) self.assertEqual(self.response_status, 200) self.assertEqual("/testadmin/v2.0/tokens/%s" % self.examples.UUID_TOKEN_DEFAULT, httpretty.last_request().path) class v3AuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest, CommonAuthTokenMiddlewareTest, testresources.ResourcedTestCase): """Test auth_token middleware with v3 tokens. Re-execute the AuthTokenMiddlewareTest class tests, but with the the auth_token middleware configured to expect v3 tokens back from a keystone server. This is done by configuring the AuthTokenMiddlewareTest class via its Setup(), passing in v3 style data that will then be used by the tests themselves. This approach has been used to ensure we really are running the same tests for both v2 and v3 tokens. There a few additional specific test for v3 only: - We allow an unscoped token to be validated (as unscoped), where as for v2 tokens, the auth_token middleware is expected to try and auto-scope it (and fail if there is no default tenant) - Domain scoped tokens Since we don't specify an auth version for auth_token to use, by definition we are thefore implicitely testing that it will use the highest available auth version, i.e. v3.0 """ resources = [('examples', client_fixtures.EXAMPLES_RESOURCE)] def setUp(self): super(v3AuthTokenMiddlewareTest, self).setUp( auth_version='v3.0', fake_app=v3FakeApp) self.token_dict = { 'uuid_token_default': self.examples.v3_UUID_TOKEN_DEFAULT, 'uuid_token_unscoped': self.examples.v3_UUID_TOKEN_UNSCOPED, 'uuid_token_bind': self.examples.v3_UUID_TOKEN_BIND, 'uuid_token_unknown_bind': self.examples.v3_UUID_TOKEN_UNKNOWN_BIND, 'signed_token_scoped': self.examples.SIGNED_v3_TOKEN_SCOPED, 'signed_token_scoped_expired': self.examples.SIGNED_TOKEN_SCOPED_EXPIRED, 'revoked_token': self.examples.REVOKED_v3_TOKEN, 'revoked_token_hash': self.examples.REVOKED_v3_TOKEN_HASH } httpretty.reset() httpretty.enable() self.addCleanup(httpretty.disable) httpretty.register_uri(httpretty.GET, "%s" % BASE_URI, body=VERSION_LIST_v3, status=300) # TODO(jamielennox): auth_token middleware uses a v2 admin token # regardless of the auth_version that is set. httpretty.register_uri(httpretty.POST, "%s/v2.0/tokens" % BASE_URI, body=FAKE_ADMIN_TOKEN) # TODO(jamielennox): there is no v3 revocation url yet, it uses v2 httpretty.register_uri(httpretty.GET, "%s/v2.0/tokens/revoked" % BASE_URI, body=self.examples.SIGNED_REVOCATION_LIST, status=200) httpretty.register_uri(httpretty.GET, "%s/v3/auth/tokens" % BASE_URI, body=self.token_response) self.set_middleware() def token_response(self, request, uri, headers): auth_id = request.headers.get('X-Auth-Token') token_id = request.headers.get('X-Subject-Token') self.assertEqual(auth_id, FAKE_ADMIN_TOKEN_ID) headers.pop('status') status = 200 response = "" if token_id == ERROR_TOKEN: raise auth_token.NetworkError("Network connection error.") try: response = self.examples.JSON_TOKEN_RESPONSES[token_id] except KeyError: status = 404 return status, headers, response def assert_valid_last_url(self, token_id): self.assertLastPath('/testadmin/v3/auth/tokens') def test_valid_unscoped_uuid_request(self): # Remove items that won't be in an unscoped token delta_expected_env = { 'HTTP_X_PROJECT_ID': None, 'HTTP_X_PROJECT_NAME': None, 'HTTP_X_PROJECT_DOMAIN_ID': None, 'HTTP_X_PROJECT_DOMAIN_NAME': None, 'HTTP_X_TENANT_ID': None, 'HTTP_X_TENANT_NAME': None, 'HTTP_X_ROLES': '', 'HTTP_X_TENANT': None, 'HTTP_X_ROLE': '', } self.set_middleware(expected_env=delta_expected_env) self.assert_valid_request_200(self.examples.v3_UUID_TOKEN_UNSCOPED, with_catalog=False) self.assertLastPath('/testadmin/v3/auth/tokens') def test_domain_scoped_uuid_request(self): # Modify items compared to default token for a domain scope delta_expected_env = { 'HTTP_X_DOMAIN_ID': 'domain_id1', 'HTTP_X_DOMAIN_NAME': 'domain_name1', 'HTTP_X_PROJECT_ID': None, 'HTTP_X_PROJECT_NAME': None, 'HTTP_X_PROJECT_DOMAIN_ID': None, 'HTTP_X_PROJECT_DOMAIN_NAME': None, 'HTTP_X_TENANT_ID': None, 'HTTP_X_TENANT_NAME': None, 'HTTP_X_TENANT': None } self.set_middleware(expected_env=delta_expected_env) self.assert_valid_request_200( self.examples.v3_UUID_TOKEN_DOMAIN_SCOPED) self.assertLastPath('/testadmin/v3/auth/tokens') class TokenEncodingTest(testtools.TestCase): def test_unquoted_token(self): self.assertEqual('foo%20bar', auth_token.safe_quote('foo bar')) def test_quoted_token(self): self.assertEqual('foo%20bar', auth_token.safe_quote('foo%20bar')) class TokenExpirationTest(BaseAuthTokenMiddlewareTest): def setUp(self): super(TokenExpirationTest, self).setUp() self.now = timeutils.utcnow() self.delta = datetime.timedelta(hours=1) self.one_hour_ago = timeutils.isotime(self.now - self.delta, subsecond=True) self.one_hour_earlier = timeutils.isotime(self.now + self.delta, subsecond=True) def create_v2_token_fixture(self, expires=None): v2_fixture = { 'access': { 'token': { 'id': 'blah', 'expires': expires or self.one_hour_earlier, 'tenant': { 'id': 'tenant_id1', 'name': 'tenant_name1', }, }, 'user': { 'id': 'user_id1', 'name': 'user_name1', 'roles': [ {'name': 'role1'}, {'name': 'role2'}, ], }, 'serviceCatalog': {} }, } return v2_fixture def create_v3_token_fixture(self, expires=None): v3_fixture = { 'token': { 'expires_at': expires or self.one_hour_earlier, 'user': { 'id': 'user_id1', 'name': 'user_name1', 'domain': { 'id': 'domain_id1', 'name': 'domain_name1' } }, 'project': { 'id': 'tenant_id1', 'name': 'tenant_name1', 'domain': { 'id': 'domain_id1', 'name': 'domain_name1' } }, 'roles': [ {'name': 'role1', 'id': 'Role1'}, {'name': 'role2', 'id': 'Role2'}, ], 'catalog': {} } } return v3_fixture def test_no_data(self): data = {} self.assertRaises(auth_token.InvalidUserToken, auth_token.confirm_token_not_expired, data) def test_bad_data(self): data = {'my_happy_token_dict': 'woo'} self.assertRaises(auth_token.InvalidUserToken, auth_token.confirm_token_not_expired, data) def test_v2_token_not_expired(self): data = self.create_v2_token_fixture() expected_expires = data['access']['token']['expires'] actual_expires = auth_token.confirm_token_not_expired(data) self.assertEqual(actual_expires, expected_expires) def test_v2_token_expired(self): data = self.create_v2_token_fixture(expires=self.one_hour_ago) self.assertRaises(auth_token.InvalidUserToken, auth_token.confirm_token_not_expired, data) @mock.patch('keystoneclient.openstack.common.timeutils.utcnow') def test_v2_token_with_timezone_offset_not_expired(self, mock_utcnow): current_time = timeutils.parse_isotime('2000-01-01T00:01:10.000123Z') current_time = timeutils.normalize_time(current_time) mock_utcnow.return_value = current_time data = self.create_v2_token_fixture( expires='2000-01-01T00:05:10.000123-05:00') expected_expires = '2000-01-01T05:05:10.000123Z' actual_expires = auth_token.confirm_token_not_expired(data) self.assertEqual(actual_expires, expected_expires) @mock.patch('keystoneclient.openstack.common.timeutils.utcnow') def test_v2_token_with_timezone_offset_expired(self, mock_utcnow): current_time = timeutils.parse_isotime('2000-01-01T00:01:10.000123Z') current_time = timeutils.normalize_time(current_time) mock_utcnow.return_value = current_time data = self.create_v2_token_fixture( expires='2000-01-01T00:05:10.000123+05:00') data['access']['token']['expires'] = '2000-01-01T00:05:10.000123+05:00' self.assertRaises(auth_token.InvalidUserToken, auth_token.confirm_token_not_expired, data) def test_v3_token_not_expired(self): data = self.create_v3_token_fixture() expected_expires = data['token']['expires_at'] actual_expires = auth_token.confirm_token_not_expired(data) self.assertEqual(actual_expires, expected_expires) def test_v3_token_expired(self): data = self.create_v3_token_fixture(expires=self.one_hour_ago) self.assertRaises(auth_token.InvalidUserToken, auth_token.confirm_token_not_expired, data) @mock.patch('keystoneclient.openstack.common.timeutils.utcnow') def test_v3_token_with_timezone_offset_not_expired(self, mock_utcnow): current_time = timeutils.parse_isotime('2000-01-01T00:01:10.000123Z') current_time = timeutils.normalize_time(current_time) mock_utcnow.return_value = current_time data = self.create_v3_token_fixture( expires='2000-01-01T00:05:10.000123-05:00') expected_expires = '2000-01-01T05:05:10.000123Z' actual_expires = auth_token.confirm_token_not_expired(data) self.assertEqual(actual_expires, expected_expires) @mock.patch('keystoneclient.openstack.common.timeutils.utcnow') def test_v3_token_with_timezone_offset_expired(self, mock_utcnow): current_time = timeutils.parse_isotime('2000-01-01T00:01:10.000123Z') current_time = timeutils.normalize_time(current_time) mock_utcnow.return_value = current_time data = self.create_v3_token_fixture( expires='2000-01-01T00:05:10.000123+05:00') self.assertRaises(auth_token.InvalidUserToken, auth_token.confirm_token_not_expired, data) def test_cached_token_not_expired(self): token = 'mytoken' data = 'this_data' self.set_middleware() self.middleware._init_cache({}) some_time_later = timeutils.strtime(at=(self.now + self.delta)) expires = some_time_later self.middleware._cache_put(token, data, expires) self.assertEqual(self.middleware._cache_get(token), data) def test_cached_token_not_expired_with_old_style_nix_timestamp(self): """Ensure we cannot retrieve a token from the cache. Getting a token from the cache should return None when the token data in the cache stores the expires time as a \*nix style timestamp. """ token = 'mytoken' data = 'this_data' self.set_middleware() self.middleware._init_cache({}) some_time_later = self.now + self.delta # Store a unix timestamp in the cache. expires = calendar.timegm(some_time_later.timetuple()) self.middleware._cache_put(token, data, expires) self.assertIsNone(self.middleware._cache_get(token)) def test_cached_token_expired(self): token = 'mytoken' data = 'this_data' self.set_middleware() self.middleware._init_cache({}) some_time_earlier = timeutils.strtime(at=(self.now - self.delta)) expires = some_time_earlier self.middleware._cache_put(token, data, expires) self.assertIsNone(self.middleware._cache_get(token)) def test_cached_token_with_timezone_offset_not_expired(self): token = 'mytoken' data = 'this_data' self.set_middleware() self.middleware._init_cache({}) timezone_offset = datetime.timedelta(hours=2) some_time_later = self.now - timezone_offset + self.delta expires = timeutils.strtime(some_time_later) + '-02:00' self.middleware._cache_put(token, data, expires) self.assertEqual(self.middleware._cache_get(token), data) def test_cached_token_with_timezone_offset_expired(self): token = 'mytoken' data = 'this_data' self.set_middleware() self.middleware._init_cache({}) timezone_offset = datetime.timedelta(hours=2) some_time_earlier = self.now - timezone_offset - self.delta expires = timeutils.strtime(some_time_earlier) + '-02:00' self.middleware._cache_put(token, data, expires) self.assertIsNone(self.middleware._cache_get(token)) def load_tests(loader, tests, pattern): return testresources.OptimisingTestSuite(tests) python-keystoneclient-0.7.1/keystoneclient/tests/v2_0/0000775000175400017540000000000012315030547024204 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/tests/v2_0/test_shell.py0000664000175400017540000003232212315030451026720 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import sys import mock from mox3 import stubout import six from testtools import matchers from keystoneclient import httpclient from keystoneclient.tests.v2_0 import fakes from keystoneclient.tests.v2_0 import utils DEFAULT_USERNAME = 'username' DEFAULT_PASSWORD = 'password' DEFAULT_TENANT_ID = 'tenant_id' DEFAULT_TENANT_NAME = 'tenant_name' DEFAULT_AUTH_URL = 'http://127.0.0.1:5000/v2.0/' class ShellTests(utils.TestCase): def setUp(self): """Patch os.environ to avoid required auth info.""" super(ShellTests, self).setUp() self.stubs = stubout.StubOutForTesting() self.fake_client = fakes.FakeHTTPClient() self.stubs.Set( httpclient.HTTPClient, "_cs_request", lambda ign_self, *args, **kwargs: self.fake_client._cs_request(*args, **kwargs)) self.stubs.Set( httpclient.HTTPClient, "authenticate", lambda cl_obj: self.fake_client.authenticate(cl_obj)) self.old_environment = os.environ.copy() os.environ = { 'OS_USERNAME': DEFAULT_USERNAME, 'OS_PASSWORD': DEFAULT_PASSWORD, 'OS_TENANT_ID': DEFAULT_TENANT_ID, 'OS_TENANT_NAME': DEFAULT_TENANT_NAME, 'OS_AUTH_URL': DEFAULT_AUTH_URL, } import keystoneclient.shell self.shell = keystoneclient.shell.OpenStackIdentityShell() def tearDown(self): self.stubs.UnsetAll() self.stubs.SmartUnsetAll() os.environ = self.old_environment self.fake_client.clear_callstack() super(ShellTests, self).tearDown() def run_command(self, cmd): orig = sys.stdout try: sys.stdout = six.StringIO() if isinstance(cmd, list): self.shell.main(cmd) else: self.shell.main(cmd.split()) except SystemExit: exc_type, exc_value, exc_traceback = sys.exc_info() self.assertEqual(exc_value.code, 0) finally: out = sys.stdout.getvalue() sys.stdout.close() sys.stdout = orig return out def assert_called(self, method, url, body=None, **kwargs): return self.fake_client.assert_called(method, url, body, **kwargs) def assert_called_anytime(self, method, url, body=None): return self.fake_client.assert_called_anytime(method, url, body) def test_user_list(self): self.run_command('user-list') self.fake_client.assert_called_anytime('GET', '/users') def test_user_create(self): self.run_command('user-create --name new-user') self.fake_client.assert_called_anytime( 'POST', '/users', {'user': {'email': None, 'password': None, 'enabled': True, 'name': 'new-user', 'tenantId': None}}) @mock.patch('sys.stdin', autospec=True) def test_user_create_password_prompt(self, mock_stdin): with mock.patch('getpass.getpass') as mock_getpass: mock_getpass.return_value = 'newpass' self.run_command('user-create --name new-user --pass') self.fake_client.assert_called_anytime( 'POST', '/users', {'user': {'email': None, 'password': 'newpass', 'enabled': True, 'name': 'new-user', 'tenantId': None}}) def test_user_get(self): self.run_command('user-get 1') self.fake_client.assert_called_anytime('GET', '/users/1') def test_user_delete(self): self.run_command('user-delete 1') self.fake_client.assert_called_anytime('DELETE', '/users/1') def test_user_password_update(self): self.run_command('user-password-update --pass newpass 1') self.fake_client.assert_called_anytime( 'PUT', '/users/1/OS-KSADM/password') def test_user_update(self): self.run_command('user-update --name new-user1' ' --email user@email.com --enabled true 1') self.fake_client.assert_called_anytime( 'PUT', '/users/1', {'user': {'id': '1', 'email': 'user@email.com', 'enabled': True, 'name': 'new-user1'} }) required = 'User not updated, no arguments present.' out = self.run_command('user-update 1') self.assertThat(out, matchers.MatchesRegex(required)) self.run_command(['user-update', '--email', '', '1']) self.fake_client.assert_called_anytime( 'PUT', '/users/1', {'user': {'id': '1', 'email': ''} }) def test_role_create(self): self.run_command('role-create --name new-role') self.fake_client.assert_called_anytime( 'POST', '/OS-KSADM/roles', {"role": {"name": "new-role"}}) def test_role_get(self): self.run_command('role-get 1') self.fake_client.assert_called_anytime('GET', '/OS-KSADM/roles/1') def test_role_list(self): self.run_command('role-list') self.fake_client.assert_called_anytime('GET', '/OS-KSADM/roles') def test_role_delete(self): self.run_command('role-delete 1') self.fake_client.assert_called_anytime('DELETE', '/OS-KSADM/roles/1') def test_user_role_add(self): self.run_command('user-role-add --user_id 1 --role_id 1') self.fake_client.assert_called_anytime( 'PUT', '/users/1/roles/OS-KSADM/1') def test_user_role_list(self): self.run_command('user-role-list --user_id 1 --tenant-id 1') self.fake_client.assert_called_anytime( 'GET', '/tenants/1/users/1/roles') self.run_command('user-role-list --user_id 1') self.fake_client.assert_called_anytime( 'GET', '/tenants/1/users/1/roles') self.run_command('user-role-list') self.fake_client.assert_called_anytime( 'GET', '/tenants/1/users/1/roles') def test_user_role_remove(self): self.run_command('user-role-remove --user_id 1 --role_id 1') self.fake_client.assert_called_anytime( 'DELETE', '/users/1/roles/OS-KSADM/1') def test_tenant_create(self): self.run_command('tenant-create --name new-tenant') self.fake_client.assert_called_anytime( 'POST', '/tenants', {"tenant": {"enabled": True, "name": "new-tenant", "description": None}}) def test_tenant_get(self): self.run_command('tenant-get 2') self.fake_client.assert_called_anytime('GET', '/tenants/2') def test_tenant_list(self): self.run_command('tenant-list') self.fake_client.assert_called_anytime('GET', '/tenants') def test_tenant_update(self): self.run_command('tenant-update' ' --name new-tenant1 --enabled false' ' --description desc 2') self.fake_client.assert_called_anytime( 'POST', '/tenants/2', {"tenant": {"enabled": False, "id": "2", "description": "desc", "name": "new-tenant1"}}) required = 'Tenant not updated, no arguments present.' out = self.run_command('tenant-update 1') self.assertThat(out, matchers.MatchesRegex(required)) def test_tenant_delete(self): self.run_command('tenant-delete 2') self.fake_client.assert_called_anytime('DELETE', '/tenants/2') def test_service_create(self): self.run_command('service-create --name service1 --type compute') self.fake_client.assert_called_anytime( 'POST', '/OS-KSADM/services', {"OS-KSADM:service": {"type": "compute", "name": "service1", "description": None}}) def test_service_get(self): self.run_command('service-get 1') self.fake_client.assert_called_anytime('GET', '/OS-KSADM/services/1') def test_service_list(self): self.run_command('service-list') self.fake_client.assert_called_anytime('GET', '/OS-KSADM/services') def test_service_delete(self): self.run_command('service-delete 1') self.fake_client.assert_called_anytime( 'DELETE', '/OS-KSADM/services/1') def test_catalog(self): self.run_command('catalog') self.run_command('catalog --service compute') def test_ec2_credentials_create(self): self.run_command('ec2-credentials-create' ' --tenant-id 1 --user-id 1') self.fake_client.assert_called_anytime( 'POST', '/users/1/credentials/OS-EC2', {'tenant_id': '1'}) self.run_command('ec2-credentials-create --tenant-id 1') self.fake_client.assert_called_anytime( 'POST', '/users/1/credentials/OS-EC2', {'tenant_id': '1'}) self.run_command('ec2-credentials-create') self.fake_client.assert_called_anytime( 'POST', '/users/1/credentials/OS-EC2', {'tenant_id': '1'}) def test_ec2_credentials_delete(self): self.run_command('ec2-credentials-delete --access 2 --user-id 1') self.fake_client.assert_called_anytime( 'DELETE', '/users/1/credentials/OS-EC2/2') self.run_command('ec2-credentials-delete --access 2') self.fake_client.assert_called_anytime( 'DELETE', '/users/1/credentials/OS-EC2/2') def test_ec2_credentials_list(self): self.run_command('ec2-credentials-list --user-id 1') self.fake_client.assert_called_anytime( 'GET', '/users/1/credentials/OS-EC2') self.run_command('ec2-credentials-list') self.fake_client.assert_called_anytime( 'GET', '/users/1/credentials/OS-EC2') def test_ec2_credentials_get(self): self.run_command('ec2-credentials-get --access 2 --user-id 1') self.fake_client.assert_called_anytime( 'GET', '/users/1/credentials/OS-EC2/2') def test_bootstrap(self): self.run_command('bootstrap --user-name new-user' ' --pass 1 --role-name admin' ' --tenant-name new-tenant') self.fake_client.assert_called_anytime( 'POST', '/users', {'user': {'email': None, 'password': '1', 'enabled': True, 'name': 'new-user', 'tenantId': None}}) self.run_command('bootstrap --user-name new-user' ' --pass 1 --role-name admin' ' --tenant-name new-tenant') self.fake_client.assert_called_anytime( 'POST', '/tenants', {"tenant": {"enabled": True, "name": "new-tenant", "description": None}}) self.run_command('bootstrap --user-name new-user' ' --pass 1 --role-name new-role' ' --tenant-name new-tenant') self.fake_client.assert_called_anytime( 'POST', '/OS-KSADM/roles', {"role": {"name": "new-role"}}) self.run_command('bootstrap --user-name' ' new-user --pass 1 --role-name admin' ' --tenant-name new-tenant') self.fake_client.assert_called_anytime( 'PUT', '/tenants/1/users/1/roles/OS-KSADM/1') def test_bash_completion(self): self.run_command('bash-completion') def test_help(self): out = self.run_command('help') required = 'usage: keystone' self.assertThat(out, matchers.MatchesRegex(required)) def test_password_update(self): self.run_command('password-update --current-password oldpass' ' --new-password newpass') self.fake_client.assert_called_anytime( 'PATCH', '/OS-KSCRUD/users/1', {'user': {'original_password': 'oldpass', 'password': 'newpass'}}) def test_endpoint_create(self): self.run_command('endpoint-create --service-id 1 ' '--publicurl=http://example.com:1234/go') self.fake_client.assert_called_anytime( 'POST', '/endpoints', {'endpoint': {'adminurl': None, 'service_id': '1', 'region': 'regionOne', 'internalurl': None, 'publicurl': "http://example.com:1234/go"}}) def test_endpoint_list(self): self.run_command('endpoint-list') self.fake_client.assert_called_anytime('GET', '/endpoints') python-keystoneclient-0.7.1/keystoneclient/tests/v2_0/client_fixtures.py0000664000175400017540000001561212315030450027763 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from __future__ import unicode_literals UNSCOPED_TOKEN = { 'access': {'serviceCatalog': {}, 'token': {'expires': '2012-10-03T16:58:01Z', 'id': '3e2813b7ba0b4006840c3825860b86ed'}, 'user': {'id': 'c4da488862bd435c9e6c0275a0d0e49a', 'name': 'exampleuser', 'roles': [], 'roles_links': [], 'username': 'exampleuser'} } } _TENANT_ID = '225da22d3ce34b15877ea70b2a575f58' PROJECT_SCOPED_TOKEN = { 'access': { 'serviceCatalog': [{ 'endpoints': [{ 'adminURL': 'http://admin:8776/v1/%s' % _TENANT_ID, 'internalURL': 'http://internal:8776/v1/%s' % _TENANT_ID, 'publicURL': 'http://public.com:8776/v1/%s' % _TENANT_ID, 'region': 'RegionOne' }], 'endpoints_links': [], 'name': 'Volume Service', 'type': 'volume'}, {'endpoints': [{ 'adminURL': 'http://admin:9292/v1', 'internalURL': 'http://internal:9292/v1', 'publicURL': 'http://public.com:9292/v1', 'region': 'RegionOne' }], 'endpoints_links': [], 'name': 'Image Service', 'type': 'image'}, {'endpoints': [{ 'adminURL': 'http://admin:8774/v2/%s' % _TENANT_ID, 'internalURL': 'http://internal:8774/v2/%s' % _TENANT_ID, 'publicURL': 'http://public.com:8774/v2/%s' % _TENANT_ID, 'region': 'RegionOne' }], 'endpoints_links': [], 'name': 'Compute Service', 'type': 'compute'}, {'endpoints': [{ 'adminURL': 'http://admin:8773/services/Admin', 'internalURL': 'http://internal:8773/services/Cloud', 'publicURL': 'http://public.com:8773/services/Cloud', 'region': 'RegionOne' }], 'endpoints_links': [], 'name': 'EC2 Service', 'type': 'ec2'}, {'endpoints': [{ 'adminURL': 'http://admin:35357/v2.0', 'internalURL': 'http://internal:5000/v2.0', 'publicURL': 'http://public.com:5000/v2.0', 'region': 'RegionOne' }], 'endpoints_links': [], 'name': 'Identity Service', 'type': 'identity'}], 'token': {'expires': '2012-10-03T16:53:36Z', 'id': '04c7d5ffaeef485f9dc69c06db285bdb', 'tenant': {'description': '', 'enabled': True, 'id': '225da22d3ce34b15877ea70b2a575f58', 'name': 'exampleproject'}}, 'user': {'id': 'c4da488862bd435c9e6c0275a0d0e49a', 'name': 'exampleuser', 'roles': [{'id': 'edc12489faa74ee0aca0b8a0b4d74a74', 'name': 'Member'}], 'roles_links': [], 'username': 'exampleuser'} } } AUTH_RESPONSE_BODY = { 'access': { 'token': { 'id': 'ab48a9efdfedb23ty3494', 'expires': '2010-11-01T03:32:15-05:00', 'tenant': { 'id': '345', 'name': 'My Project' } }, 'user': { 'id': '123', 'name': 'jqsmith', 'roles': [{ 'id': '234', 'name': 'compute:admin' }, { 'id': '235', 'name': 'object-store:admin', 'tenantId': '1' }], 'roles_links': [] }, 'serviceCatalog': [{ 'name': 'Cloud Servers', 'type': 'compute', 'endpoints': [{ 'tenantId': '1', 'publicURL': 'https://compute.north.host/v1/1234', 'internalURL': 'https://compute.north.host/v1/1234', 'region': 'North', 'versionId': '1.0', 'versionInfo': 'https://compute.north.host/v1.0/', 'versionList': 'https://compute.north.host/' }, { 'tenantId': '2', 'publicURL': 'https://compute.north.host/v1.1/3456', 'internalURL': 'https://compute.north.host/v1.1/3456', 'region': 'North', 'versionId': '1.1', 'versionInfo': 'https://compute.north.host/v1.1/', 'versionList': 'https://compute.north.host/' }], 'endpoints_links': [] }, { 'name': 'Cloud Files', 'type': 'object-store', 'endpoints': [{ 'tenantId': '11', 'publicURL': 'https://swift.north.host/v1/blah', 'internalURL': 'https://swift.north.host/v1/blah', 'region': 'South', 'versionId': '1.0', 'versionInfo': 'uri', 'versionList': 'uri' }, { 'tenantId': '2', 'publicURL': 'https://swift.north.host/v1.1/blah', 'internalURL': 'https://compute.north.host/v1.1/blah', 'region': 'South', 'versionId': '1.1', 'versionInfo': 'https://swift.north.host/v1.1/', 'versionList': 'https://swift.north.host/' }], 'endpoints_links': [{ 'rel': 'next', 'href': 'https://identity.north.host/v2.0/' 'endpoints?marker=2' }] }, { 'name': 'Image Servers', 'type': 'image', 'endpoints': [{ 'publicURL': 'https://image.north.host/v1/', 'internalURL': 'https://image-internal.north.host/v1/', 'region': 'North' }, { 'publicURL': 'https://image.south.host/v1/', 'internalURL': 'https://image-internal.south.host/v1/', 'region': 'South' }], 'endpoints_links': [] }], 'serviceCatalog_links': [{ 'rel': 'next', 'href': ('https://identity.host/v2.0/endpoints?' 'session=2hfh8Ar&marker=2') }] } } python-keystoneclient-0.7.1/keystoneclient/tests/v2_0/test_roles.py0000664000175400017540000001030512315030451026732 0ustar jenkinsjenkins00000000000000# 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 httpretty from keystoneclient.tests.v2_0 import utils from keystoneclient.v2_0 import roles class RoleTests(utils.TestCase): def setUp(self): super(RoleTests, self).setUp() self.TEST_ROLES = { "roles": { "values": [ { "name": "admin", "id": 1, }, { "name": "member", "id": 2, } ], }, } @httpretty.activate def test_create(self): req_body = { "role": { "name": "sysadmin", } } resp_body = { "role": { "name": "sysadmin", "id": 3, } } self.stub_url(httpretty.POST, ['OS-KSADM', 'roles'], json=resp_body) role = self.client.roles.create(req_body['role']['name']) self.assertRequestBodyIs(json=req_body) self.assertIsInstance(role, roles.Role) self.assertEqual(role.id, 3) self.assertEqual(role.name, req_body['role']['name']) @httpretty.activate def test_delete(self): self.stub_url(httpretty.DELETE, ['OS-KSADM', 'roles', '1'], status=204) self.client.roles.delete(1) @httpretty.activate def test_get(self): self.stub_url(httpretty.GET, ['OS-KSADM', 'roles', '1'], json={'role': self.TEST_ROLES['roles']['values'][0]}) role = self.client.roles.get(1) self.assertIsInstance(role, roles.Role) self.assertEqual(role.id, 1) self.assertEqual(role.name, 'admin') @httpretty.activate def test_list(self): self.stub_url(httpretty.GET, ['OS-KSADM', 'roles'], json=self.TEST_ROLES) role_list = self.client.roles.list() [self.assertIsInstance(r, roles.Role) for r in role_list] @httpretty.activate def test_roles_for_user(self): self.stub_url(httpretty.GET, ['users', 'foo', 'roles'], json=self.TEST_ROLES) role_list = self.client.roles.roles_for_user('foo') [self.assertIsInstance(r, roles.Role) for r in role_list] @httpretty.activate def test_roles_for_user_tenant(self): self.stub_url(httpretty.GET, ['tenants', 'barrr', 'users', 'foo', 'roles'], json=self.TEST_ROLES) role_list = self.client.roles.roles_for_user('foo', 'barrr') [self.assertIsInstance(r, roles.Role) for r in role_list] @httpretty.activate def test_add_user_role(self): self.stub_url(httpretty.PUT, ['users', 'foo', 'roles', 'OS-KSADM', 'barrr'], status=204) self.client.roles.add_user_role('foo', 'barrr') @httpretty.activate def test_add_user_role_tenant(self): self.stub_url(httpretty.PUT, ['tenants', '4', 'users', 'foo', 'roles', 'OS-KSADM', 'barrr'], status=204) self.client.roles.add_user_role('foo', 'barrr', '4') @httpretty.activate def test_remove_user_role(self): self.stub_url(httpretty.DELETE, ['users', 'foo', 'roles', 'OS-KSADM', 'barrr'], status=204) self.client.roles.remove_user_role('foo', 'barrr') @httpretty.activate def test_remove_user_role_tenant(self): self.stub_url(httpretty.DELETE, ['tenants', '4', 'users', 'foo', 'roles', 'OS-KSADM', 'barrr'], status=204) self.client.roles.remove_user_role('foo', 'barrr', '4') python-keystoneclient-0.7.1/keystoneclient/tests/v2_0/test_discovery.py0000664000175400017540000000647112315030451027626 0ustar jenkinsjenkins00000000000000# 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 httpretty from keystoneclient.generic import client from keystoneclient.tests.v2_0 import utils class DiscoverKeystoneTests(utils.UnauthenticatedTestCase): def setUp(self): super(DiscoverKeystoneTests, self).setUp() self.TEST_RESPONSE_DICT = { "versions": { "values": [{ "id": "v2.0", "status": "beta", "updated": "2011-11-19T00:00:00Z", "links": [ {"rel": "self", "href": "http://127.0.0.1:5000/v2.0/", }, {"rel": "describedby", "type": "text/html", "href": "http://docs.openstack.org/api/" "openstack-identity-service/2.0/content/", }, {"rel": "describedby", "type": "application/pdf", "href": "http://docs.openstack.org/api/" "openstack-identity-service/2.0/" "identity-dev-guide-2.0.pdf", }, {"rel": "describedby", "type": "application/vnd.sun.wadl+xml", "href": "http://127.0.0.1:5000/v2.0/identity.wadl", } ], "media-types": [{ "base": "application/xml", "type": "application/vnd.openstack.identity-v2.0+xml", }, { "base": "application/json", "type": "application/vnd.openstack.identity-v2.0+json", }], }], }, } @httpretty.activate def test_get_versions(self): self.stub_url(httpretty.GET, base_url=self.TEST_ROOT_URL, json=self.TEST_RESPONSE_DICT) cs = client.Client() versions = cs.discover(self.TEST_ROOT_URL) self.assertIsInstance(versions, dict) self.assertIn('message', versions) self.assertIn('v2.0', versions) self.assertEqual( versions['v2.0']['url'], self.TEST_RESPONSE_DICT['versions']['values'][0]['links'][0] ['href']) @httpretty.activate def test_get_version_local(self): self.stub_url(httpretty.GET, base_url="http://localhost:35357/", json=self.TEST_RESPONSE_DICT) cs = client.Client() versions = cs.discover() self.assertIsInstance(versions, dict) self.assertIn('message', versions) self.assertIn('v2.0', versions) self.assertEqual( versions['v2.0']['url'], self.TEST_RESPONSE_DICT['versions']['values'][0]['links'][0] ['href']) python-keystoneclient-0.7.1/keystoneclient/tests/v2_0/test_client.py0000664000175400017540000001467712315030451027104 0ustar jenkinsjenkins00000000000000# 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 copy import json import httpretty from keystoneclient import exceptions from keystoneclient.tests.v2_0 import client_fixtures from keystoneclient.tests.v2_0 import utils from keystoneclient.v2_0 import client class KeystoneClientTest(utils.TestCase): @httpretty.activate def test_unscoped_init(self): self.stub_auth(json=client_fixtures.UNSCOPED_TOKEN) c = client.Client(username='exampleuser', password='password', auth_url=self.TEST_URL) self.assertIsNotNone(c.auth_ref) self.assertFalse(c.auth_ref.scoped) self.assertFalse(c.auth_ref.domain_scoped) self.assertFalse(c.auth_ref.project_scoped) self.assertIsNone(c.auth_ref.trust_id) self.assertFalse(c.auth_ref.trust_scoped) @httpretty.activate def test_scoped_init(self): self.stub_auth(json=client_fixtures.PROJECT_SCOPED_TOKEN) c = client.Client(username='exampleuser', password='password', tenant_name='exampleproject', auth_url=self.TEST_URL) self.assertIsNotNone(c.auth_ref) self.assertTrue(c.auth_ref.scoped) self.assertTrue(c.auth_ref.project_scoped) self.assertFalse(c.auth_ref.domain_scoped) self.assertIsNone(c.auth_ref.trust_id) self.assertFalse(c.auth_ref.trust_scoped) @httpretty.activate def test_auth_ref_load(self): self.stub_auth(json=client_fixtures.PROJECT_SCOPED_TOKEN) cl = client.Client(username='exampleuser', password='password', tenant_name='exampleproject', auth_url=self.TEST_URL) cache = json.dumps(cl.auth_ref) new_client = client.Client(auth_ref=json.loads(cache)) self.assertIsNotNone(new_client.auth_ref) self.assertTrue(new_client.auth_ref.scoped) self.assertTrue(new_client.auth_ref.project_scoped) self.assertFalse(new_client.auth_ref.domain_scoped) self.assertIsNone(new_client.auth_ref.trust_id) self.assertFalse(new_client.auth_ref.trust_scoped) self.assertEqual(new_client.username, 'exampleuser') self.assertIsNone(new_client.password) self.assertEqual(new_client.management_url, 'http://admin:35357/v2.0') @httpretty.activate def test_auth_ref_load_with_overridden_arguments(self): self.stub_auth(json=client_fixtures.PROJECT_SCOPED_TOKEN) cl = client.Client(username='exampleuser', password='password', tenant_name='exampleproject', auth_url=self.TEST_URL) cache = json.dumps(cl.auth_ref) new_auth_url = "http://new-public:5000/v2.0" new_client = client.Client(auth_ref=json.loads(cache), auth_url=new_auth_url) self.assertIsNotNone(new_client.auth_ref) self.assertTrue(new_client.auth_ref.scoped) self.assertTrue(new_client.auth_ref.scoped) self.assertTrue(new_client.auth_ref.project_scoped) self.assertFalse(new_client.auth_ref.domain_scoped) self.assertIsNone(new_client.auth_ref.trust_id) self.assertFalse(new_client.auth_ref.trust_scoped) self.assertEqual(new_client.auth_url, new_auth_url) self.assertEqual(new_client.username, 'exampleuser') self.assertIsNone(new_client.password) self.assertEqual(new_client.management_url, 'http://admin:35357/v2.0') def test_init_err_no_auth_url(self): self.assertRaises(exceptions.AuthorizationFailure, client.Client, username='exampleuser', password='password') @httpretty.activate def test_management_url_is_updated(self): second = copy.deepcopy(client_fixtures.PROJECT_SCOPED_TOKEN) first_url = 'http://admin:35357/v2.0' second_url = "http://secondurl:%d/v2.0'" for entry in second['access']['serviceCatalog']: if entry['type'] == 'identity': entry['endpoints'] = [{'adminURL': second_url % 35357, 'internalURL': second_url % 5000, 'publicURL': second_url % 6000, 'region': 'RegionOne'}] self.stub_auth(json=client_fixtures.PROJECT_SCOPED_TOKEN) cl = client.Client(username='exampleuser', password='password', tenant_name='exampleproject', auth_url=self.TEST_URL) self.assertEqual(cl.management_url, first_url) self.stub_auth(json=second) cl.authenticate() self.assertEqual(cl.management_url, second_url % 35357) @httpretty.activate def test_client_with_region_name_passes_to_service_catalog(self): # NOTE(jamielennox): this is deprecated behaviour that should be # removed ASAP, however must remain compatible. self.stub_auth(json=client_fixtures.AUTH_RESPONSE_BODY) cl = client.Client(username='exampleuser', password='password', tenant_name='exampleproject', auth_url=self.TEST_URL, region_name='North') self.assertEqual(cl.service_catalog.url_for(service_type='image'), 'https://image.north.host/v1/') cl = client.Client(username='exampleuser', password='password', tenant_name='exampleproject', auth_url=self.TEST_URL, region_name='South') self.assertEqual(cl.service_catalog.url_for(service_type='image'), 'https://image.south.host/v1/') python-keystoneclient-0.7.1/keystoneclient/tests/v2_0/fakes.py0000664000175400017540000003757312315030450025657 0ustar jenkinsjenkins00000000000000# Copyright (c) 2011 X.commerce, a business unit of eBay Inc. # Copyright 2011 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from six.moves.urllib import parse as urlparse from keystoneclient.tests import fakes from keystoneclient.tests.v2_0 import utils class FakeHTTPClient(fakes.FakeClient): def __init__(self, **kwargs): self.username = 'username' self.password = 'password' self.auth_url = 'auth_url' self.callstack = [] def _cs_request(self, url, method, **kwargs): # Check that certain things are called correctly if method in ['GET', 'DELETE']: assert 'body' not in kwargs elif method == 'PUT': kwargs.setdefault('body', None) # Call the method args = urlparse.parse_qsl(urlparse.urlparse(url)[4]) kwargs.update(args) munged_url = url.rsplit('?', 1)[0] munged_url = munged_url.strip('/').replace('/', '_').replace('.', '_') munged_url = munged_url.replace('-', '_') callback = "%s_%s" % (method.lower(), munged_url) if not hasattr(self, callback): raise AssertionError('Called unknown API method: %s %s, ' 'expected fakes method name: %s' % (method, url, callback)) # Note the call self.callstack.append((method, url, kwargs.get('body'))) if not hasattr(self, callback): raise AssertionError('Called unknown API method: %s %s, ' 'expected fakes method name: %s' % (method, url, callback)) # Note the call self.callstack.append((method, url, kwargs.get('body'))) status, body = getattr(self, callback)(**kwargs) r = utils.TestResponse({ "status_code": status, "text": body}) return r, body # # List all extensions # def post_tokens(self, **kw): body = [ {"access": {"token": {"expires": "2012-02-05T00:00:00", "id": "887665443383838", "tenant": {"id": "1", "name": "customer-x"}}, "serviceCatalog": [ {"endpoints": [ {"adminURL": "http://swift.admin-nets.local:8080/", "region": "RegionOne", "internalURL": "http://127.0.0.1:8080/v1/AUTH_1", "publicURL": "http://swift.publicinternets.com/v1/AUTH_1"}], "type": "object-store", "name": "swift"}, {"endpoints": [ {"adminURL": "http://cdn.admin-nets.local/v1.1/1", "region": "RegionOne", "internalURL": "http://127.0.0.1:7777/v1.1/1", "publicURL": "http://cdn.publicinternets.com/v1.1/1"}], "type": "object-store", "name": "cdn"}], "user": {"id": "1", "roles": [ {"tenantId": "1", "id": "3", "name": "Member"}], "name": "joeuser"}} } ] return (200, body) def get_tokens_887665443383838(self, **kw): body = [ {"access": {"token": {"expires": "2012-02-05T00:00:00", "id": "887665443383838", "tenant": {"id": "1", "name": "customer-x"}}, "user": {"name": "joeuser", "tenantName": "customer-x", "id": "1", "roles": [{"serviceId": "1", "id": "3", "name": "Member"}], "tenantId": "1"}} } ] return (200, body) def get_tokens_887665443383838_endpoints(self, **kw): body = [ {"endpoints_links": [ {"href": "http://127.0.0.1:35357/tokens/887665443383838" "/endpoints?'marker=5&limit=10'", "rel": "next"}], "endpoints": [ {"internalURL": "http://127.0.0.1:8080/v1/AUTH_1", "name": "swift", "adminURL": "http://swift.admin-nets.local:8080/", "region": "RegionOne", "tenantId": 1, "type": "object-store", "id": 1, "publicURL": "http://swift.publicinternets.com/v1/AUTH_1"}, {"internalURL": "http://localhost:8774/v1.0", "name": "nova_compat", "adminURL": "http://127.0.0.1:8774/v1.0", "region": "RegionOne", "tenantId": 1, "type": "compute", "id": 2, "publicURL": "http://nova.publicinternets.com/v1.0/"}, {"internalURL": "http://localhost:8774/v1.1", "name": "nova", "adminURL": "http://127.0.0.1:8774/v1.1", "region": "RegionOne", "tenantId": 1, "type": "compute", "id": 3, "publicURL": "http://nova.publicinternets.com/v1.1/"}, {"internalURL": "http://127.0.0.1:9292/v1.1/", "name": "glance", "adminURL": "http://nova.admin-nets.local/v1.1/", "region": "RegionOne", "tenantId": 1, "type": "image", "id": 4, "publicURL": "http://glance.publicinternets.com/v1.1/"}, {"internalURL": "http://127.0.0.1:7777/v1.1/1", "name": "cdn", "adminURL": "http://cdn.admin-nets.local/v1.1/1", "region": "RegionOne", "tenantId": 1, "versionId": "1.1", "versionList": "http://127.0.0.1:7777/", "versionInfo": "http://127.0.0.1:7777/v1.1", "type": "object-store", "id": 5, "publicURL": "http://cdn.publicinternets.com/v1.1/1"}] } ] return (200, body) def get(self, **kw): body = { "version": { "id": "v2.0", "status": "beta", "updated": "2011-11-19T00:00:00Z", "links": [ {"rel": "self", "href": "http://127.0.0.1:35357/v2.0/"}, {"rel": "describedby", "type": "text/html", "href": "http://docs.openstack.org/" "api/openstack-identity-service/2.0/content/"}, {"rel": "describedby", "type": "application/pdf", "href": "http://docs.openstack.org/api/" "openstack-identity-service/2.0/" "identity-dev-guide-2.0.pdf"}, {"rel": "describedby", "type": "application/vnd.sun.wadl+xml", "href": "http://127.0.0.1:35357/v2.0/identity-admin.wadl"}], "media-types": [ {"base": "application/xml", "type": "application/vnd.openstack.identity-v2.0+xml"}, {"base": "application/json", "type": "application/vnd.openstack.identity-v2.0+json"}] } } return (200, body) def get_extensions(self, **kw): body = { "extensions": {"values": []} } return (200, body) def post_tenants(self, **kw): body = {"tenant": {"enabled": True, "description": None, "name": "new-tenant", "id": "1"}} return (200, body) def post_tenants_2(self, **kw): body = {"tenant": {"enabled": False, "description": "desc", "name": "new-tenant1", "id": "2"}} return (200, body) def get_tenants(self, **kw): body = { "tenants_links": [], "tenants": [ {"enabled": False, "description": None, "name": "project-y", "id": "1"}, {"enabled": True, "description": None, "name": "new-tenant", "id": "2"}, {"enabled": True, "description": None, "name": "customer-x", "id": "1"}] } return (200, body) def get_tenants_1(self, **kw): body = {"tenant": {"enabled": True, "description": None, "name": "new-tenant", "id": "1"}} return (200, body) def get_tenants_2(self, **kw): body = {"tenant": {"enabled": True, "description": None, "name": "new-tenant", "id": "2"}} return (200, body) def delete_tenants_2(self, **kw): body = {} return (200, body) def get_tenants_1_users_1_roles(self, **kw): body = { "roles": [ {"id": "1", "name": "Admin"}, {"id": "2", "name": "Member"}, {"id": "3", "name": "new-role"}] } return (200, body) def put_users_1_roles_OS_KSADM_1(self, **kw): body = { "roles": {"id": "1", "name": "Admin"}} return (200, body) def delete_users_1_roles_OS_KSADM_1(self, **kw): body = {} return (200, body) def put_tenants_1_users_1_roles_OS_KSADM_1(self, **kw): body = { "role": {"id": "1", "name": "Admin"}} return (200, body) def get_users(self, **kw): body = { "users": [ {"name": self.username, "enabled": "true", "email": "sdfsdf@sdfsd.sdf", "id": "1", "tenantId": "1"}, {"name": "user2", "enabled": "true", "email": "sdfsdf@sdfsd.sdf", "id": "2", "tenantId": "1"}] } return (200, body) def get_users_1(self, **kw): body = { "user": { "tenantId": "1", "enabled": "true", "id": "1", "name": self.username} } return (200, body) def put_users_1(self, **kw): body = { "user": { "tenantId": "1", "enabled": "true", "id": "1", "name": "new-user1", "email": "user@email.com"} } return (200, body) def put_users_1_OS_KSADM_password(self, **kw): body = { "user": { "tenantId": "1", "enabled": "true", "id": "1", "name": "new-user1", "email": "user@email.com"} } return (200, body) def post_users(self, **kw): body = { "user": { "tenantId": "1", "enabled": "true", "id": "1", "name": self.username} } return (200, body) def delete_users_1(self, **kw): body = [] return (200, body) def get_users_1_roles(self, **kw): body = [ {"roles_links": [], "roles":[ {"id": "2", "name": "KeystoneServiceAdmin"}] } ] return (200, body) def post_OS_KSADM_roles(self, **kw): body = {"role": {"name": "new-role", "id": "1"}} return (200, body) def get_OS_KSADM_roles(self, **kw): body = {"roles": [ {"id": "10", "name": "admin"}, {"id": "20", "name": "member"}, {"id": "1", "name": "new-role"}] } return (200, body) def get_OS_KSADM_roles_1(self, **kw): body = {"role": {"name": "new-role", "id": "1"} } return (200, body) def delete_OS_KSADM_roles_1(self, **kw): body = {} return (200, body) def post_OS_KSADM_services(self, **kw): body = {"OS-KSADM:service": {"id": "1", "type": "compute", "name": "service1", "description": None} } return (200, body) def get_OS_KSADM_services_1(self, **kw): body = {"OS-KSADM:service": {"description": None, "type": "compute", "id": "1", "name": "service1"} } return (200, body) def get_OS_KSADM_services(self, **kw): body = { "OS-KSADM:services": [ {"description": None, "type": "compute", "id": "1", "name": "service1"}, {"description": None, "type": "identity", "id": "2", "name": "service2"}] } return (200, body) def delete_OS_KSADM_services_1(self, **kw): body = {} return (200, body) def post_users_1_credentials_OS_EC2(self, **kw): body = {"credential": {"access": "1", "tenant_id": "1", "secret": "1", "user_id": "1"} } return (200, body) def get_users_1_credentials_OS_EC2(self, **kw): body = {"credentials": [ {"access": "1", "tenant_id": "1", "secret": "1", "user_id": "1"}] } return (200, body) def get_users_1_credentials_OS_EC2_2(self, **kw): body = { "credential": {"access": "2", "tenant_id": "1", "secret": "1", "user_id": "1"} } return (200, body) def delete_users_1_credentials_OS_EC2_2(self, **kw): body = {} return (200, body) def patch_OS_KSCRUD_users_1(self, **kw): body = {} return (200, body) def get_endpoints(self, **kw): body = { 'endpoints': [ {'adminURL': 'http://cdn.admin-nets.local/v1.1/1', 'region': 'RegionOne', 'internalURL': 'http://127.0.0.1:7777/v1.1/1', 'publicURL': 'http://cdn.publicinternets.com/v1.1/1'}], 'type': 'compute', 'name': 'nova-compute' } return (200, body) def post_endpoints(self, **kw): body = { "endpoint": {"adminURL": "http://swift.admin-nets.local:8080/", "region": "RegionOne", "internalURL": "http://127.0.0.1:8080/v1/AUTH_1", "publicURL": "http://swift.publicinternets.com/v1/AUTH_1"}, "type": "compute", "name": "nova-compute" } return (200, body) python-keystoneclient-0.7.1/keystoneclient/tests/v2_0/utils.py0000664000175400017540000000607412315030451025717 0ustar jenkinsjenkins00000000000000# 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 httpretty from keystoneclient.tests import utils from keystoneclient.v2_0 import client TestResponse = utils.TestResponse class UnauthenticatedTestCase(utils.TestCase): """Class used as base for unauthenticated calls.""" TEST_ROOT_URL = 'http://127.0.0.1:5000/' TEST_URL = '%s%s' % (TEST_ROOT_URL, 'v2.0') TEST_ROOT_ADMIN_URL = 'http://127.0.0.1:35357/' TEST_ADMIN_URL = '%s%s' % (TEST_ROOT_ADMIN_URL, 'v2.0') class TestCase(UnauthenticatedTestCase): TEST_ADMIN_IDENTITY_ENDPOINT = "http://127.0.0.1:35357/v2.0" TEST_SERVICE_CATALOG = [{ "endpoints": [{ "adminURL": "http://cdn.admin-nets.local:8774/v1.0", "region": "RegionOne", "internalURL": "http://127.0.0.1:8774/v1.0", "publicURL": "http://cdn.admin-nets.local:8774/v1.0/" }], "type": "nova_compat", "name": "nova_compat" }, { "endpoints": [{ "adminURL": "http://nova/novapi/admin", "region": "RegionOne", "internalURL": "http://nova/novapi/internal", "publicURL": "http://nova/novapi/public" }], "type": "compute", "name": "nova" }, { "endpoints": [{ "adminURL": "http://glance/glanceapi/admin", "region": "RegionOne", "internalURL": "http://glance/glanceapi/internal", "publicURL": "http://glance/glanceapi/public" }], "type": "image", "name": "glance" }, { "endpoints": [{ "adminURL": TEST_ADMIN_IDENTITY_ENDPOINT, "region": "RegionOne", "internalURL": "http://127.0.0.1:5000/v2.0", "publicURL": "http://127.0.0.1:5000/v2.0" }], "type": "identity", "name": "keystone" }, { "endpoints": [{ "adminURL": "http://swift/swiftapi/admin", "region": "RegionOne", "internalURL": "http://swift/swiftapi/internal", "publicURL": "http://swift/swiftapi/public" }], "type": "object-store", "name": "swift" }] def setUp(self): super(TestCase, self).setUp() self.client = client.Client(username=self.TEST_USER, token=self.TEST_TOKEN, tenant_name=self.TEST_TENANT_NAME, auth_url=self.TEST_URL, endpoint=self.TEST_URL) def stub_auth(self, **kwargs): self.stub_url(httpretty.POST, ['tokens'], **kwargs) python-keystoneclient-0.7.1/keystoneclient/tests/v2_0/test_services.py0000664000175400017540000000646412315030451027444 0ustar jenkinsjenkins00000000000000# 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 httpretty from keystoneclient.tests.v2_0 import utils from keystoneclient.v2_0 import services class ServiceTests(utils.TestCase): def setUp(self): super(ServiceTests, self).setUp() self.TEST_SERVICES = { "OS-KSADM:services": { "values": [ { "name": "nova", "type": "compute", "description": "Nova-compatible service.", "id": 1 }, { "name": "keystone", "type": "identity", "description": "Keystone-compatible service.", "id": 2 }, ], }, } @httpretty.activate def test_create(self): req_body = { "OS-KSADM:service": { "name": "swift", "type": "object-store", "description": "Swift-compatible service.", } } resp_body = { "OS-KSADM:service": { "name": "swift", "type": "object-store", "description": "Swift-compatible service.", "id": 3, } } self.stub_url(httpretty.POST, ['OS-KSADM', 'services'], json=resp_body) service = self.client.services.create( req_body['OS-KSADM:service']['name'], req_body['OS-KSADM:service']['type'], req_body['OS-KSADM:service']['description']) self.assertIsInstance(service, services.Service) self.assertEqual(service.id, 3) self.assertEqual(service.name, req_body['OS-KSADM:service']['name']) self.assertRequestBodyIs(json=req_body) @httpretty.activate def test_delete(self): self.stub_url(httpretty.DELETE, ['OS-KSADM', 'services', '1'], status=204) self.client.services.delete(1) @httpretty.activate def test_get(self): test_services = self.TEST_SERVICES['OS-KSADM:services']['values'][0] self.stub_url(httpretty.GET, ['OS-KSADM', 'services', '1'], json={'OS-KSADM:service': test_services}) service = self.client.services.get(1) self.assertIsInstance(service, services.Service) self.assertEqual(service.id, 1) self.assertEqual(service.name, 'nova') self.assertEqual(service.type, 'compute') @httpretty.activate def test_list(self): self.stub_url(httpretty.GET, ['OS-KSADM', 'services'], json=self.TEST_SERVICES) service_list = self.client.services.list() [self.assertIsInstance(r, services.Service) for r in service_list] python-keystoneclient-0.7.1/keystoneclient/tests/v2_0/test_endpoints.py0000664000175400017540000000642612315030451027622 0ustar jenkinsjenkins00000000000000# 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 httpretty from keystoneclient.tests.v2_0 import utils from keystoneclient.v2_0 import endpoints class EndpointTests(utils.TestCase): def setUp(self): super(EndpointTests, self).setUp() self.TEST_ENDPOINTS = { 'endpoints': [ { 'adminurl': 'http://host-1:8774/v1.1/$(tenant_id)s', 'id': '8f9531231e044e218824b0e58688d262', 'internalurl': 'http://host-1:8774/v1.1/$(tenant_id)s', 'publicurl': 'http://host-1:8774/v1.1/$(tenant_id)s', 'region': 'RegionOne', }, { 'adminurl': 'http://host-1:8774/v1.1/$(tenant_id)s', 'id': '8f9531231e044e218824b0e58688d263', 'internalurl': 'http://host-1:8774/v1.1/$(tenant_id)s', 'publicurl': 'http://host-1:8774/v1.1/$(tenant_id)s', 'region': 'RegionOne', } ] } @httpretty.activate def test_create(self): req_body = { "endpoint": { "region": "RegionOne", "publicurl": "http://host-3:8774/v1.1/$(tenant_id)s", "internalurl": "http://host-3:8774/v1.1/$(tenant_id)s", "adminurl": "http://host-3:8774/v1.1/$(tenant_id)s", "service_id": "e044e21", } } resp_body = { "endpoint": { "adminurl": "http://host-3:8774/v1.1/$(tenant_id)s", "region": "RegionOne", "id": "1fd485b2ffd54f409a5ecd42cba11401", "internalurl": "http://host-3:8774/v1.1/$(tenant_id)s", "publicurl": "http://host-3:8774/v1.1/$(tenant_id)s", } } self.stub_url(httpretty.POST, ['endpoints'], json=resp_body) endpoint = self.client.endpoints.create( region=req_body['endpoint']['region'], publicurl=req_body['endpoint']['publicurl'], adminurl=req_body['endpoint']['adminurl'], internalurl=req_body['endpoint']['internalurl'], service_id=req_body['endpoint']['service_id'] ) self.assertIsInstance(endpoint, endpoints.Endpoint) self.assertRequestBodyIs(json=req_body) @httpretty.activate def test_delete(self): self.stub_url(httpretty.DELETE, ['endpoints', '8f953'], status=204) self.client.endpoints.delete('8f953') @httpretty.activate def test_list(self): self.stub_url(httpretty.GET, ['endpoints'], json=self.TEST_ENDPOINTS) endpoint_list = self.client.endpoints.list() [self.assertIsInstance(r, endpoints.Endpoint) for r in endpoint_list] python-keystoneclient-0.7.1/keystoneclient/tests/v2_0/test_tokens.py0000664000175400017540000000147412315030451027120 0ustar jenkinsjenkins00000000000000# 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 httpretty from keystoneclient.tests.v2_0 import utils class TokenTests(utils.TestCase): @httpretty.activate def test_delete(self): self.stub_url(httpretty.DELETE, ['tokens', '1'], status=204) self.client.tokens.delete(1) python-keystoneclient-0.7.1/keystoneclient/tests/v2_0/test_tenants.py0000664000175400017540000002376412315030451027277 0ustar jenkinsjenkins00000000000000# 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 httpretty from keystoneclient import exceptions from keystoneclient.tests.v2_0 import utils from keystoneclient.v2_0 import tenants class TenantTests(utils.TestCase): def setUp(self): super(TenantTests, self).setUp() self.TEST_TENANTS = { "tenants": { "values": [ { "enabled": True, "description": "A description change!", "name": "invisible_to_admin", "id": 3, }, { "enabled": True, "description": "None", "name": "demo", "id": 2, }, { "enabled": True, "description": "None", "name": "admin", "id": 1, }, { "extravalue01": "metadata01", "enabled": True, "description": "For testing extras", "name": "test_extras", "id": 4, } ], "links": [], }, } @httpretty.activate def test_create(self): req_body = { "tenant": { "name": "tenantX", "description": "Like tenant 9, but better.", "enabled": True, "extravalue01": "metadata01", }, } resp_body = { "tenant": { "name": "tenantX", "enabled": True, "id": 4, "description": "Like tenant 9, but better.", "extravalue01": "metadata01", } } self.stub_url(httpretty.POST, ['tenants'], json=resp_body) tenant = self.client.tenants.create( req_body['tenant']['name'], req_body['tenant']['description'], req_body['tenant']['enabled'], extravalue01=req_body['tenant']['extravalue01'], name="dont overwrite priors") self.assertIsInstance(tenant, tenants.Tenant) self.assertEqual(tenant.id, 4) self.assertEqual(tenant.name, "tenantX") self.assertEqual(tenant.description, "Like tenant 9, but better.") self.assertEqual(tenant.extravalue01, "metadata01") self.assertRequestBodyIs(json=req_body) @httpretty.activate def test_duplicate_create(self): req_body = { "tenant": { "name": "tenantX", "description": "The duplicate tenant.", "enabled": True }, } resp_body = { "error": { "message": "Conflict occurred attempting to store project.", "code": 409, "title": "Conflict", } } self.stub_url(httpretty.POST, ['tenants'], status=409, json=resp_body) def create_duplicate_tenant(): self.client.tenants.create(req_body['tenant']['name'], req_body['tenant']['description'], req_body['tenant']['enabled']) self.assertRaises(exceptions.Conflict, create_duplicate_tenant) @httpretty.activate def test_delete(self): self.stub_url(httpretty.DELETE, ['tenants', '1'], status=204) self.client.tenants.delete(1) @httpretty.activate def test_get(self): resp = {'tenant': self.TEST_TENANTS['tenants']['values'][2]} self.stub_url(httpretty.GET, ['tenants', '1'], json=resp) t = self.client.tenants.get(1) self.assertIsInstance(t, tenants.Tenant) self.assertEqual(t.id, 1) self.assertEqual(t.name, 'admin') @httpretty.activate def test_list(self): self.stub_url(httpretty.GET, ['tenants'], json=self.TEST_TENANTS) tenant_list = self.client.tenants.list() [self.assertIsInstance(t, tenants.Tenant) for t in tenant_list] @httpretty.activate def test_list_limit(self): self.stub_url(httpretty.GET, ['tenants'], json=self.TEST_TENANTS) tenant_list = self.client.tenants.list(limit=1) self.assertQueryStringIs('limit=1') [self.assertIsInstance(t, tenants.Tenant) for t in tenant_list] @httpretty.activate def test_list_marker(self): self.stub_url(httpretty.GET, ['tenants'], json=self.TEST_TENANTS) tenant_list = self.client.tenants.list(marker=1) self.assertQueryStringIs('marker=1') [self.assertIsInstance(t, tenants.Tenant) for t in tenant_list] @httpretty.activate def test_list_limit_marker(self): self.stub_url(httpretty.GET, ['tenants'], json=self.TEST_TENANTS) tenant_list = self.client.tenants.list(limit=1, marker=1) self.assertQueryStringIs('marker=1&limit=1') [self.assertIsInstance(t, tenants.Tenant) for t in tenant_list] @httpretty.activate def test_update(self): req_body = { "tenant": { "id": 4, "name": "tenantX", "description": "I changed you!", "enabled": False, "extravalue01": "metadataChanged", #"extraname": "dontoverwrite!", }, } resp_body = { "tenant": { "name": "tenantX", "enabled": False, "id": 4, "description": "I changed you!", "extravalue01": "metadataChanged", }, } self.stub_url(httpretty.POST, ['tenants', '4'], json=resp_body) tenant = self.client.tenants.update( req_body['tenant']['id'], req_body['tenant']['name'], req_body['tenant']['description'], req_body['tenant']['enabled'], extravalue01=req_body['tenant']['extravalue01'], name="dont overwrite priors") self.assertIsInstance(tenant, tenants.Tenant) self.assertRequestBodyIs(json=req_body) self.assertEqual(tenant.id, 4) self.assertEqual(tenant.name, "tenantX") self.assertEqual(tenant.description, "I changed you!") self.assertFalse(tenant.enabled) self.assertEqual(tenant.extravalue01, "metadataChanged") @httpretty.activate def test_update_empty_description(self): req_body = { "tenant": { "id": 4, "name": "tenantX", "description": "", "enabled": False, }, } resp_body = { "tenant": { "name": "tenantX", "enabled": False, "id": 4, "description": "", }, } self.stub_url(httpretty.POST, ['tenants', '4'], json=resp_body) tenant = self.client.tenants.update(req_body['tenant']['id'], req_body['tenant']['name'], req_body['tenant']['description'], req_body['tenant']['enabled']) self.assertIsInstance(tenant, tenants.Tenant) self.assertRequestBodyIs(json=req_body) self.assertEqual(tenant.id, 4) self.assertEqual(tenant.name, "tenantX") self.assertEqual(tenant.description, "") self.assertFalse(tenant.enabled) @httpretty.activate def test_add_user(self): self.stub_url(httpretty.PUT, ['tenants', '4', 'users', 'foo', 'roles', 'OS-KSADM', 'barrr'], status=204) self.client.tenants.add_user('4', 'foo', 'barrr') @httpretty.activate def test_remove_user(self): self.stub_url(httpretty.DELETE, ['tenants', '4', 'users', 'foo', 'roles', 'OS-KSADM', 'barrr'], status=204) self.client.tenants.remove_user('4', 'foo', 'barrr') @httpretty.activate def test_tenant_add_user(self): self.stub_url(httpretty.PUT, ['tenants', '4', 'users', 'foo', 'roles', 'OS-KSADM', 'barrr'], status=204) req_body = { "tenant": { "id": 4, "name": "tenantX", "description": "I changed you!", "enabled": False, }, } # make tenant object with manager tenant = self.client.tenants.resource_class(self.client.tenants, req_body['tenant']) tenant.add_user('foo', 'barrr') self.assertIsInstance(tenant, tenants.Tenant) @httpretty.activate def test_tenant_remove_user(self): self.stub_url(httpretty.DELETE, ['tenants', '4', 'users', 'foo', 'roles', 'OS-KSADM', 'barrr'], status=204) req_body = { "tenant": { "id": 4, "name": "tenantX", "description": "I changed you!", "enabled": False, }, } # make tenant object with manager tenant = self.client.tenants.resource_class(self.client.tenants, req_body['tenant']) tenant.remove_user('foo', 'barrr') self.assertIsInstance(tenant, tenants.Tenant) python-keystoneclient-0.7.1/keystoneclient/tests/v2_0/test_auth.py0000664000175400017540000002557312315030450026563 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import datetime import json import httpretty import six from keystoneclient import exceptions from keystoneclient.openstack.common import jsonutils from keystoneclient.openstack.common import timeutils from keystoneclient.tests.v2_0 import utils from keystoneclient.v2_0 import client class AuthenticateAgainstKeystoneTests(utils.TestCase): def setUp(self): super(AuthenticateAgainstKeystoneTests, self).setUp() self.TEST_RESPONSE_DICT = { "access": { "token": { "expires": "2020-01-01T00:00:10.000123Z", "id": self.TEST_TOKEN, "tenant": { "id": self.TEST_TENANT_ID }, }, "user": { "id": self.TEST_USER }, "serviceCatalog": self.TEST_SERVICE_CATALOG, }, } self.TEST_REQUEST_BODY = { "auth": { "passwordCredentials": { "username": self.TEST_USER, "password": self.TEST_TOKEN, }, "tenantId": self.TEST_TENANT_ID, }, } @httpretty.activate def test_authenticate_success_expired(self): # Build an expired token self.TEST_RESPONSE_DICT['access']['token']['expires'] = \ (timeutils.utcnow() - datetime.timedelta(1)).isoformat() exp_resp = httpretty.Response(body=json.dumps(self.TEST_RESPONSE_DICT), content_type='application/json') # Build a new response TEST_TOKEN = "abcdef" self.TEST_RESPONSE_DICT['access']['token']['expires'] = \ '2020-01-01T00:00:10.000123Z' self.TEST_RESPONSE_DICT['access']['token']['id'] = TEST_TOKEN new_resp = httpretty.Response(body=json.dumps(self.TEST_RESPONSE_DICT), content_type='application/json') # return expired first, and then the new response self.stub_auth(responses=[exp_resp, new_resp]) cs = client.Client(tenant_id=self.TEST_TENANT_ID, auth_url=self.TEST_URL, username=self.TEST_USER, password=self.TEST_TOKEN) self.assertEqual(cs.management_url, self.TEST_RESPONSE_DICT["access"]["serviceCatalog"][3] ['endpoints'][0]["adminURL"]) self.assertEqual(cs.auth_token, TEST_TOKEN) self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY) @httpretty.activate def test_authenticate_failure(self): _auth = 'auth' _cred = 'passwordCredentials' _pass = 'password' self.TEST_REQUEST_BODY[_auth][_cred][_pass] = 'bad_key' error = {"unauthorized": {"message": "Unauthorized", "code": "401"}} self.stub_auth(status=401, json=error) # Workaround for issue with assertRaises on python2.6 # where with assertRaises(exceptions.Unauthorized): doesn't work # right def client_create_wrapper(): client.Client(username=self.TEST_USER, password="bad_key", tenant_id=self.TEST_TENANT_ID, auth_url=self.TEST_URL) self.assertRaises(exceptions.Unauthorized, client_create_wrapper) self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY) @httpretty.activate def test_auth_redirect(self): self.stub_auth(status=305, body='Use Proxy', location=self.TEST_ADMIN_URL + "/tokens") self.stub_auth(base_url=self.TEST_ADMIN_URL, json=self.TEST_RESPONSE_DICT) cs = client.Client(username=self.TEST_USER, password=self.TEST_TOKEN, tenant_id=self.TEST_TENANT_ID, auth_url=self.TEST_URL) self.assertEqual(cs.management_url, self.TEST_RESPONSE_DICT["access"]["serviceCatalog"][3] ['endpoints'][0]["adminURL"]) self.assertEqual(cs.auth_token, self.TEST_RESPONSE_DICT["access"]["token"]["id"]) self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY) @httpretty.activate def test_authenticate_success_password_scoped(self): self.stub_auth(json=self.TEST_RESPONSE_DICT) cs = client.Client(username=self.TEST_USER, password=self.TEST_TOKEN, tenant_id=self.TEST_TENANT_ID, auth_url=self.TEST_URL) self.assertEqual(cs.management_url, self.TEST_RESPONSE_DICT["access"]["serviceCatalog"][3] ['endpoints'][0]["adminURL"]) self.assertEqual(cs.auth_token, self.TEST_RESPONSE_DICT["access"]["token"]["id"]) self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY) @httpretty.activate def test_authenticate_success_password_unscoped(self): del self.TEST_RESPONSE_DICT['access']['serviceCatalog'] del self.TEST_REQUEST_BODY['auth']['tenantId'] self.stub_auth(json=self.TEST_RESPONSE_DICT) cs = client.Client(username=self.TEST_USER, password=self.TEST_TOKEN, auth_url=self.TEST_URL) self.assertEqual(cs.auth_token, self.TEST_RESPONSE_DICT["access"]["token"]["id"]) self.assertFalse('serviceCatalog' in cs.service_catalog.catalog) self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY) @httpretty.activate def test_auth_url_token_authentication(self): fake_token = 'fake_token' fake_url = '/fake-url' fake_resp = {'result': True} self.stub_auth(json=self.TEST_RESPONSE_DICT) self.stub_url('GET', [fake_url], json=fake_resp, base_url=self.TEST_ADMIN_IDENTITY_ENDPOINT) cl = client.Client(auth_url=self.TEST_URL, token=fake_token) body = httpretty.last_request().body if six.PY3: body = body.decode('utf-8') body = jsonutils.loads(body) self.assertEqual(body['auth']['token']['id'], fake_token) resp, body = cl.get(fake_url) self.assertEqual(fake_resp, body) self.assertEqual(httpretty.last_request().headers.get('X-Auth-Token'), self.TEST_TOKEN) @httpretty.activate def test_authenticate_success_token_scoped(self): del self.TEST_REQUEST_BODY['auth']['passwordCredentials'] self.TEST_REQUEST_BODY['auth']['token'] = {'id': self.TEST_TOKEN} self.stub_auth(json=self.TEST_RESPONSE_DICT) cs = client.Client(token=self.TEST_TOKEN, tenant_id=self.TEST_TENANT_ID, auth_url=self.TEST_URL) self.assertEqual(cs.management_url, self.TEST_RESPONSE_DICT["access"]["serviceCatalog"][3] ['endpoints'][0]["adminURL"]) self.assertEqual(cs.auth_token, self.TEST_RESPONSE_DICT["access"]["token"]["id"]) self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY) @httpretty.activate def test_authenticate_success_token_scoped_trust(self): del self.TEST_REQUEST_BODY['auth']['passwordCredentials'] self.TEST_REQUEST_BODY['auth']['token'] = {'id': self.TEST_TOKEN} self.TEST_REQUEST_BODY['auth']['trust_id'] = self.TEST_TRUST_ID response = self.TEST_RESPONSE_DICT.copy() response['access']['trust'] = {"trustee_user_id": self.TEST_USER, "id": self.TEST_TRUST_ID} self.stub_auth(json=response) cs = client.Client(token=self.TEST_TOKEN, tenant_id=self.TEST_TENANT_ID, trust_id=self.TEST_TRUST_ID, auth_url=self.TEST_URL) self.assertTrue(cs.auth_ref.trust_scoped) self.assertEqual(cs.auth_ref.trust_id, self.TEST_TRUST_ID) self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY) @httpretty.activate def test_authenticate_success_token_unscoped(self): del self.TEST_REQUEST_BODY['auth']['passwordCredentials'] del self.TEST_REQUEST_BODY['auth']['tenantId'] del self.TEST_RESPONSE_DICT['access']['serviceCatalog'] self.TEST_REQUEST_BODY['auth']['token'] = {'id': self.TEST_TOKEN} self.stub_auth(json=self.TEST_RESPONSE_DICT) cs = client.Client(token=self.TEST_TOKEN, auth_url=self.TEST_URL) self.assertEqual(cs.auth_token, self.TEST_RESPONSE_DICT["access"]["token"]["id"]) self.assertFalse('serviceCatalog' in cs.service_catalog.catalog) self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY) @httpretty.activate def test_allow_override_of_auth_token(self): fake_url = '/fake-url' fake_token = 'fake_token' fake_resp = {'result': True} self.stub_auth(json=self.TEST_RESPONSE_DICT) self.stub_url('GET', [fake_url], json=fake_resp, base_url=self.TEST_ADMIN_IDENTITY_ENDPOINT) cl = client.Client(username='exampleuser', password='password', tenant_name='exampleproject', auth_url=self.TEST_URL) self.assertEqual(cl.auth_token, self.TEST_TOKEN) # the token returned from the authentication will be used resp, body = cl.get(fake_url) self.assertEqual(fake_resp, body) self.assertEqual(httpretty.last_request().headers.get('X-Auth-Token'), self.TEST_TOKEN) # then override that token and the new token shall be used cl.auth_token = fake_token resp, body = cl.get(fake_url) self.assertEqual(fake_resp, body) self.assertEqual(httpretty.last_request().headers.get('X-Auth-Token'), fake_token) # if we clear that overriden token then we fall back to the original del cl.auth_token resp, body = cl.get(fake_url) self.assertEqual(fake_resp, body) self.assertEqual(httpretty.last_request().headers.get('X-Auth-Token'), self.TEST_TOKEN) python-keystoneclient-0.7.1/keystoneclient/tests/v2_0/test_users.py0000664000175400017540000001627512315030451026763 0ustar jenkinsjenkins00000000000000# 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 httpretty from keystoneclient.tests.v2_0 import utils from keystoneclient.v2_0 import users class UserTests(utils.TestCase): def setUp(self): super(UserTests, self).setUp() self.TEST_USERS = { "users": { "values": [ { "email": "None", "enabled": True, "id": 1, "name": "admin", }, { "email": "None", "enabled": True, "id": 2, "name": "demo", }, ] } } @httpretty.activate def test_create(self): req_body = { "user": { "name": "gabriel", "password": "test", "tenantId": 2, "email": "test@example.com", "enabled": True, } } resp_body = { "user": { "name": "gabriel", "enabled": True, "tenantId": 2, "id": 3, "password": "test", "email": "test@example.com", } } self.stub_url(httpretty.POST, ['users'], json=resp_body) user = self.client.users.create(req_body['user']['name'], req_body['user']['password'], req_body['user']['email'], tenant_id=req_body['user']['tenantId'], enabled=req_body['user']['enabled']) self.assertIsInstance(user, users.User) self.assertEqual(user.id, 3) self.assertEqual(user.name, "gabriel") self.assertEqual(user.email, "test@example.com") self.assertRequestBodyIs(json=req_body) @httpretty.activate def test_create_user_without_email(self): req_body = { "user": { "name": "gabriel", "password": "test", "tenantId": 2, "enabled": True, "email": None, } } resp_body = { "user": { "name": "gabriel", "enabled": True, "tenantId": 2, "id": 3, "password": "test", } } self.stub_url(httpretty.POST, ['users'], json=resp_body) user = self.client.users.create( req_body['user']['name'], req_body['user']['password'], tenant_id=req_body['user']['tenantId'], enabled=req_body['user']['enabled']) self.assertIsInstance(user, users.User) self.assertEqual(user.id, 3) self.assertEqual(user.name, "gabriel") self.assertRequestBodyIs(json=req_body) @httpretty.activate def test_delete(self): self.stub_url(httpretty.DELETE, ['users', '1'], status=204) self.client.users.delete(1) @httpretty.activate def test_get(self): self.stub_url(httpretty.GET, ['users', '1'], json={'user': self.TEST_USERS['users']['values'][0]}) u = self.client.users.get(1) self.assertIsInstance(u, users.User) self.assertEqual(u.id, 1) self.assertEqual(u.name, 'admin') @httpretty.activate def test_list(self): self.stub_url(httpretty.GET, ['users'], json=self.TEST_USERS) user_list = self.client.users.list() [self.assertIsInstance(u, users.User) for u in user_list] @httpretty.activate def test_list_limit(self): self.stub_url(httpretty.GET, ['users'], json=self.TEST_USERS) user_list = self.client.users.list(limit=1) self.assertEqual(httpretty.last_request().querystring, {'limit': ['1']}) [self.assertIsInstance(u, users.User) for u in user_list] @httpretty.activate def test_list_marker(self): self.stub_url(httpretty.GET, ['users'], json=self.TEST_USERS) user_list = self.client.users.list(marker='foo') self.assertDictEqual(httpretty.last_request().querystring, {'marker': ['foo']}) [self.assertIsInstance(u, users.User) for u in user_list] @httpretty.activate def test_list_limit_marker(self): self.stub_url(httpretty.GET, ['users'], json=self.TEST_USERS) user_list = self.client.users.list(limit=1, marker='foo') self.assertDictEqual(httpretty.last_request().querystring, {'marker': ['foo'], 'limit': ['1']}) [self.assertIsInstance(u, users.User) for u in user_list] @httpretty.activate def test_update(self): req_1 = { "user": { "id": 2, "email": "gabriel@example.com", "name": "gabriel", } } req_2 = { "user": { "id": 2, "password": "swordfish", } } req_3 = { "user": { "id": 2, "tenantId": 1, } } req_4 = { "user": { "id": 2, "enabled": False, } } self.stub_url(httpretty.PUT, ['users', '2'], json=req_1) self.stub_url(httpretty.PUT, ['users', '2', 'OS-KSADM', 'password'], json=req_2) self.stub_url(httpretty.PUT, ['users', '2', 'OS-KSADM', 'tenant'], json=req_3) self.stub_url(httpretty.PUT, ['users', '2', 'OS-KSADM', 'enabled'], json=req_4) self.client.users.update(2, name='gabriel', email='gabriel@example.com') self.assertRequestBodyIs(json=req_1) self.client.users.update_password(2, 'swordfish') self.assertRequestBodyIs(json=req_2) self.client.users.update_tenant(2, 1) self.assertRequestBodyIs(json=req_3) self.client.users.update_enabled(2, False) self.assertRequestBodyIs(json=req_4) @httpretty.activate def test_update_own_password(self): req_body = { 'user': { 'password': 'ABCD', 'original_password': 'DCBA' } } resp_body = { 'access': {} } self.stub_url(httpretty.PATCH, ['OS-KSCRUD', 'users', '123'], json=resp_body) self.client.user_id = '123' self.client.users.update_own_password('DCBA', 'ABCD') self.assertRequestBodyIs(json=req_body) python-keystoneclient-0.7.1/keystoneclient/tests/v2_0/test_service_catalog.py0000664000175400017540000001545512315030451030753 0ustar jenkinsjenkins00000000000000# 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 copy from keystoneclient import access from keystoneclient import exceptions from keystoneclient.tests.v2_0 import client_fixtures from keystoneclient.tests.v2_0 import utils class ServiceCatalogTest(utils.TestCase): def setUp(self): super(ServiceCatalogTest, self).setUp() self.AUTH_RESPONSE_BODY = copy.deepcopy( client_fixtures.AUTH_RESPONSE_BODY) def test_building_a_service_catalog(self): auth_ref = access.AccessInfo.factory(None, self.AUTH_RESPONSE_BODY) sc = auth_ref.service_catalog self.assertEqual(sc.url_for(service_type='compute'), "https://compute.north.host/v1/1234") self.assertEqual(sc.url_for('tenantId', '1', service_type='compute'), "https://compute.north.host/v1/1234") self.assertEqual(sc.url_for('tenantId', '2', service_type='compute'), "https://compute.north.host/v1.1/3456") self.assertRaises(exceptions.EndpointNotFound, sc.url_for, "region", "South", service_type='compute') def test_service_catalog_endpoints(self): auth_ref = access.AccessInfo.factory(None, self.AUTH_RESPONSE_BODY) sc = auth_ref.service_catalog public_ep = sc.get_endpoints(service_type='compute', endpoint_type='publicURL') self.assertEqual(public_ep['compute'][1]['tenantId'], '2') self.assertEqual(public_ep['compute'][1]['versionId'], '1.1') self.assertEqual(public_ep['compute'][1]['internalURL'], "https://compute.north.host/v1.1/3456") def test_service_catalog_regions(self): self.AUTH_RESPONSE_BODY['access']['region_name'] = "North" auth_ref = access.AccessInfo.factory(None, self.AUTH_RESPONSE_BODY) sc = auth_ref.service_catalog url = sc.url_for(service_type='image', endpoint_type='publicURL') self.assertEqual(url, "https://image.north.host/v1/") self.AUTH_RESPONSE_BODY['access']['region_name'] = "South" auth_ref = access.AccessInfo.factory(None, self.AUTH_RESPONSE_BODY) sc = auth_ref.service_catalog url = sc.url_for(service_type='image', endpoint_type='internalURL') self.assertEqual(url, "https://image-internal.south.host/v1/") def test_service_catalog_empty(self): self.AUTH_RESPONSE_BODY['access']['serviceCatalog'] = [] auth_ref = access.AccessInfo.factory(None, self.AUTH_RESPONSE_BODY) self.assertRaises(exceptions.EmptyCatalog, auth_ref.service_catalog.url_for, service_type='image', endpoint_type='internalURL') def test_service_catalog_get_endpoints_region_names(self): auth_ref = access.AccessInfo.factory(None, self.AUTH_RESPONSE_BODY) sc = auth_ref.service_catalog endpoints = sc.get_endpoints(service_type='image', region_name='North') self.assertEqual(len(endpoints), 1) self.assertEqual(endpoints['image'][0]['publicURL'], 'https://image.north.host/v1/') endpoints = sc.get_endpoints(service_type='image', region_name='South') self.assertEqual(len(endpoints), 1) self.assertEqual(endpoints['image'][0]['publicURL'], 'https://image.south.host/v1/') endpoints = sc.get_endpoints(service_type='compute') self.assertEqual(len(endpoints['compute']), 2) endpoints = sc.get_endpoints(service_type='compute', region_name='North') self.assertEqual(len(endpoints['compute']), 2) endpoints = sc.get_endpoints(service_type='compute', region_name='West') self.assertEqual(len(endpoints['compute']), 0) def test_service_catalog_url_for_region_names(self): auth_ref = access.AccessInfo.factory(None, self.AUTH_RESPONSE_BODY) sc = auth_ref.service_catalog url = sc.url_for(service_type='image', region_name='North') self.assertEqual(url, 'https://image.north.host/v1/') url = sc.url_for(service_type='image', region_name='South') self.assertEqual(url, 'https://image.south.host/v1/') url = sc.url_for(service_type='compute', region_name='North', attr='versionId', filter_value='1.1') self.assertEqual(url, 'https://compute.north.host/v1.1/3456') self.assertRaises(exceptions.EndpointNotFound, sc.url_for, service_type='image', region_name='West') def test_servcie_catalog_get_url_region_names(self): auth_ref = access.AccessInfo.factory(None, self.AUTH_RESPONSE_BODY) sc = auth_ref.service_catalog urls = sc.get_urls(service_type='image') self.assertEqual(len(urls), 2) urls = sc.get_urls(service_type='image', region_name='North') self.assertEqual(len(urls), 1) self.assertEqual(urls[0], 'https://image.north.host/v1/') urls = sc.get_urls(service_type='image', region_name='South') self.assertEqual(len(urls), 1) self.assertEqual(urls[0], 'https://image.south.host/v1/') urls = sc.get_urls(service_type='image', region_name='West') self.assertIsNone(urls) def test_service_catalog_param_overrides_body_region(self): self.AUTH_RESPONSE_BODY['access']['region_name'] = "North" auth_ref = access.AccessInfo.factory(None, self.AUTH_RESPONSE_BODY) sc = auth_ref.service_catalog url = sc.url_for(service_type='image') self.assertEqual(url, 'https://image.north.host/v1/') url = sc.url_for(service_type='image', region_name='South') self.assertEqual(url, 'https://image.south.host/v1/') endpoints = sc.get_endpoints(service_type='image') self.assertEqual(len(endpoints['image']), 1) self.assertEqual(endpoints['image'][0]['publicURL'], 'https://image.north.host/v1/') endpoints = sc.get_endpoints(service_type='image', region_name='South') self.assertEqual(len(endpoints['image']), 1) self.assertEqual(endpoints['image'][0]['publicURL'], 'https://image.south.host/v1/') python-keystoneclient-0.7.1/keystoneclient/tests/v2_0/test_ec2.py0000664000175400017540000000755412315030451026273 0ustar jenkinsjenkins00000000000000# 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 httpretty from keystoneclient.tests.v2_0 import utils from keystoneclient.v2_0 import ec2 class EC2Tests(utils.TestCase): @httpretty.activate def test_create(self): user_id = 'usr' tenant_id = 'tnt' req_body = { "tenant_id": tenant_id, } resp_body = { "credential": { "access": "access", "secret": "secret", "tenant_id": tenant_id, "created": "12/12/12", "enabled": True, } } self.stub_url(httpretty.POST, ['users', user_id, 'credentials', 'OS-EC2'], json=resp_body) cred = self.client.ec2.create(user_id, tenant_id) self.assertIsInstance(cred, ec2.EC2) self.assertEqual(cred.tenant_id, tenant_id) self.assertEqual(cred.enabled, True) self.assertEqual(cred.access, 'access') self.assertEqual(cred.secret, 'secret') self.assertRequestBodyIs(json=req_body) @httpretty.activate def test_get(self): user_id = 'usr' tenant_id = 'tnt' resp_body = { "credential": { "access": "access", "secret": "secret", "tenant_id": tenant_id, "created": "12/12/12", "enabled": True, } } self.stub_url(httpretty.GET, ['users', user_id, 'credentials', 'OS-EC2', 'access'], json=resp_body) cred = self.client.ec2.get(user_id, 'access') self.assertIsInstance(cred, ec2.EC2) self.assertEqual(cred.tenant_id, tenant_id) self.assertEqual(cred.enabled, True) self.assertEqual(cred.access, 'access') self.assertEqual(cred.secret, 'secret') @httpretty.activate def test_list(self): user_id = 'usr' tenant_id = 'tnt' resp_body = { "credentials": { "values": [ { "access": "access", "secret": "secret", "tenant_id": tenant_id, "created": "12/12/12", "enabled": True, }, { "access": "another", "secret": "key", "tenant_id": tenant_id, "created": "12/12/31", "enabled": True, } ] } } self.stub_url(httpretty.GET, ['users', user_id, 'credentials', 'OS-EC2'], json=resp_body) creds = self.client.ec2.list(user_id) self.assertEqual(len(creds), 2) cred = creds[0] self.assertIsInstance(cred, ec2.EC2) self.assertEqual(cred.tenant_id, tenant_id) self.assertEqual(cred.enabled, True) self.assertEqual(cred.access, 'access') self.assertEqual(cred.secret, 'secret') @httpretty.activate def test_delete(self): user_id = 'usr' access = 'access' self.stub_url(httpretty.DELETE, ['users', user_id, 'credentials', 'OS-EC2', access], status=204) self.client.ec2.delete(user_id, access) python-keystoneclient-0.7.1/keystoneclient/tests/v2_0/test_access.py0000664000175400017540000001337712315030450027062 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import datetime import testresources from keystoneclient import access from keystoneclient.openstack.common import timeutils from keystoneclient.tests import client_fixtures as token_data from keystoneclient.tests.v2_0 import client_fixtures from keystoneclient.tests.v2_0 import utils UNSCOPED_TOKEN = client_fixtures.UNSCOPED_TOKEN PROJECT_SCOPED_TOKEN = client_fixtures.PROJECT_SCOPED_TOKEN class AccessInfoTest(utils.TestCase, testresources.ResourcedTestCase): resources = [('examples', token_data.EXAMPLES_RESOURCE)] def test_building_unscoped_accessinfo(self): auth_ref = access.AccessInfo.factory(body=UNSCOPED_TOKEN) self.assertTrue(auth_ref) self.assertIn('token', auth_ref) self.assertIn('serviceCatalog', auth_ref) self.assertFalse(auth_ref['serviceCatalog']) self.assertEqual(auth_ref.auth_token, '3e2813b7ba0b4006840c3825860b86ed') self.assertEqual(auth_ref.username, 'exampleuser') self.assertEqual(auth_ref.user_id, 'c4da488862bd435c9e6c0275a0d0e49a') self.assertEqual(auth_ref.role_names, []) self.assertIsNone(auth_ref.tenant_name) self.assertIsNone(auth_ref.tenant_id) self.assertIsNone(auth_ref.auth_url) self.assertIsNone(auth_ref.management_url) self.assertFalse(auth_ref.scoped) self.assertFalse(auth_ref.domain_scoped) self.assertFalse(auth_ref.project_scoped) self.assertFalse(auth_ref.trust_scoped) self.assertIsNone(auth_ref.project_domain_id) self.assertIsNone(auth_ref.project_domain_name) self.assertEqual(auth_ref.user_domain_id, 'default') self.assertEqual(auth_ref.user_domain_name, 'Default') self.assertEqual(auth_ref.expires, timeutils.parse_isotime( UNSCOPED_TOKEN['access']['token']['expires'])) def test_will_expire_soon(self): expires = timeutils.utcnow() + datetime.timedelta(minutes=5) UNSCOPED_TOKEN['access']['token']['expires'] = expires.isoformat() auth_ref = access.AccessInfo.factory(body=UNSCOPED_TOKEN) self.assertFalse(auth_ref.will_expire_soon(stale_duration=120)) self.assertTrue(auth_ref.will_expire_soon(stale_duration=300)) self.assertFalse(auth_ref.will_expire_soon()) def test_building_scoped_accessinfo(self): auth_ref = access.AccessInfo.factory(body=PROJECT_SCOPED_TOKEN) self.assertTrue(auth_ref) self.assertIn('token', auth_ref) self.assertIn('serviceCatalog', auth_ref) self.assertTrue(auth_ref['serviceCatalog']) self.assertEqual(auth_ref.auth_token, '04c7d5ffaeef485f9dc69c06db285bdb') self.assertEqual(auth_ref.username, 'exampleuser') self.assertEqual(auth_ref.user_id, 'c4da488862bd435c9e6c0275a0d0e49a') self.assertEqual(auth_ref.role_names, ['Member']) self.assertEqual(auth_ref.tenant_name, 'exampleproject') self.assertEqual(auth_ref.tenant_id, '225da22d3ce34b15877ea70b2a575f58') self.assertEqual(auth_ref.tenant_name, auth_ref.project_name) self.assertEqual(auth_ref.tenant_id, auth_ref.project_id) self.assertEqual(auth_ref.auth_url, ('http://public.com:5000/v2.0',)) self.assertEqual(auth_ref.management_url, ('http://admin:35357/v2.0',)) self.assertEqual(auth_ref.project_domain_id, 'default') self.assertEqual(auth_ref.project_domain_name, 'Default') self.assertEqual(auth_ref.user_domain_id, 'default') self.assertEqual(auth_ref.user_domain_name, 'Default') self.assertTrue(auth_ref.scoped) self.assertTrue(auth_ref.project_scoped) self.assertFalse(auth_ref.domain_scoped) def test_diablo_token(self): diablo_token = self.examples.TOKEN_RESPONSES[ self.examples.VALID_DIABLO_TOKEN] auth_ref = access.AccessInfo.factory(body=diablo_token) self.assertTrue(auth_ref) self.assertEqual(auth_ref.username, 'user_name1') self.assertEqual(auth_ref.project_id, 'tenant_id1') self.assertEqual(auth_ref.project_name, 'tenant_id1') self.assertEqual(auth_ref.project_domain_id, 'default') self.assertEqual(auth_ref.project_domain_name, 'Default') self.assertEqual(auth_ref.user_domain_id, 'default') self.assertEqual(auth_ref.user_domain_name, 'Default') self.assertEqual(auth_ref.role_names, ['role1', 'role2']) self.assertFalse(auth_ref.scoped) def test_grizzly_token(self): grizzly_token = self.examples.TOKEN_RESPONSES[ self.examples.SIGNED_TOKEN_SCOPED_KEY] auth_ref = access.AccessInfo.factory(body=grizzly_token) self.assertEqual(auth_ref.project_id, 'tenant_id1') self.assertEqual(auth_ref.project_name, 'tenant_name1') self.assertEqual(auth_ref.project_domain_id, 'default') self.assertEqual(auth_ref.project_domain_name, 'Default') self.assertEqual(auth_ref.user_domain_id, 'default') self.assertEqual(auth_ref.user_domain_name, 'Default') self.assertEqual(auth_ref.role_names, ['role1', 'role2']) def load_tests(loader, tests, pattern): return testresources.OptimisingTestSuite(tests) python-keystoneclient-0.7.1/keystoneclient/tests/v2_0/__init__.py0000664000175400017540000000000012315030450026274 0ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/tests/test_http.py0000664000175400017540000001612012315030450026017 0ustar jenkinsjenkins00000000000000# Copyright 2013 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging import httpretty import six from testtools import matchers from keystoneclient import exceptions from keystoneclient import httpclient from keystoneclient import session from keystoneclient.tests import utils RESPONSE_BODY = '{"hi": "there"}' def get_client(): cl = httpclient.HTTPClient(username="username", password="password", tenant_id="tenant", auth_url="auth_test") return cl def get_authed_client(): cl = get_client() cl.management_url = "http://127.0.0.1:5000" cl.auth_token = "token" return cl class FakeLog(object): def __init__(self): self.warn_log = str() self.debug_log = str() def warn(self, msg=None, *args, **kwargs): self.warn_log = "%s\n%s" % (self.warn_log, (msg % args)) def debug(self, msg=None, *args, **kwargs): self.debug_log = "%s\n%s" % (self.debug_log, (msg % args)) class ClientTest(utils.TestCase): TEST_URL = 'http://127.0.0.1:5000/hi' def test_unauthorized_client_requests(self): cl = get_client() self.assertRaises(exceptions.AuthorizationFailure, cl.get, '/hi') self.assertRaises(exceptions.AuthorizationFailure, cl.post, '/hi') self.assertRaises(exceptions.AuthorizationFailure, cl.put, '/hi') self.assertRaises(exceptions.AuthorizationFailure, cl.delete, '/hi') @httpretty.activate def test_get(self): cl = get_authed_client() self.stub_url(httpretty.GET, body=RESPONSE_BODY) resp, body = cl.get("/hi") self.assertEqual(httpretty.last_request().method, 'GET') self.assertEqual(httpretty.last_request().path, '/hi') self.assertRequestHeaderEqual('X-Auth-Token', 'token') self.assertRequestHeaderEqual('User-Agent', httpclient.USER_AGENT) # Automatic JSON parsing self.assertEqual(body, {"hi": "there"}) @httpretty.activate def test_get_error_with_plaintext_resp(self): cl = get_authed_client() self.stub_url(httpretty.GET, status=400, body='Some evil plaintext string') self.assertRaises(exceptions.BadRequest, cl.get, '/hi') @httpretty.activate def test_get_error_with_json_resp(self): cl = get_authed_client() err_response = { "error": { "code": 400, "title": "Error title", "message": "Error message string" } } self.stub_url(httpretty.GET, status=400, json=err_response) exc_raised = False try: cl.get('/hi') except exceptions.BadRequest as exc: exc_raised = True self.assertEqual(exc.message, "Error message string") self.assertTrue(exc_raised, 'Exception not raised.') @httpretty.activate def test_post(self): cl = get_authed_client() self.stub_url(httpretty.POST) cl.post("/hi", body=[1, 2, 3]) self.assertEqual(httpretty.last_request().method, 'POST') self.assertEqual(httpretty.last_request().body, b'[1, 2, 3]') self.assertRequestHeaderEqual('X-Auth-Token', 'token') self.assertRequestHeaderEqual('Content-Type', 'application/json') self.assertRequestHeaderEqual('User-Agent', httpclient.USER_AGENT) @httpretty.activate def test_forwarded_for(self): ORIGINAL_IP = "10.100.100.1" cl = httpclient.HTTPClient(username="username", password="password", tenant_id="tenant", auth_url="auth_test", original_ip=ORIGINAL_IP) self.stub_url(httpretty.GET) cl.request(self.TEST_URL, 'GET') forwarded = "for=%s;by=%s" % (ORIGINAL_IP, httpclient.USER_AGENT) self.assertRequestHeaderEqual('Forwarded', forwarded) def test_client_deprecated(self): # Can resolve symbols from the keystoneclient.client module. # keystoneclient.client was deprecated and renamed to # keystoneclient.httpclient. This tests that keystoneclient.client # can still be used. from keystoneclient import client # These statements will raise an AttributeError if the symbol isn't # defined in the module. client.HTTPClient class BasicRequestTests(utils.TestCase): url = 'http://keystone.test.com/' def setUp(self): super(BasicRequestTests, self).setUp() self.logger_message = six.moves.cStringIO() handler = logging.StreamHandler(self.logger_message) handler.setLevel(logging.DEBUG) self.logger = logging.getLogger(session.__name__) level = self.logger.getEffectiveLevel() self.logger.setLevel(logging.DEBUG) self.logger.addHandler(handler) self.addCleanup(self.logger.removeHandler, handler) self.addCleanup(self.logger.setLevel, level) def request(self, method='GET', response='Test Response', status=200, url=None, **kwargs): if not url: url = self.url httpretty.register_uri(method, url, body=response, status=status) return httpclient.request(url, method, **kwargs) @httpretty.activate def test_basic_params(self): method = 'GET' response = 'Test Response' status = 200 self.request(method=method, status=status, response=response) self.assertEqual(httpretty.last_request().method, method) logger_message = self.logger_message.getvalue() self.assertThat(logger_message, matchers.Contains('curl')) self.assertThat(logger_message, matchers.Contains('-X %s' % method)) self.assertThat(logger_message, matchers.Contains(self.url)) self.assertThat(logger_message, matchers.Contains(str(status))) self.assertThat(logger_message, matchers.Contains(response)) @httpretty.activate def test_headers(self): headers = {'key': 'val', 'test': 'other'} self.request(headers=headers) for k, v in six.iteritems(headers): self.assertRequestHeaderEqual(k, v) for header in six.iteritems(headers): self.assertThat(self.logger_message.getvalue(), matchers.Contains('-H "%s: %s"' % header)) @httpretty.activate def test_body(self): data = "BODY DATA" self.request(response=data) logger_message = self.logger_message.getvalue() self.assertThat(logger_message, matchers.Contains('BODY:')) self.assertThat(logger_message, matchers.Contains(data)) python-keystoneclient-0.7.1/keystoneclient/tests/test_keyring.py0000664000175400017540000001632012315030450026512 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import datetime import mock from keystoneclient import access from keystoneclient import httpclient from keystoneclient.openstack.common import timeutils from keystoneclient.tests import utils from keystoneclient.tests.v2_0 import client_fixtures try: import keyring # noqa import pickle # noqa except ImportError: keyring = None PROJECT_SCOPED_TOKEN = client_fixtures.PROJECT_SCOPED_TOKEN # These mirror values from PROJECT_SCOPED_TOKEN USERNAME = 'exampleuser' AUTH_URL = 'http://public.com:5000/v2.0' TOKEN = '04c7d5ffaeef485f9dc69c06db285bdb' PASSWORD = 'password' TENANT = 'tenant' TENANT_ID = 'tenant_id' class KeyringTest(utils.TestCase): def setUp(self): if keyring is None: self.skipTest( 'optional package keyring or pickle is not installed') class MemoryKeyring(keyring.backend.KeyringBackend): """A Simple testing keyring. This class supports stubbing an initial password to be returned by setting password, and allows easy password and key retrieval. Also records if a password was retrieved. """ def __init__(self): self.key = None self.password = None self.fetched = False self.get_password_called = False self.set_password_called = False def supported(self): return 1 def get_password(self, service, username): self.get_password_called = True key = username + '@' + service # make sure we don't get passwords crossed if one is enforced. if self.key and self.key != key: return None if self.password: self.fetched = True return self.password def set_password(self, service, username, password): self.set_password_called = True self.key = username + '@' + service self.password = password super(KeyringTest, self).setUp() self.memory_keyring = MemoryKeyring() keyring.set_keyring(self.memory_keyring) def test_no_keyring_key(self): """Ensure that if we don't have use_keyring set in the client that the keyring is never accessed. """ cl = httpclient.HTTPClient(username=USERNAME, password=PASSWORD, tenant_id=TENANT_ID, auth_url=AUTH_URL) # stub and check that a new token is received with mock.patch.object(cl, 'get_raw_token_from_identity_service') \ as meth: meth.return_value = (True, PROJECT_SCOPED_TOKEN) self.assertTrue(cl.authenticate()) meth.assert_called_once() # make sure that we never touched the keyring self.assertFalse(self.memory_keyring.get_password_called) self.assertFalse(self.memory_keyring.set_password_called) def test_build_keyring_key(self): cl = httpclient.HTTPClient(username=USERNAME, password=PASSWORD, tenant_id=TENANT_ID, auth_url=AUTH_URL) keyring_key = cl._build_keyring_key(auth_url=AUTH_URL, username=USERNAME, tenant_name=TENANT, tenant_id=TENANT_ID, token=TOKEN) self.assertEqual(keyring_key, '%s/%s/%s/%s/%s' % (AUTH_URL, TENANT_ID, TENANT, TOKEN, USERNAME)) def test_set_and_get_keyring_expired(self): cl = httpclient.HTTPClient(username=USERNAME, password=PASSWORD, tenant_id=TENANT_ID, auth_url=AUTH_URL, use_keyring=True) # set an expired token into the keyring auth_ref = access.AccessInfo.factory(body=PROJECT_SCOPED_TOKEN) expired = timeutils.utcnow() - datetime.timedelta(minutes=30) auth_ref['token']['expires'] = timeutils.isotime(expired) self.memory_keyring.password = pickle.dumps(auth_ref) # stub and check that a new token is received, so not using expired with mock.patch.object(cl, 'get_raw_token_from_identity_service') \ as meth: meth.return_value = (True, PROJECT_SCOPED_TOKEN) self.assertTrue(cl.authenticate()) meth.assert_called_once() # check that a value was returned from the keyring self.assertTrue(self.memory_keyring.fetched) # check that the new token has been loaded into the keyring new_auth_ref = pickle.loads(self.memory_keyring.password) self.assertEqual(new_auth_ref['token']['expires'], PROJECT_SCOPED_TOKEN['access']['token']['expires']) def test_get_keyring(self): cl = httpclient.HTTPClient(username=USERNAME, password=PASSWORD, tenant_id=TENANT_ID, auth_url=AUTH_URL, use_keyring=True) # set an token into the keyring auth_ref = access.AccessInfo.factory(body=PROJECT_SCOPED_TOKEN) future = timeutils.utcnow() + datetime.timedelta(minutes=30) auth_ref['token']['expires'] = timeutils.isotime(future) self.memory_keyring.password = pickle.dumps(auth_ref) # don't stub get_raw_token so will fail if authenticate happens self.assertTrue(cl.authenticate()) self.assertTrue(self.memory_keyring.fetched) def test_set_keyring(self): cl = httpclient.HTTPClient(username=USERNAME, password=PASSWORD, tenant_id=TENANT_ID, auth_url=AUTH_URL, use_keyring=True) # stub and check that a new token is received with mock.patch.object(cl, 'get_raw_token_from_identity_service') \ as meth: meth.return_value = (True, PROJECT_SCOPED_TOKEN) self.assertTrue(cl.authenticate()) meth.assert_called_once() # we checked the keyring, but we didn't find anything self.assertTrue(self.memory_keyring.get_password_called) self.assertFalse(self.memory_keyring.fetched) # check that the new token has been loaded into the keyring self.assertTrue(self.memory_keyring.set_password_called) new_auth_ref = pickle.loads(self.memory_keyring.password) self.assertEqual(new_auth_ref.auth_token, TOKEN) self.assertEqual(new_auth_ref['token'], PROJECT_SCOPED_TOKEN['access']['token']) self.assertEqual(new_auth_ref.username, USERNAME) python-keystoneclient-0.7.1/keystoneclient/tests/test_utils.py0000664000175400017540000001576112315030450026212 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging import sys import six from keystoneclient import exceptions from keystoneclient.tests import utils as test_utils from keystoneclient import utils class FakeResource(object): pass class FakeManager(object): resource_class = FakeResource resources = { '1234': {'name': 'entity_one'}, '8e8ec658-c7b0-4243-bdf8-6f7f2952c0d0': {'name': 'entity_two'}, '\xe3\x82\xbdtest': {'name': u'\u30bdtest'}, '5678': {'name': '9876'} } def get(self, resource_id): try: return self.resources[str(resource_id)] except KeyError: raise exceptions.NotFound(resource_id) def find(self, name=None): if name == '9999': # NOTE(morganfainberg): special case that raises NoUniqueMatch. raise exceptions.NoUniqueMatch() for resource_id, resource in self.resources.items(): if resource['name'] == str(name): return resource raise exceptions.NotFound(name) class FindResourceTestCase(test_utils.TestCase): def setUp(self): super(FindResourceTestCase, self).setUp() self.manager = FakeManager() def test_find_none(self): self.assertRaises(exceptions.CommandError, utils.find_resource, self.manager, 'asdf') def test_find_by_integer_id(self): output = utils.find_resource(self.manager, 1234) self.assertEqual(output, self.manager.resources['1234']) def test_find_by_str_id(self): output = utils.find_resource(self.manager, '1234') self.assertEqual(output, self.manager.resources['1234']) def test_find_by_uuid(self): uuid = '8e8ec658-c7b0-4243-bdf8-6f7f2952c0d0' output = utils.find_resource(self.manager, uuid) self.assertEqual(output, self.manager.resources[uuid]) def test_find_by_unicode(self): name = '\xe3\x82\xbdtest' output = utils.find_resource(self.manager, name) self.assertEqual(output, self.manager.resources[name]) def test_find_by_str_name(self): output = utils.find_resource(self.manager, 'entity_one') self.assertEqual(output, self.manager.resources['1234']) def test_find_by_int_name(self): output = utils.find_resource(self.manager, 9876) self.assertEqual(output, self.manager.resources['5678']) def test_find_no_unique_match(self): self.assertRaises(exceptions.CommandError, utils.find_resource, self.manager, 9999) class FakeObject(object): def __init__(self, name): self.name = name class PrintTestCase(test_utils.TestCase): def setUp(self): super(PrintTestCase, self).setUp() self.old_stdout = sys.stdout self.stdout = six.moves.cStringIO() self.addCleanup(setattr, self, 'stdout', None) sys.stdout = self.stdout self.addCleanup(setattr, sys, 'stdout', self.old_stdout) def test_print_list_unicode(self): name = u'\u540d\u5b57' objs = [FakeObject(name)] # NOTE(Jeffrey4l) If the text's encode is proper, this method will not # raise UnicodeEncodeError exceptions utils.print_list(objs, ['name']) output = self.stdout.getvalue() # In Python 2, output will be bytes, while in Python 3, it will not. # Let's decode the value if needed. if isinstance(output, six.binary_type): output = output.decode('utf-8') self.assertIn(name, output) def test_print_dict_unicode(self): name = u'\u540d\u5b57' utils.print_dict({'name': name}) output = self.stdout.getvalue() # In Python 2, output will be bytes, while in Python 3, it will not. # Let's decode the value if needed. if isinstance(output, six.binary_type): output = output.decode('utf-8') self.assertIn(name, output) class TestPositional(test_utils.TestCase): @utils.positional(1) def no_vars(self): # positional doesn't enforce anything here return True @utils.positional(3, utils.positional.EXCEPT) def mixed_except(self, arg, kwarg1=None, kwarg2=None): # self, arg, and kwarg1 may be passed positionally return (arg, kwarg1, kwarg2) @utils.positional(3, utils.positional.WARN) def mixed_warn(self, arg, kwarg1=None, kwarg2=None): # self, arg, and kwarg1 may be passed positionally, only a warning # is emitted return (arg, kwarg1, kwarg2) def test_nothing(self): self.assertTrue(self.no_vars()) def test_mixed_except(self): self.assertEqual((1, 2, 3), self.mixed_except(1, 2, kwarg2=3)) self.assertEqual((1, 2, 3), self.mixed_except(1, kwarg1=2, kwarg2=3)) self.assertEqual((1, None, None), self.mixed_except(1)) self.assertRaises(TypeError, self.mixed_except, 1, 2, 3) def test_mixed_warn(self): logger_message = six.moves.cStringIO() handler = logging.StreamHandler(logger_message) handler.setLevel(logging.DEBUG) logger = logging.getLogger(utils.__name__) level = logger.getEffectiveLevel() logger.setLevel(logging.DEBUG) logger.addHandler(handler) self.addCleanup(logger.removeHandler, handler) self.addCleanup(logger.setLevel, level) self.mixed_warn(1, 2, 3) self.assertIn('takes at most 3 positional', logger_message.getvalue()) @utils.positional(enforcement=utils.positional.EXCEPT) def inspect_func(self, arg, kwarg=None): return (arg, kwarg) def test_inspect_positions(self): self.assertEqual((1, None), self.inspect_func(1)) self.assertEqual((1, 2), self.inspect_func(1, kwarg=2)) self.assertRaises(TypeError, self.inspect_func) self.assertRaises(TypeError, self.inspect_func, 1, 2) @utils.positional.classmethod(1) def class_method(cls, a, b): return (cls, a, b) @utils.positional.method(1) def normal_method(self, a, b): self.assertIsInstance(self, TestPositional) return (self, a, b) def test_class_method(self): self.assertEqual((TestPositional, 1, 2), self.class_method(1, b=2)) self.assertRaises(TypeError, self.class_method, 1, 2) def test_normal_method(self): self.assertEqual((self, 1, 2), self.normal_method(1, b=2)) self.assertRaises(TypeError, self.normal_method, 1, 2) python-keystoneclient-0.7.1/keystoneclient/tests/generic/0000775000175400017540000000000012315030547025052 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/tests/generic/test_shell.py0000664000175400017540000001155312315030450027570 0ustar jenkinsjenkins00000000000000# Copyright 2014 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from six import moves from keystoneclient.generic import shell from keystoneclient.tests import utils class DoDiscoverTest(utils.TestCase): """Unit tests for do_discover function.""" foo_version = { 'id': 'foo_id', 'status': 'foo_status', 'url': 'http://foo/url', } bar_version = { 'id': 'bar_id', 'status': 'bar_status', 'url': 'http://bar/url', } foo_extension = { 'foo': 'foo_extension', 'message': 'extension_message', 'bar': 'bar_extension', } stub_message = 'This is a stub message' def setUp(self): super(DoDiscoverTest, self).setUp() self.client_mock = mock.Mock() self.client_mock.discover.return_value = {} def _execute_discover(self): """Call do_discover function and capture output :return: captured output is returned """ with mock.patch('sys.stdout', new_callable=moves.StringIO) as mock_stdout: shell.do_discover(self.client_mock, args=None) output = mock_stdout.getvalue() return output def _check_version_print(self, output, version): """Checks all api version's parameters are present in output.""" self.assertIn(version['id'], output) self.assertIn(version['status'], output) self.assertIn(version['url'], output) def test_no_keystones(self): # No servers configured for client, # corresponding message should be printed output = self._execute_discover() self.assertIn('No Keystone-compatible endpoint found', output) def test_endpoint(self): # Endpoint is configured for client, # client's discover method should be called with that value self.client_mock.endpoint = 'Some non-empty value' shell.do_discover(self.client_mock, args=None) self.client_mock.discover.assert_called_with(self.client_mock.endpoint) def test_auth_url(self): # No endpoint provided for client, but there is an auth_url # client's discover method should be called with auth_url value self.client_mock.endpoint = False self.client_mock.auth_url = 'Some non-empty value' shell.do_discover(self.client_mock, args=None) self.client_mock.discover.assert_called_with(self.client_mock.auth_url) def test_empty(self): # No endpoint or auth_url is configured for client. # client.discover() should be called without parameters self.client_mock.endpoint = False self.client_mock.auth_url = False shell.do_discover(self.client_mock, args=None) self.client_mock.discover.assert_called_with() def test_message(self): # If client.discover() result contains message - it should be printed self.client_mock.discover.return_value = {'message': self.stub_message} output = self._execute_discover() self.assertIn(self.stub_message, output) def test_versions(self): # Every version in client.discover() result should be printed # and client.discover_extension() should be called on its url self.client_mock.discover.return_value = { 'foo': self.foo_version, 'bar': self.bar_version, } self.client_mock.discover_extensions.return_value = {} output = self._execute_discover() self._check_version_print(output, self.foo_version) self._check_version_print(output, self.bar_version) discover_extension_calls = [ mock.call(self.foo_version['url']), mock.call(self.bar_version['url']), ] self.client_mock.discover_extensions.assert_has_calls( discover_extension_calls, any_order=True) def test_extensions(self): # Every extension's parameters should be printed # Extension's message should be omitted self.client_mock.discover.return_value = {'foo': self.foo_version} self.client_mock.discover_extensions.return_value = self.foo_extension output = self._execute_discover() self.assertIn(self.foo_extension['foo'], output) self.assertIn(self.foo_extension['bar'], output) self.assertNotIn(self.foo_extension['message'], output) python-keystoneclient-0.7.1/keystoneclient/tests/generic/test_client.py0000664000175400017540000000522712315030450027740 0ustar jenkinsjenkins00000000000000# Copyright 2014 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import httpretty from keystoneclient.generic import client from keystoneclient.openstack.common import jsonutils from keystoneclient.tests import utils BASE_HOST = 'http://keystone.example.com' BASE_URL = "%s:5000/" % BASE_HOST V2_URL = "%sv2.0" % BASE_URL EXTENSION_NAMESPACE = "http://docs.openstack.org/identity/api/ext/OS-FAKE/v1.0" EXTENSION_DESCRIBED = {"href": "https://github.com/openstack/identity-api", "rel": "describedby", "type": "text/html"} EXTENSION_ALIAS_FOO = "OS-FAKE-FOO" EXTENSION_NAME_FOO = "OpenStack Keystone Fake Extension Foo" EXTENSION_FOO = {"alias": EXTENSION_ALIAS_FOO, "description": "Fake Foo extension to V2.0 API.", "links": [EXTENSION_DESCRIBED], "name": EXTENSION_NAME_FOO, "namespace": EXTENSION_NAMESPACE, "updated": '2014-01-08T00:00:00Z'} EXTENSION_ALIAS_BAR = "OS-FAKE-BAR" EXTENSION_NAME_BAR = "OpenStack Keystone Fake Extension Bar" EXTENSION_BAR = {"alias": EXTENSION_ALIAS_BAR, "description": "Fake Bar extension to V2.0 API.", "links": [EXTENSION_DESCRIBED], "name": EXTENSION_NAME_BAR, "namespace": EXTENSION_NAMESPACE, "updated": '2014-01-08T00:00:00Z'} def _create_extension_list(extensions): return jsonutils.dumps({'extensions': {'values': extensions}}) EXTENSION_LIST = _create_extension_list([EXTENSION_FOO, EXTENSION_BAR]) @httpretty.activate class ClientDiscoveryTests(utils.TestCase): def test_discover_extensions_v2(self): httpretty.register_uri(httpretty.GET, "%s/extensions" % V2_URL, body=EXTENSION_LIST) extensions = client.Client().discover_extensions(url=V2_URL) self.assertIn(EXTENSION_ALIAS_FOO, extensions) self.assertEqual(extensions[EXTENSION_ALIAS_FOO], EXTENSION_NAME_FOO) self.assertIn(EXTENSION_ALIAS_BAR, extensions) self.assertEqual(extensions[EXTENSION_ALIAS_BAR], EXTENSION_NAME_BAR) python-keystoneclient-0.7.1/keystoneclient/tests/generic/__init__.py0000664000175400017540000000000012315030450027142 0ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/tests/test_session.py0000664000175400017540000003333212315030450026527 0ustar jenkinsjenkins00000000000000# 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 httpretty import mock import requests import six from keystoneclient.auth import base from keystoneclient import exceptions from keystoneclient import session as client_session from keystoneclient.tests import utils class SessionTests(utils.TestCase): TEST_URL = 'http://127.0.0.1:5000/' @httpretty.activate def test_get(self): session = client_session.Session() self.stub_url(httpretty.GET, body='response') resp = session.get(self.TEST_URL) self.assertEqual(httpretty.GET, httpretty.last_request().method) self.assertEqual(resp.text, 'response') self.assertTrue(resp.ok) @httpretty.activate def test_post(self): session = client_session.Session() self.stub_url(httpretty.POST, body='response') resp = session.post(self.TEST_URL, json={'hello': 'world'}) self.assertEqual(httpretty.POST, httpretty.last_request().method) self.assertEqual(resp.text, 'response') self.assertTrue(resp.ok) self.assertRequestBodyIs(json={'hello': 'world'}) @httpretty.activate def test_head(self): session = client_session.Session() self.stub_url(httpretty.HEAD) resp = session.head(self.TEST_URL) self.assertEqual(httpretty.HEAD, httpretty.last_request().method) self.assertTrue(resp.ok) self.assertRequestBodyIs('') @httpretty.activate def test_put(self): session = client_session.Session() self.stub_url(httpretty.PUT, body='response') resp = session.put(self.TEST_URL, json={'hello': 'world'}) self.assertEqual(httpretty.PUT, httpretty.last_request().method) self.assertEqual(resp.text, 'response') self.assertTrue(resp.ok) self.assertRequestBodyIs(json={'hello': 'world'}) @httpretty.activate def test_delete(self): session = client_session.Session() self.stub_url(httpretty.DELETE, body='response') resp = session.delete(self.TEST_URL) self.assertEqual(httpretty.DELETE, httpretty.last_request().method) self.assertTrue(resp.ok) self.assertEqual(resp.text, 'response') @httpretty.activate def test_patch(self): session = client_session.Session() self.stub_url(httpretty.PATCH, body='response') resp = session.patch(self.TEST_URL, json={'hello': 'world'}) self.assertEqual(httpretty.PATCH, httpretty.last_request().method) self.assertTrue(resp.ok) self.assertEqual(resp.text, 'response') self.assertRequestBodyIs(json={'hello': 'world'}) @httpretty.activate def test_user_agent(self): session = client_session.Session(user_agent='test-agent') self.stub_url(httpretty.GET, body='response') resp = session.get(self.TEST_URL) self.assertTrue(resp.ok) self.assertRequestHeaderEqual('User-Agent', 'test-agent') resp = session.get(self.TEST_URL, headers={'User-Agent': 'new-agent'}) self.assertTrue(resp.ok) self.assertRequestHeaderEqual('User-Agent', 'new-agent') resp = session.get(self.TEST_URL, headers={'User-Agent': 'new-agent'}, user_agent='overrides-agent') self.assertTrue(resp.ok) self.assertRequestHeaderEqual('User-Agent', 'overrides-agent') @httpretty.activate def test_http_session_opts(self): session = client_session.Session(cert='cert.pem', timeout=5, verify='certs') FAKE_RESP = utils.TestResponse({'status_code': 200, 'text': 'resp'}) RESP = mock.Mock(return_value=FAKE_RESP) with mock.patch.object(session.session, 'request', RESP) as mocked: session.post(self.TEST_URL, data='value') mock_args, mock_kwargs = mocked.call_args self.assertEqual(mock_args[0], 'POST') self.assertEqual(mock_args[1], self.TEST_URL) self.assertEqual(mock_kwargs['data'], 'value') self.assertEqual(mock_kwargs['cert'], 'cert.pem') self.assertEqual(mock_kwargs['verify'], 'certs') self.assertEqual(mock_kwargs['timeout'], 5) @httpretty.activate def test_not_found(self): session = client_session.Session() self.stub_url(httpretty.GET, status=404) self.assertRaises(exceptions.NotFound, session.get, self.TEST_URL) @httpretty.activate def test_server_error(self): session = client_session.Session() self.stub_url(httpretty.GET, status=500) self.assertRaises(exceptions.InternalServerError, session.get, self.TEST_URL) @httpretty.activate def test_session_debug_output(self): session = client_session.Session(verify=False) headers = {'HEADERA': 'HEADERVALB'} body = 'BODYRESPONSE' data = 'BODYDATA' self.stub_url(httpretty.POST, body=body) session.post(self.TEST_URL, headers=headers, data=data) self.assertIn('curl', self.logger.output) self.assertIn('POST', self.logger.output) self.assertIn('--insecure', self.logger.output) self.assertIn(body, self.logger.output) self.assertIn("'%s'" % data, self.logger.output) for k, v in six.iteritems(headers): self.assertIn(k, self.logger.output) self.assertIn(v, self.logger.output) class RedirectTests(utils.TestCase): REDIRECT_CHAIN = ['http://myhost:3445/', 'http://anotherhost:6555/', 'http://thirdhost/', 'http://finaldestination:55/'] DEFAULT_REDIRECT_BODY = 'Redirect' DEFAULT_RESP_BODY = 'Found' def setup_redirects(self, method=httpretty.GET, status=305, redirect_kwargs={}, final_kwargs={}): redirect_kwargs.setdefault('body', self.DEFAULT_REDIRECT_BODY) for s, d in zip(self.REDIRECT_CHAIN, self.REDIRECT_CHAIN[1:]): httpretty.register_uri(method, s, status=status, location=d, **redirect_kwargs) final_kwargs.setdefault('status', 200) final_kwargs.setdefault('body', self.DEFAULT_RESP_BODY) httpretty.register_uri(method, self.REDIRECT_CHAIN[-1], **final_kwargs) def assertResponse(self, resp): self.assertEqual(resp.status_code, 200) self.assertEqual(resp.text, self.DEFAULT_RESP_BODY) @httpretty.activate def test_basic_get(self): session = client_session.Session() self.setup_redirects() resp = session.get(self.REDIRECT_CHAIN[-2]) self.assertResponse(resp) @httpretty.activate def test_basic_post_keeps_correct_method(self): session = client_session.Session() self.setup_redirects(method=httpretty.POST, status=301) resp = session.post(self.REDIRECT_CHAIN[-2]) self.assertResponse(resp) @httpretty.activate def test_redirect_forever(self): session = client_session.Session(redirect=True) self.setup_redirects() resp = session.get(self.REDIRECT_CHAIN[0]) self.assertResponse(resp) self.assertTrue(len(resp.history), len(self.REDIRECT_CHAIN)) @httpretty.activate def test_no_redirect(self): session = client_session.Session(redirect=False) self.setup_redirects() resp = session.get(self.REDIRECT_CHAIN[0]) self.assertEqual(resp.status_code, 305) self.assertEqual(resp.url, self.REDIRECT_CHAIN[0]) @httpretty.activate def test_redirect_limit(self): self.setup_redirects() for i in (1, 2): session = client_session.Session(redirect=i) resp = session.get(self.REDIRECT_CHAIN[0]) self.assertEqual(resp.status_code, 305) self.assertEqual(resp.url, self.REDIRECT_CHAIN[i]) self.assertEqual(resp.text, self.DEFAULT_REDIRECT_BODY) @httpretty.activate def test_history_matches_requests(self): self.setup_redirects(status=301) session = client_session.Session(redirect=True) req_resp = requests.get(self.REDIRECT_CHAIN[0], allow_redirects=True) ses_resp = session.get(self.REDIRECT_CHAIN[0]) self.assertEqual(type(req_resp.history), type(ses_resp.history)) self.assertEqual(len(req_resp.history), len(ses_resp.history)) for r, s in zip(req_resp.history, ses_resp.history): self.assertEqual(r.url, s.url) self.assertEqual(r.status_code, s.status_code) class ConstructSessionFromArgsTests(utils.TestCase): KEY = 'keyfile' CERT = 'certfile' CACERT = 'cacert-path' def _s(self, k=None, **kwargs): k = k or kwargs return client_session.Session.construct(k) def test_verify(self): self.assertFalse(self._s(insecure=True).verify) self.assertTrue(self._s(verify=True, insecure=True).verify) self.assertFalse(self._s(verify=False, insecure=True).verify) self.assertEqual(self._s(cacert=self.CACERT).verify, self.CACERT) def test_cert(self): tup = (self.CERT, self.KEY) self.assertEqual(self._s(cert=tup).cert, tup) self.assertEqual(self._s(cert=self.CERT, key=self.KEY).cert, tup) self.assertIsNone(self._s(key=self.KEY).cert) def test_pass_through(self): value = 42 # only a number because timeout needs to be for key in ['timeout', 'session', 'original_ip', 'user_agent']: args = {key: value} self.assertEqual(getattr(self._s(args), key), value) self.assertNotIn(key, args) class AuthPlugin(base.BaseAuthPlugin): """Very simple debug authentication plugin. Takes Parameters such that it can throw exceptions at the right times. """ TEST_TOKEN = 'aToken' SERVICE_URLS = { 'identity': {'public': 'http://identity-public:1111/v2.0', 'admin': 'http://identity-admin:1111/v2.0'}, 'compute': {'public': 'http://compute-public:2222/v1.0', 'admin': 'http://compute-admin:2222/v1.0'}, 'image': {'public': 'http://image-public:3333/v2.0', 'admin': 'http://image-admin:3333/v2.0'} } def __init__(self, token=TEST_TOKEN): self.token = token def get_token(self, session): return self.token def get_endpoint(self, session, service_type=None, interface=None, **kwargs): try: return self.SERVICE_URLS[service_type][interface] except (KeyError, AttributeError): return None class SessionAuthTests(utils.TestCase): TEST_URL = 'http://127.0.0.1:5000/' TEST_JSON = {'hello': 'world'} def stub_service_url(self, service_type, interface, path, method=httpretty.GET, **kwargs): base_url = AuthPlugin.SERVICE_URLS[service_type][interface] uri = "%s/%s" % (base_url.rstrip('/'), path.lstrip('/')) httpretty.register_uri(method, uri, **kwargs) @httpretty.activate def test_auth_plugin_default_with_plugin(self): self.stub_url('GET', base_url=self.TEST_URL, json=self.TEST_JSON) # if there is an auth_plugin then it should default to authenticated auth = AuthPlugin() sess = client_session.Session(auth=auth) resp = sess.get(self.TEST_URL) self.assertDictEqual(resp.json(), self.TEST_JSON) self.assertRequestHeaderEqual('X-Auth-Token', AuthPlugin.TEST_TOKEN) @httpretty.activate def test_auth_plugin_disable(self): self.stub_url('GET', base_url=self.TEST_URL, json=self.TEST_JSON) auth = AuthPlugin() sess = client_session.Session(auth=auth) resp = sess.get(self.TEST_URL, authenticated=False) self.assertDictEqual(resp.json(), self.TEST_JSON) self.assertRequestHeaderEqual('X-Auth-Token', None) @httpretty.activate def test_service_type_urls(self): service_type = 'compute' interface = 'public' path = '/instances' status = 200 body = 'SUCCESS' self.stub_service_url(service_type=service_type, interface=interface, path=path, status=status, body=body) sess = client_session.Session(auth=AuthPlugin()) resp = sess.get(path, endpoint_filter={'service_type': service_type, 'interface': interface}) self.assertEqual(httpretty.last_request().path, '/v1.0/instances') self.assertEqual(resp.text, body) self.assertEqual(resp.status_code, status) def test_service_url_raises_if_no_auth_plugin(self): sess = client_session.Session() self.assertRaises(exceptions.MissingAuthPlugin, sess.get, '/path', endpoint_filter={'service_type': 'compute', 'interface': 'public'}) def test_service_url_raises_if_no_url_returned(self): sess = client_session.Session(auth=AuthPlugin()) self.assertRaises(exceptions.EndpointNotFound, sess.get, '/path', endpoint_filter={'service_type': 'unknown', 'interface': 'public'}) python-keystoneclient-0.7.1/keystoneclient/tests/test_base.py0000664000175400017540000001366312315030450025763 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from keystoneclient import base from keystoneclient.tests import utils from keystoneclient.v2_0 import client from keystoneclient.v2_0 import roles class HumanReadable(base.Resource): HUMAN_ID = True class BaseTest(utils.TestCase): def test_resource_repr(self): r = base.Resource(None, dict(foo="bar", baz="spam")) self.assertEqual(repr(r), "") def test_getid(self): self.assertEqual(base.getid(4), 4) class TmpObject(object): id = 4 self.assertEqual(base.getid(TmpObject), 4) def test_resource_lazy_getattr(self): self.client = client.Client(username=self.TEST_USER, token=self.TEST_TOKEN, tenant_name=self.TEST_TENANT_NAME, auth_url='http://127.0.0.1:5000', endpoint='http://127.0.0.1:5000') self.client.get = self.mox.CreateMockAnything() self.client.get('/OS-KSADM/roles/1').AndRaise(AttributeError) self.mox.ReplayAll() f = roles.Role(self.client.roles, {'id': 1, 'name': 'Member'}) self.assertEqual(f.name, 'Member') # Missing stuff still fails after a second get self.assertRaises(AttributeError, getattr, f, 'blahblah') def test_eq(self): # Two resources of the same type with the same id: equal r1 = base.Resource(None, {'id': 1, 'name': 'hi'}) r2 = base.Resource(None, {'id': 1, 'name': 'hello'}) self.assertEqual(r1, r2) # Two resoruces of different types: never equal r1 = base.Resource(None, {'id': 1}) r2 = roles.Role(None, {'id': 1}) self.assertNotEqual(r1, r2) # Two resources with no ID: equal if their info is equal r1 = base.Resource(None, {'name': 'joe', 'age': 12}) r2 = base.Resource(None, {'name': 'joe', 'age': 12}) self.assertEqual(r1, r2) r1 = base.Resource(None, {'id': 1}) self.assertNotEqual(r1, object()) self.assertNotEqual(r1, {'id': 1}) def test_human_id(self): r = base.Resource(None, {"name": "1 of !"}) self.assertIsNone(r.human_id) r = HumanReadable(None, {"name": "1 of !"}) self.assertEqual(r.human_id, "1-of") class ManagerTest(utils.TestCase): body = {"hello": {"hi": 1}} url = "/test-url" def setUp(self): super(ManagerTest, self).setUp() self.client = client.Client(username=self.TEST_USER, token=self.TEST_TOKEN, tenant_name=self.TEST_TENANT_NAME, auth_url='http://127.0.0.1:5000', endpoint='http://127.0.0.1:5000') self.mgr = base.Manager(self.client) self.mgr.resource_class = base.Resource def test_api(self): self.assertEqual(self.mgr.api, self.client) def test_get(self): self.client.get = self.mox.CreateMockAnything() self.client.get(self.url).AndReturn((None, self.body)) self.mox.ReplayAll() rsrc = self.mgr._get(self.url, "hello") self.assertEqual(rsrc.hi, 1) def test_post(self): self.client.post = self.mox.CreateMockAnything() self.client.post(self.url, body=self.body).AndReturn((None, self.body)) self.client.post(self.url, body=self.body).AndReturn((None, self.body)) self.mox.ReplayAll() rsrc = self.mgr._post(self.url, self.body, "hello") self.assertEqual(rsrc.hi, 1) rsrc = self.mgr._post(self.url, self.body, "hello", return_raw=True) self.assertEqual(rsrc["hi"], 1) def test_put(self): self.client.put = self.mox.CreateMockAnything() self.client.put(self.url, body=self.body).AndReturn((None, self.body)) self.client.put(self.url, body=self.body).AndReturn((None, self.body)) self.mox.ReplayAll() rsrc = self.mgr._put(self.url, self.body, "hello") self.assertEqual(rsrc.hi, 1) rsrc = self.mgr._put(self.url, self.body) self.assertEqual(rsrc.hello["hi"], 1) def test_patch(self): self.client.patch = self.mox.CreateMockAnything() self.client.patch(self.url, body=self.body).AndReturn( (None, self.body)) self.client.patch(self.url, body=self.body).AndReturn( (None, self.body)) self.mox.ReplayAll() rsrc = self.mgr._patch(self.url, self.body, "hello") self.assertEqual(rsrc.hi, 1) rsrc = self.mgr._patch(self.url, self.body) self.assertEqual(rsrc.hello["hi"], 1) def test_update(self): self.client.patch = self.mox.CreateMockAnything() self.client.put = self.mox.CreateMockAnything() self.client.patch( self.url, body=self.body, management=False).AndReturn((None, self.body)) self.client.put(self.url, body=None, management=True).AndReturn( (None, self.body)) self.mox.ReplayAll() rsrc = self.mgr._update( self.url, body=self.body, response_key="hello", method="PATCH", management=False) self.assertEqual(rsrc.hi, 1) rsrc = self.mgr._update( self.url, body=None, response_key="hello", method="PUT", management=True) self.assertEqual(rsrc.hi, 1) python-keystoneclient-0.7.1/keystoneclient/tests/__init__.py0000664000175400017540000000000012315030450025526 0ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/access.py0000664000175400017540000004222412315030450024104 0ustar jenkinsjenkins00000000000000# Copyright 2012 Nebula, Inc. # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import datetime from keystoneclient.openstack.common import timeutils from keystoneclient import service_catalog # gap, in seconds, to determine whether the given token is about to expire STALE_TOKEN_DURATION = 30 class AccessInfo(dict): """Encapsulates a raw authentication token from keystone. Provides helper methods for extracting useful values from that token. """ @classmethod def factory(cls, resp=None, body=None, region_name=None, **kwargs): """Create AccessInfo object given a successful auth response & body or a user-provided dict. """ # FIXME(jamielennox): Passing region_name is deprecated. Provide an # appropriate warning. if body is not None or len(kwargs): if AccessInfoV3.is_valid(body, **kwargs): token = None if resp: token = resp.headers['X-Subject-Token'] if body: if region_name: body['token']['region_name'] = region_name return AccessInfoV3(token, **body['token']) else: return AccessInfoV3(token, **kwargs) elif AccessInfoV2.is_valid(body, **kwargs): if body: if region_name: body['access']['region_name'] = region_name return AccessInfoV2(**body['access']) else: return AccessInfoV2(**kwargs) else: raise NotImplementedError('Unrecognized auth response') else: return AccessInfoV2(**kwargs) def __init__(self, *args, **kwargs): super(AccessInfo, self).__init__(*args, **kwargs) self.service_catalog = service_catalog.ServiceCatalog.factory( resource_dict=self, region_name=self._region_name) @property def _region_name(self): return self.get('region_name') def will_expire_soon(self, stale_duration=None): """Determines if expiration is about to occur. :return: boolean : true if expiration is within the given duration """ stale_duration = (STALE_TOKEN_DURATION if stale_duration is None else stale_duration) norm_expires = timeutils.normalize_time(self.expires) # (gyee) should we move auth_token.will_expire_soon() to timeutils # instead of duplicating code here? soon = (timeutils.utcnow() + datetime.timedelta( seconds=stale_duration)) return norm_expires < soon @classmethod def is_valid(cls, body, **kwargs): """Determines if processing v2 or v3 token given a successful auth body or a user-provided dict. :return: boolean : true if auth body matches implementing class """ raise NotImplementedError() def has_service_catalog(self): """Returns true if the authorization token has a service catalog. :returns: boolean """ raise NotImplementedError() @property def auth_token(self): """Returns the token_id associated with the auth request, to be used in headers for authenticating OpenStack API requests. :returns: str """ raise NotImplementedError() @property def expires(self): """Returns the token expiration (as datetime object) :returns: datetime """ raise NotImplementedError() @property def username(self): """Returns the username associated with the authentication request. Follows the pattern defined in the V2 API of first looking for 'name', returning that if available, and falling back to 'username' if name is unavailable. :returns: str """ raise NotImplementedError() @property def user_id(self): """Returns the user id associated with the authentication request. :returns: str """ raise NotImplementedError() @property def user_domain_id(self): """Returns the domain id of the user associated with the authentication request. For v2, it always returns 'default' which may be different from the Keystone configuration. :returns: str """ raise NotImplementedError() @property def user_domain_name(self): """Returns the domain name of the user associated with the authentication request. For v2, it always returns 'Default' which may be different from the Keystone configuration. :returns: str """ raise NotImplementedError() @property def role_names(self): """Returns a list of role names of the user associated with the authentication request. :returns: a list of strings of role names """ raise NotImplementedError() @property def domain_name(self): """Returns the domain name associated with the authentication token. :returns: str or None (if no domain associated with the token) """ raise NotImplementedError() @property def domain_id(self): """Returns the domain id associated with the authentication token. :returns: str or None (if no domain associated with the token) """ raise NotImplementedError() @property def project_name(self): """Returns the project name associated with the authentication request. :returns: str or None (if no project associated with the token) """ raise NotImplementedError() @property def tenant_name(self): """Synonym for project_name.""" return self.project_name @property def scoped(self): """Returns true if the authorization token was scoped to a tenant (project), and contains a populated service catalog. This is deprecated, use project_scoped instead. :returns: bool """ raise NotImplementedError() @property def project_scoped(self): """Returns true if the authorization token was scoped to a tenant (project). :returns: bool """ raise NotImplementedError() @property def domain_scoped(self): """Returns true if the authorization token was scoped to a domain. :returns: bool """ raise NotImplementedError() @property def trust_id(self): """Returns the trust id associated with the authentication token. :returns: str or None (if no trust associated with the token) """ raise NotImplementedError() @property def trust_scoped(self): """Returns true if the authorization token was scoped as delegated in a trust, via the OS-TRUST v3 extension. :returns: bool """ raise NotImplementedError() @property def project_id(self): """Returns the project ID associated with the authentication request, or None if the authentication request wasn't scoped to a project. :returns: str or None (if no project associated with the token) """ raise NotImplementedError() @property def tenant_id(self): """Synonym for project_id.""" return self.project_id @property def project_domain_id(self): """Returns the domain id of the project associated with the authentication request. For v2, it returns 'default' if a project is scoped or None which may be different from the keystone configuration. :returns: str """ raise NotImplementedError() @property def project_domain_name(self): """Returns the domain name of the project associated with the authentication request. For v2, it returns 'Default' if a project is scoped or None which may be different from the keystone configuration. :returns: str """ raise NotImplementedError() @property def auth_url(self): """Returns a tuple of URLs from publicURL and adminURL for the service 'identity' from the service catalog associated with the authorization request. If the authentication request wasn't scoped to a tenant (project), this property will return None. DEPRECATED: this doesn't correctly handle region name. You should fetch it from the service catalog yourself. :returns: tuple of urls """ raise NotImplementedError() @property def management_url(self): """Returns the first adminURL for 'identity' from the service catalog associated with the authorization request, or None if the authentication request wasn't scoped to a tenant (project). DEPRECATED: this doesn't correctly handle region name. You should fetch it from the service catalog yourself. :returns: tuple of urls """ raise NotImplementedError() @property def version(self): """Returns the version of the auth token from identity service. :returns: str """ return self.get('version') class AccessInfoV2(AccessInfo): """An object for encapsulating a raw v2 auth token from identity service. """ def __init__(self, *args, **kwargs): super(AccessInfo, self).__init__(*args, **kwargs) self.update(version='v2.0') self.service_catalog = service_catalog.ServiceCatalog.factory( resource_dict=self, token=self['token']['id'], region_name=self._region_name) @classmethod def is_valid(cls, body, **kwargs): if body: return 'access' in body elif kwargs: return kwargs.get('version') == 'v2.0' else: return False def has_service_catalog(self): return 'serviceCatalog' in self @property def auth_token(self): return self['token']['id'] @property def expires(self): return timeutils.parse_isotime(self['token']['expires']) @property def username(self): return self['user'].get('name', self['user'].get('username')) @property def user_id(self): return self['user']['id'] @property def user_domain_id(self): return 'default' @property def user_domain_name(self): return 'Default' @property def role_names(self): return [r['name'] for r in self['user']['roles']] @property def domain_name(self): return None @property def domain_id(self): return None @property def project_name(self): try: tenant_dict = self['token']['tenant'] except KeyError: pass else: return tenant_dict.get('name') # pre grizzly try: return self['user']['tenantName'] except KeyError: pass # pre diablo, keystone only provided a tenantId try: return self['token']['tenantId'] except KeyError: pass @property def scoped(self): if ('serviceCatalog' in self and self['serviceCatalog'] and 'tenant' in self['token']): return True return False @property def project_scoped(self): return 'tenant' in self['token'] @property def domain_scoped(self): return False @property def trust_id(self): return self.get('trust', {}).get('id') @property def trust_scoped(self): return 'trust' in self @property def project_id(self): try: tenant_dict = self['token']['tenant'] except KeyError: pass else: return tenant_dict.get('id') # pre grizzly try: return self['user']['tenantId'] except KeyError: pass # pre diablo try: return self['token']['tenantId'] except KeyError: pass @property def project_domain_id(self): if self.project_id: return 'default' @property def project_domain_name(self): if self.project_id: return 'Default' @property def auth_url(self): # FIXME(jamielennox): this is deprecated in favour of retrieving it # from the service catalog. Provide a warning. if self.service_catalog: return self.service_catalog.get_urls(service_type='identity', endpoint_type='publicURL', region_name=self._region_name) else: return None @property def management_url(self): # FIXME(jamielennox): this is deprecated in favour of retrieving it # from the service catalog. Provide a warning. if self.service_catalog: return self.service_catalog.get_urls(service_type='identity', endpoint_type='adminURL', region_name=self._region_name) else: return None class AccessInfoV3(AccessInfo): """An object for encapsulating a raw v3 auth token from identity service. """ def __init__(self, token, *args, **kwargs): super(AccessInfo, self).__init__(*args, **kwargs) self.update(version='v3') self.service_catalog = service_catalog.ServiceCatalog.factory( resource_dict=self, token=token, region_name=self._region_name) if token: self.update(auth_token=token) @classmethod def is_valid(cls, body, **kwargs): if body: return 'token' in body elif kwargs: return kwargs.get('version') == 'v3' else: return False def has_service_catalog(self): return 'catalog' in self @property def auth_token(self): return self['auth_token'] @property def expires(self): return timeutils.parse_isotime(self['expires_at']) @property def user_id(self): return self['user']['id'] @property def user_domain_id(self): return self['user']['domain']['id'] @property def user_domain_name(self): return self['user']['domain']['name'] @property def role_names(self): return [r['name'] for r in self.get('roles', [])] @property def username(self): return self['user']['name'] @property def domain_name(self): domain = self.get('domain') if domain: return domain['name'] @property def domain_id(self): domain = self.get('domain') if domain: return domain['id'] @property def project_id(self): project = self.get('project') if project: return project['id'] @property def project_domain_id(self): project = self.get('project') if project: return project['domain']['id'] @property def project_domain_name(self): project = self.get('project') if project: return project['domain']['name'] @property def project_name(self): project = self.get('project') if project: return project['name'] @property def scoped(self): return ('catalog' in self and self['catalog'] and 'project' in self) @property def project_scoped(self): return 'project' in self @property def domain_scoped(self): return 'domain' in self @property def trust_id(self): return self.get('OS-TRUST:trust', {}).get('id') @property def trust_scoped(self): return 'OS-TRUST:trust' in self @property def auth_url(self): # FIXME(jamielennox): this is deprecated in favour of retrieving it # from the service catalog. Provide a warning. if self.service_catalog: return self.service_catalog.get_urls(service_type='identity', endpoint_type='public', region_name=self._region_name) else: return None @property def management_url(self): # FIXME(jamielennox): this is deprecated in favour of retrieving it # from the service catalog. Provide a warning. if self.service_catalog: return self.service_catalog.get_urls(service_type='identity', endpoint_type='admin', region_name=self._region_name) else: return None python-keystoneclient-0.7.1/keystoneclient/session.py0000664000175400017540000003643512315030450024335 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging import requests import six from six.moves import urllib from keystoneclient import exceptions from keystoneclient.openstack.common import jsonutils from keystoneclient import utils USER_AGENT = 'python-keystoneclient' _logger = logging.getLogger(__name__) def request(url, method='GET', **kwargs): return Session().request(url, method=method, **kwargs) class _FakeRequestSession(object): """This object is a temporary hack that should be removed later. Keystoneclient has a cyclical dependency with its managers which is preventing it from being cleaned up correctly. This is always bad but when we switched to doing connection pooling this object wasn't getting cleaned either and so we had left over TCP connections hanging around. Until we can fix the client cleanup we rollback the use of a requests session and do individual connections like we used to. """ def request(self, *args, **kwargs): return requests.request(*args, **kwargs) class Session(object): user_agent = None REDIRECT_STATUSES = (301, 302, 303, 305, 307) DEFAULT_REDIRECT_LIMIT = 30 @utils.positional(2, enforcement=utils.positional.WARN) def __init__(self, auth=None, session=None, original_ip=None, verify=True, cert=None, timeout=None, user_agent=None, redirect=DEFAULT_REDIRECT_LIMIT): """Maintains client communication state and common functionality. As much as possible the parameters to this class reflect and are passed directly to the requests library. :param auth: An authentication plugin to authenticate the session with. (optional, defaults to None) :param requests.Session session: A requests session object that can be used for issuing requests. (optional) :param string original_ip: The original IP of the requesting user which will be sent to identity service in a 'Forwarded' header. (optional) :param verify: The verification arguments to pass to requests. These are of the same form as requests expects, so True or False to verify (or not) against system certificates or a path to a bundle or CA certs to check against or None for requests to attempt to locate and use certificates. (optional, defaults to True) :param cert: A client certificate to pass to requests. These are of the same form as requests expects. Either a single filename containing both the certificate and key or a tuple containing the path to the certificate then a path to the key. (optional) :param float timeout: A timeout to pass to requests. This should be a numerical value indicating some amount (or fraction) of seconds or 0 for no timeout. (optional, defaults to 0) :param string user_agent: A User-Agent header string to use for the request. If not provided a default is used. (optional, defaults to 'python-keystoneclient') :param int/bool redirect: Controls the maximum number of redirections that can be followed by a request. Either an integer for a specific count or True/False for forever/never. (optional, default to 30) """ if not session: session = _FakeRequestSession() self.auth = auth self.session = session self.original_ip = original_ip self.verify = verify self.cert = cert self.timeout = None self.redirect = redirect if timeout is not None: self.timeout = float(timeout) # don't override the class variable if none provided if user_agent is not None: self.user_agent = user_agent @utils.positional(enforcement=utils.positional.WARN) def request(self, url, method, json=None, original_ip=None, user_agent=None, redirect=None, authenticated=None, endpoint_filter=None, **kwargs): """Send an HTTP request with the specified characteristics. Wrapper around `requests.Session.request` to handle tasks such as setting headers, JSON encoding/decoding, and error handling. Arguments that are not handled are passed through to the requests library. :param string url: Path or fully qualified URL of HTTP request. If only a path is provided then endpoint_filter must also be provided such that the base URL can be determined. If a fully qualified URL is provided then endpoint_filter will be ignored. :param string method: The http method to use. (eg. 'GET', 'POST') :param string original_ip: Mark this request as forwarded for this ip. (optional) :param dict headers: Headers to be included in the request. (optional) :param json: Some data to be represented as JSON. (optional) :param string user_agent: A user_agent to use for the request. If present will override one present in headers. (optional) :param int/bool redirect: the maximum number of redirections that can be followed by a request. Either an integer for a specific count or True/False for forever/never. (optional) :param bool authenticated: True if a token should be attached to this request, False if not or None for attach if an auth_plugin is available. (optional, defaults to None) :param dict endpoint_filter: Data to be provided to an auth plugin with which it should be able to determine an endpoint to use for this request. If not provided then URL is expected to be a fully qualified URL. (optional) :param kwargs: any other parameter that can be passed to requests.Session.request (such as `headers`). Except: 'data' will be overwritten by the data in 'json' param. 'allow_redirects' is ignored as redirects are handled by the session. :raises exceptions.ClientException: For connection failure, or to indicate an error response code. :returns: The response to the request. """ headers = kwargs.setdefault('headers', dict()) if authenticated is None: authenticated = self.auth is not None if authenticated: token = self.get_token() if not token: raise exceptions.AuthorizationFailure("No token Available") headers['X-Auth-Token'] = token # if we are passed a fully qualified URL and a endpoint_filter we # should ignore the filter. This will make it easier for clients who # want to overrule the default endpoint_filter data added to all client # requests. We check fully qualified here by the presence of a host. url_data = urllib.parse.urlparse(url) if endpoint_filter and not url_data.netloc: base_url = self.get_endpoint(**endpoint_filter) if not base_url: raise exceptions.EndpointNotFound() url = '%s/%s' % (base_url.rstrip('/'), url.lstrip('/')) if self.cert: kwargs.setdefault('cert', self.cert) if self.timeout is not None: kwargs.setdefault('timeout', self.timeout) if user_agent: headers['User-Agent'] = user_agent elif self.user_agent: user_agent = headers.setdefault('User-Agent', self.user_agent) else: user_agent = headers.setdefault('User-Agent', USER_AGENT) if self.original_ip: headers.setdefault('Forwarded', 'for=%s;by=%s' % (self.original_ip, user_agent)) if json is not None: headers['Content-Type'] = 'application/json' kwargs['data'] = jsonutils.dumps(json) kwargs.setdefault('verify', self.verify) string_parts = ['curl -i'] # NOTE(jamielennox): None means let requests do its default validation # so we need to actually check that this is False. if self.verify is False: string_parts.append('--insecure') if method: string_parts.extend(['-X', method]) string_parts.append(url) if headers: for header in six.iteritems(headers): string_parts.append('-H "%s: %s"' % header) try: string_parts.append("-d '%s'" % kwargs['data']) except KeyError: pass _logger.debug('REQ: %s', ' '.join(string_parts)) # Force disable requests redirect handling. We will manage this below. kwargs['allow_redirects'] = False if redirect is None: redirect = self.redirect resp = self._send_request(url, method, redirect, **kwargs) # NOTE(jamielennox): we create a tuple here to be the same as what is # returned by the requests library. resp.history = tuple(resp.history) if resp.status_code >= 400: _logger.debug('Request returned failure status: %s', resp.status_code) raise exceptions.from_response(resp, method, url) return resp def _send_request(self, url, method, redirect, **kwargs): # NOTE(jamielennox): We handle redirection manually because the # requests lib follows some browser patterns where it will redirect # POSTs as GETs for certain statuses which is not want we want for an # API. See: https://en.wikipedia.org/wiki/Post/Redirect/Get try: resp = self.session.request(method, url, **kwargs) except requests.exceptions.SSLError: msg = 'SSL exception connecting to %s' % url raise exceptions.SSLError(msg) except requests.exceptions.Timeout: msg = 'Request to %s timed out' % url raise exceptions.Timeout(msg) except requests.exceptions.ConnectionError: msg = 'Unable to establish connection to %s' % url raise exceptions.ConnectionError(msg) _logger.debug('RESP: [%s] %s\nRESP BODY: %s\n', resp.status_code, resp.headers, resp.text) if resp.status_code in self.REDIRECT_STATUSES: # be careful here in python True == 1 and False == 0 if isinstance(redirect, bool): redirect_allowed = redirect else: redirect -= 1 redirect_allowed = redirect >= 0 if not redirect_allowed: return resp try: location = resp.headers['location'] except KeyError: _logger.warn("Failed to redirect request to %s as new " "location was not provided.", resp.url) else: new_resp = self._send_request(location, method, redirect, **kwargs) if not isinstance(new_resp.history, list): new_resp.history = list(new_resp.history) new_resp.history.insert(0, resp) resp = new_resp return resp def head(self, url, **kwargs): return self.request(url, 'HEAD', **kwargs) def get(self, url, **kwargs): return self.request(url, 'GET', **kwargs) def post(self, url, **kwargs): return self.request(url, 'POST', **kwargs) def put(self, url, **kwargs): return self.request(url, 'PUT', **kwargs) def delete(self, url, **kwargs): return self.request(url, 'DELETE', **kwargs) def patch(self, url, **kwargs): return self.request(url, 'PATCH', **kwargs) @classmethod def construct(cls, kwargs): """Handles constructing a session from the older HTTPClient args as well as the new request style arguments. *DEPRECATED*: This function is purely for bridging the gap between older client arguments and the session arguments that they relate to. It is not intended to be used as a generic Session Factory. This function purposefully modifies the input kwargs dictionary so that the remaining kwargs dict can be reused and passed on to other functionswithout session arguments. """ verify = kwargs.pop('verify', None) cacert = kwargs.pop('cacert', None) cert = kwargs.pop('cert', None) key = kwargs.pop('key', None) insecure = kwargs.pop('insecure', False) if verify is None: if insecure: verify = False else: verify = cacert or True if cert and key: # passing cert and key together is deprecated in favour of the # requests lib form of having the cert and key as a tuple cert = (cert, key) return cls(verify=verify, cert=cert, timeout=kwargs.pop('timeout', None), session=kwargs.pop('session', None), original_ip=kwargs.pop('original_ip', None), user_agent=kwargs.pop('user_agent', None)) def get_token(self): """Return a token as provided by the auth plugin. :raises AuthorizationFailure: if a new token fetch fails. :returns string: A valid token. """ if not self.auth: raise exceptions.MissingAuthPlugin("Token Required") try: return self.auth.get_token(self) except exceptions.HTTPError as exc: raise exceptions.AuthorizationFailure("Authentication failure: " "%s" % exc) def get_endpoint(self, **kwargs): """Get an endpoint as provided by the auth plugin.""" if not self.auth: raise exceptions.MissingAuthPlugin('An auth plugin is required to ' 'determine the endpoint URL.') return self.auth.get_endpoint(self, **kwargs) python-keystoneclient-0.7.1/keystoneclient/shell.py0000664000175400017540000004771212315030450023761 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Pending deprecation: Command-line interface to the OpenStack Identity API. This CLI is pending deprecation in favor of python-openstackclient. For a Python library, continue using python-keystoneclient. """ from __future__ import print_function import argparse import getpass import logging import os import sys import six import keystoneclient from keystoneclient import access from keystoneclient.contrib.bootstrap import shell as shell_bootstrap from keystoneclient import exceptions as exc from keystoneclient.generic import shell as shell_generic from keystoneclient.openstack.common import strutils from keystoneclient import utils from keystoneclient.v2_0 import shell as shell_v2_0 def positive_non_zero_float(argument_value): if argument_value is None: return None try: value = float(argument_value) except ValueError: msg = "%s must be a float" % argument_value raise argparse.ArgumentTypeError(msg) if value <= 0: msg = "%s must be greater than 0" % argument_value raise argparse.ArgumentTypeError(msg) return value def env(*vars, **kwargs): """Search for the first defined of possibly many env vars Returns the first environment variable defined in vars, or returns the default defined in kwargs. """ for v in vars: value = os.environ.get(v) if value: return value return kwargs.get('default', '') class OpenStackIdentityShell(object): def __init__(self, parser_class=argparse.ArgumentParser): self.parser_class = parser_class def get_base_parser(self): parser = self.parser_class( prog='keystone', description=__doc__.strip(), epilog='See "keystone help COMMAND" ' 'for help on a specific command.', add_help=False, formatter_class=OpenStackHelpFormatter, ) # Global arguments parser.add_argument('-h', '--help', action='store_true', help=argparse.SUPPRESS) parser.add_argument('--version', action='version', version=keystoneclient.__version__, help="Shows the client version and exits.") parser.add_argument('--debug', default=False, action='store_true', help=argparse.SUPPRESS) parser.add_argument('--timeout', default=600, type=positive_non_zero_float, metavar='', help="Set request timeout (in seconds).") parser.add_argument('--os-username', metavar='', default=env('OS_USERNAME'), help='Name used for authentication with the ' 'OpenStack Identity service. ' 'Defaults to env[OS_USERNAME].') parser.add_argument('--os_username', help=argparse.SUPPRESS) parser.add_argument('--os-password', metavar='', default=env('OS_PASSWORD'), help='Password used for authentication with the ' 'OpenStack Identity service. ' 'Defaults to env[OS_PASSWORD].') parser.add_argument('--os_password', help=argparse.SUPPRESS) parser.add_argument('--os-tenant-name', metavar='', default=env('OS_TENANT_NAME'), help='Tenant to request authorization on. ' 'Defaults to env[OS_TENANT_NAME].') parser.add_argument('--os_tenant_name', help=argparse.SUPPRESS) parser.add_argument('--os-tenant-id', metavar='', default=env('OS_TENANT_ID'), help='Tenant to request authorization on. ' 'Defaults to env[OS_TENANT_ID].') parser.add_argument('--os_tenant_id', help=argparse.SUPPRESS) parser.add_argument('--os-auth-url', metavar='', default=env('OS_AUTH_URL'), help='Specify the Identity endpoint to use for ' 'authentication. ' 'Defaults to env[OS_AUTH_URL].') parser.add_argument('--os_auth_url', help=argparse.SUPPRESS) parser.add_argument('--os-region-name', metavar='', default=env('OS_REGION_NAME'), help='Specify the region to use. ' 'Defaults to env[OS_REGION_NAME].') parser.add_argument('--os_region_name', help=argparse.SUPPRESS) parser.add_argument('--os-identity-api-version', metavar='', default=env('OS_IDENTITY_API_VERSION', 'KEYSTONE_VERSION'), help='Specify Identity API version to use. ' 'Defaults to env[OS_IDENTITY_API_VERSION]' ' or 2.0.') parser.add_argument('--os_identity_api_version', help=argparse.SUPPRESS) parser.add_argument('--os-token', metavar='', default=env('OS_SERVICE_TOKEN'), help='Specify an existing token to use instead of ' 'retrieving one via authentication (e.g. ' 'with username & password). ' 'Defaults to env[OS_SERVICE_TOKEN].') parser.add_argument('--os-endpoint', metavar='', default=env('OS_SERVICE_ENDPOINT'), help='Specify an endpoint to use instead of ' 'retrieving one from the service catalog ' '(via authentication). ' 'Defaults to env[OS_SERVICE_ENDPOINT].') parser.add_argument('--os-cacert', metavar='', default=env('OS_CACERT', default=None), help='Specify a CA bundle file to use in ' 'verifying a TLS (https) server certificate. ' 'Defaults to env[OS_CACERT].') parser.add_argument('--os_cacert', help=argparse.SUPPRESS) parser.add_argument('--insecure', default=False, action="store_true", help='Explicitly allow keystoneclient to perform ' '"insecure" TLS (https) requests. The ' 'server\'s certificate will not be verified ' 'against any certificate authorities. This ' 'option should be used with caution.') parser.add_argument('--os-cert', metavar='', default=env('OS_CERT'), help='Defaults to env[OS_CERT].') parser.add_argument('--os_cert', help=argparse.SUPPRESS) parser.add_argument('--os-key', metavar='', default=env('OS_KEY'), help='Defaults to env[OS_KEY].') parser.add_argument('--os_key', help=argparse.SUPPRESS) parser.add_argument('--os-cache', default=env('OS_CACHE', default=False), action='store_true', help='Use the auth token cache. ' 'Defaults to env[OS_CACHE].') parser.add_argument('--os_cache', help=argparse.SUPPRESS) parser.add_argument('--force-new-token', default=False, action="store_true", dest='force_new_token', help="If the keyring is available and in use, " "token will always be stored and fetched " "from the keyring until the token has " "expired. Use this option to request a " "new token and replace the existing one " "in the keyring.") parser.add_argument('--stale-duration', metavar='', default=access.STALE_TOKEN_DURATION, dest='stale_duration', help="Stale duration (in seconds) used to " "determine whether a token has expired " "when retrieving it from keyring. This " "is useful in mitigating process or " "network delays. Default is %s seconds." % access.STALE_TOKEN_DURATION) return parser def get_subcommand_parser(self, version): parser = self.get_base_parser() self.subcommands = {} subparsers = parser.add_subparsers(metavar='') try: actions_module = { '2.0': shell_v2_0, }[version] except KeyError: actions_module = shell_v2_0 self._find_actions(subparsers, actions_module) self._find_actions(subparsers, shell_generic) self._find_actions(subparsers, shell_bootstrap) self._find_actions(subparsers, self) self._add_bash_completion_subparser(subparsers) return parser def _add_bash_completion_subparser(self, subparsers): subparser = subparsers.add_parser( 'bash_completion', add_help=False, formatter_class=OpenStackHelpFormatter ) self.subcommands['bash_completion'] = subparser subparser.set_defaults(func=self.do_bash_completion) def _find_actions(self, subparsers, actions_module): for attr in (a for a in dir(actions_module) if a.startswith('do_')): # I prefer to be hyphen-separated instead of underscores. command = attr[3:].replace('_', '-') callback = getattr(actions_module, attr) desc = callback.__doc__ or '' help = desc.strip().split('\n')[0] arguments = getattr(callback, 'arguments', []) subparser = subparsers.add_parser( command, help=help, description=desc, add_help=False, formatter_class=OpenStackHelpFormatter) subparser.add_argument('-h', '--help', action='help', help=argparse.SUPPRESS) self.subcommands[command] = subparser group = subparser.add_argument_group(title='Arguments') for (args, kwargs) in arguments: group.add_argument(*args, **kwargs) subparser.set_defaults(func=callback) def auth_check(self, args): if args.os_token or args.os_endpoint: if not args.os_token: raise exc.CommandError( 'Expecting a token provided via either --os-token or ' 'env[OS_SERVICE_TOKEN]') if not args.os_endpoint: raise exc.CommandError( 'Expecting an endpoint provided via either ' '--os-endpoint or env[OS_SERVICE_ENDPOINT]') # user supplied a token and endpoint and at least one other cred if args.os_username or args.os_password or args.os_auth_url: msg = ('WARNING: Bypassing authentication using a token & ' 'endpoint (authentication credentials are being ' 'ignored).') print(msg) else: if not args.os_auth_url: raise exc.CommandError( 'Expecting an auth URL via either --os-auth-url or ' 'env[OS_AUTH_URL]') if args.os_username or args.os_password: if not args.os_username: raise exc.CommandError( 'Expecting a username provided via either ' '--os-username or env[OS_USERNAME]') if not args.os_password: # No password, If we've got a tty, try prompting for it if hasattr(sys.stdin, 'isatty') and sys.stdin.isatty(): # Check for Ctl-D try: args.os_password = getpass.getpass('OS Password: ') except EOFError: pass # No password because we didn't have a tty or the # user Ctl-D when prompted? if not args.os_password: raise exc.CommandError( 'Expecting a password provided via either ' '--os-password, env[OS_PASSWORD], or ' 'prompted response') else: raise exc.CommandError('Expecting authentication method via' '\n either a service token, ' '--os-token or env[OS_SERVICE_TOKEN], ' '\n credentials, ' '--os-username or env[OS_USERNAME]') def main(self, argv): # Parse args once to find version parser = self.get_base_parser() (options, args) = parser.parse_known_args(argv) # build available subcommands based on version api_version = options.os_identity_api_version subcommand_parser = self.get_subcommand_parser(api_version) self.parser = subcommand_parser # Handle top-level --help/-h before attempting to parse # a command off the command line if not argv or options.help: self.do_help(options) return 0 # Parse args again and call whatever callback was selected args = subcommand_parser.parse_args(argv) # Short-circuit and deal with help command right away. if args.func == self.do_help: self.do_help(args) return 0 elif args.func == self.do_bash_completion: self.do_bash_completion(args) return 0 if args.debug: logging.basicConfig(level=logging.DEBUG) # TODO(heckj): supporting backwards compatibility with environment # variables. To be removed after DEVSTACK is updated, ideally in # the Grizzly release cycle. args.os_token = args.os_token or env('SERVICE_TOKEN') args.os_endpoint = args.os_endpoint or env('SERVICE_ENDPOINT') if utils.isunauthenticated(args.func): self.cs = shell_generic.CLIENT_CLASS(endpoint=args.os_auth_url, cacert=args.os_cacert, key=args.os_key, cert=args.os_cert, insecure=args.insecure, timeout=args.timeout) else: self.auth_check(args) token = None if args.os_token and args.os_endpoint: token = args.os_token api_version = options.os_identity_api_version self.cs = self.get_api_class(api_version)( username=args.os_username, tenant_name=args.os_tenant_name, tenant_id=args.os_tenant_id, token=token, endpoint=args.os_endpoint, password=args.os_password, auth_url=args.os_auth_url, region_name=args.os_region_name, cacert=args.os_cacert, key=args.os_key, cert=args.os_cert, insecure=args.insecure, debug=args.debug, use_keyring=args.os_cache, force_new_token=args.force_new_token, stale_duration=args.stale_duration, timeout=args.timeout) try: args.func(self.cs, args) except exc.Unauthorized: raise exc.CommandError("Invalid OpenStack Identity credentials.") except exc.AuthorizationFailure: raise exc.CommandError("Unable to authorize user") def get_api_class(self, version): try: return { "2.0": shell_v2_0.CLIENT_CLASS, }[version] except KeyError: if version: msg = ('WARNING: unsupported identity-api-version %s, ' 'falling back to 2.0' % version) print(msg) return shell_v2_0.CLIENT_CLASS def do_bash_completion(self, args): """Prints all of the commands and options to stdout. The keystone.bash_completion script doesn't have to hard code them. """ commands = set() options = set() for sc_str, sc in self.subcommands.items(): commands.add(sc_str) for option in list(sc._optionals._option_string_actions): options.add(option) commands.remove('bash-completion') commands.remove('bash_completion') print(' '.join(commands | options)) @utils.arg('command', metavar='', nargs='?', help='Display help for .') def do_help(self, args): """Display help about this program or one of its subcommands.""" if getattr(args, 'command', None): if args.command in self.subcommands: self.subcommands[args.command].print_help() else: raise exc.CommandError("'%s' is not a valid subcommand" % args.command) else: self.parser.print_help() # I'm picky about my shell help. class OpenStackHelpFormatter(argparse.HelpFormatter): def start_section(self, heading): # Title-case the headings heading = '%s%s' % (heading[0].upper(), heading[1:]) super(OpenStackHelpFormatter, self).start_section(heading) def main(): try: OpenStackIdentityShell().main(sys.argv[1:]) except Exception as e: print(strutils.safe_encode(six.text_type(e)), file=sys.stderr) sys.exit(1) if __name__ == "__main__": sys.exit(main()) python-keystoneclient-0.7.1/keystoneclient/utils.py0000664000175400017540000002323612315030451024006 0ustar jenkinsjenkins00000000000000# 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 functools import getpass import hashlib import inspect import logging import sys import prettytable import six from keystoneclient import exceptions from keystoneclient.openstack.common import strutils logger = logging.getLogger(__name__) # Decorator for cli-args def arg(*args, **kwargs): def _decorator(func): # Because of the semantics of decorator composition if we just append # to the options list positional options will appear to be backwards. func.__dict__.setdefault('arguments', []).insert(0, (args, kwargs)) return func return _decorator def pretty_choice_list(l): return ', '.join("'%s'" % i for i in l) def print_list(objs, fields, formatters={}, order_by=None): pt = prettytable.PrettyTable([f for f in fields], caching=False, print_empty=False) pt.aligns = ['l' for f in fields] for o in objs: row = [] for field in fields: if field in formatters: row.append(formatters[field](o)) else: field_name = field.lower().replace(' ', '_') data = getattr(o, field_name, '') if data is None: data = '' row.append(data) pt.add_row(row) if order_by is None: order_by = fields[0] print(strutils.safe_encode(pt.get_string(sortby=order_by))) def _word_wrap(string, max_length=0): """wrap long strings to be no longer then max_length.""" if max_length <= 0: return string return '\n'.join([string[i:i + max_length] for i in range(0, len(string), max_length)]) def print_dict(d, wrap=0): """pretty table prints dictionaries. Wrap values to max_length wrap if wrap>0 """ pt = prettytable.PrettyTable(['Property', 'Value'], caching=False, print_empty=False) pt.aligns = ['l', 'l'] for (prop, value) in six.iteritems(d): if value is None: value = '' value = _word_wrap(value, max_length=wrap) pt.add_row([prop, value]) print(strutils.safe_encode(pt.get_string(sortby='Property'))) def find_resource(manager, name_or_id): """Helper for the _find_* methods.""" # first try to get entity as integer id try: if isinstance(name_or_id, int) or name_or_id.isdigit(): return manager.get(int(name_or_id)) except exceptions.NotFound: pass # now try the entity as a string try: return manager.get(name_or_id) except (exceptions.NotFound): pass # finally try to find entity by name try: if isinstance(name_or_id, six.binary_type): name_or_id = name_or_id.decode('utf-8', 'strict') return manager.find(name=name_or_id) except exceptions.NotFound: msg = ("No %s with a name or ID of '%s' exists." % (manager.resource_class.__name__.lower(), name_or_id)) raise exceptions.CommandError(msg) except exceptions.NoUniqueMatch: msg = ("Multiple %s matches found for '%s', use an ID to be more" " specific." % (manager.resource_class.__name__.lower(), name_or_id)) raise exceptions.CommandError(msg) def unauthenticated(f): """Adds 'unauthenticated' attribute to decorated function. Usage:: @unauthenticated def mymethod(f): ... """ f.unauthenticated = True return f def isunauthenticated(f): """Checks to see if the function is marked as not requiring authentication with the @unauthenticated decorator. Returns True if decorator is set to True, False otherwise. """ return getattr(f, 'unauthenticated', False) def hash_signed_token(signed_text): hash_ = hashlib.md5() hash_.update(signed_text) return hash_.hexdigest() def prompt_for_password(): """Prompt user for password if not provided so the password doesn't show up in the bash history. """ if not (hasattr(sys.stdin, 'isatty') and sys.stdin.isatty()): # nothing to do return while True: try: new_passwd = getpass.getpass('New Password: ') rep_passwd = getpass.getpass('Repeat New Password: ') if new_passwd == rep_passwd: return new_passwd except EOFError: return class positional(object): """A decorator which enforces only some args may be passed positionally. This idea and some of the code was taken from the oauth2 client of the google-api client. This decorator makes it easy to support Python 3 style key-word only parameters. For example, in Python 3 it is possible to write:: def fn(pos1, *, kwonly1, kwonly2=None): ... All named parameters after * must be a keyword:: fn(10, 'kw1', 'kw2') # Raises exception. fn(10, kwonly1='kw1', kwonly2='kw2') # Ok. To replicate this behaviour with the positional decorator you simply specify how many arguments may be passed positionally. To replicate the example above:: @positional(1) def fn(pos1, kwonly1=None, kwonly2=None): ... If no default value is provided to a keyword argument, it becomes a required keyword argument:: @positional(0) def fn(required_kw): ... This must be called with the keyword parameter:: fn() # Raises exception. fn(10) # Raises exception. fn(required_kw=10) # Ok. When defining instance or class methods always remember that in python the first positional argument passed is always the instance so you will need to account for `self` and `cls`:: class MyClass(object): @positional(2) def my_method(self, pos1, kwonly1=None): ... @classmethod @positional(2) def my_method(cls, pos1, kwonly1=None): ... If you would prefer not to account for `self` and `cls` you can use the `method` and `classmethod` helpers which do not consider the initial positional argument. So the following class is exactly the same as the one above:: class MyClass(object): @positional.method(1) def my_method(self, pos1, kwonly1=None): ... @positional.classmethod(1) def my_method(cls, pos1, kwonly1=None): ... If a value isn't provided to the decorator then it will enforce that every variable without a default value will be required to be a kwarg:: @positional() def fn(pos1, kwonly1=None): ... fn(10) # Ok. fn(10, 20) # Raises exception. fn(10, kwonly1=20) # Ok. This behaviour will work with the `positional.method` and `positional.classmethod` helper functions as well:: class MyClass(object): @positional.classmethod() def my_method(cls, pos1, kwonly1=None): ... MyClass.my_method(10) # Ok. MyClass.my_method(10, 20) # Raises exception. MyClass.my_method(10, kwonly1=20) # Ok. For compatibility reasons you may wish to not always raise an exception so a WARN mode is available. Rather than raise an exception a warning message will be logged:: @positional(1, enforcement=positional.WARN): def fn(pos1, kwonly=1): ... Available modes are: - positional.EXCEPT - the default, raise an exception. - positional.WARN - log a warning on mistake. """ EXCEPT = 'except' WARN = 'warn' def __init__(self, max_positional_args=None, enforcement=EXCEPT): self._max_positional_args = max_positional_args self._enforcement = enforcement @classmethod def method(cls, max_positional_args=None, enforcement=EXCEPT): if max_positional_args is not None: max_positional_args += 1 def f(func): return cls(max_positional_args, enforcement)(func) return f @classmethod def classmethod(cls, *args, **kwargs): def f(func): return classmethod(cls.method(*args, **kwargs)(func)) return f def __call__(self, func): if self._max_positional_args is None: spec = inspect.getargspec(func) self._max_positional_args = len(spec.args) - len(spec.defaults) plural = '' if self._max_positional_args == 1 else 's' @functools.wraps(func) def inner(*args, **kwargs): if len(args) > self._max_positional_args: message = ('%(name)s takes at most %(max)d positional ' 'argument%(plural)s (%(given)d given)' % {'name': func.__name__, 'max': self._max_positional_args, 'given': len(args), 'plural': plural}) if self._enforcement == self.EXCEPT: raise TypeError(message) elif self._enforcement == self.WARN: logger.warn(message) return func(*args, **kwargs) return inner python-keystoneclient-0.7.1/keystoneclient/locale/0000775000175400017540000000000012315030547023533 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/locale/keystoneclient.pot0000664000175400017540000000120712315030450027310 0ustar jenkinsjenkins00000000000000# Translations template for python-keystoneclient. # Copyright (C) 2012 ORGANIZATION # This file is distributed under the same license as the # python-keystoneclient project. # FIRST AUTHOR , 2012. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: python-keystoneclient 0.1.3.12\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2012-09-29 16:02-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 0.9.6\n" python-keystoneclient-0.7.1/keystoneclient/openstack/0000775000175400017540000000000012315030547024263 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/openstack/common/0000775000175400017540000000000012315030547025553 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/openstack/common/apiclient/0000775000175400017540000000000012315030547027523 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/openstack/common/apiclient/fake_client.py0000664000175400017540000001333612315030450032340 0ustar jenkinsjenkins00000000000000# Copyright 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ A fake server that "responds" to API methods with pre-canned responses. All of these responses come from the spec, so if for some reason the spec's wrong the tests might raise AssertionError. I've indicated in comments the places where actual behavior differs from the spec. """ # W0102: Dangerous default value %s as argument # pylint: disable=W0102 import json import requests import six from six.moves.urllib import parse from keystoneclient.openstack.common.apiclient import client def assert_has_keys(dct, required=[], optional=[]): for k in required: try: assert k in dct except AssertionError: extra_keys = set(dct.keys()).difference(set(required + optional)) raise AssertionError("found unexpected keys: %s" % list(extra_keys)) class TestResponse(requests.Response): """Wrap requests.Response and provide a convenient initialization. """ def __init__(self, data): super(TestResponse, self).__init__() self._content_consumed = True if isinstance(data, dict): self.status_code = data.get('status_code', 200) # Fake the text attribute to streamline Response creation text = data.get('text', "") if isinstance(text, (dict, list)): self._content = json.dumps(text) default_headers = { "Content-Type": "application/json", } else: self._content = text default_headers = {} if six.PY3 and isinstance(self._content, six.string_types): self._content = self._content.encode('utf-8', 'strict') self.headers = data.get('headers') or default_headers else: self.status_code = data def __eq__(self, other): return (self.status_code == other.status_code and self.headers == other.headers and self._content == other._content) class FakeHTTPClient(client.HTTPClient): def __init__(self, *args, **kwargs): self.callstack = [] self.fixtures = kwargs.pop("fixtures", None) or {} if not args and not "auth_plugin" in kwargs: args = (None, ) super(FakeHTTPClient, self).__init__(*args, **kwargs) def assert_called(self, method, url, body=None, pos=-1): """Assert than an API method was just called. """ expected = (method, url) called = self.callstack[pos][0:2] assert self.callstack, \ "Expected %s %s but no calls were made." % expected assert expected == called, 'Expected %s %s; got %s %s' % \ (expected + called) if body is not None: if self.callstack[pos][3] != body: raise AssertionError('%r != %r' % (self.callstack[pos][3], body)) def assert_called_anytime(self, method, url, body=None): """Assert than an API method was called anytime in the test. """ expected = (method, url) assert self.callstack, \ "Expected %s %s but no calls were made." % expected found = False entry = None for entry in self.callstack: if expected == entry[0:2]: found = True break assert found, 'Expected %s %s; got %s' % \ (method, url, self.callstack) if body is not None: assert entry[3] == body, "%s != %s" % (entry[3], body) self.callstack = [] def clear_callstack(self): self.callstack = [] def authenticate(self): pass def client_request(self, client, method, url, **kwargs): # Check that certain things are called correctly if method in ["GET", "DELETE"]: assert "json" not in kwargs # Note the call self.callstack.append( (method, url, kwargs.get("headers") or {}, kwargs.get("json") or kwargs.get("data"))) try: fixture = self.fixtures[url][method] except KeyError: pass else: return TestResponse({"headers": fixture[0], "text": fixture[1]}) # Call the method args = parse.parse_qsl(parse.urlparse(url)[4]) kwargs.update(args) munged_url = url.rsplit('?', 1)[0] munged_url = munged_url.strip('/').replace('/', '_').replace('.', '_') munged_url = munged_url.replace('-', '_') callback = "%s_%s" % (method.lower(), munged_url) if not hasattr(self, callback): raise AssertionError('Called unknown API method: %s %s, ' 'expected fakes method name: %s' % (method, url, callback)) resp = getattr(self, callback)(**kwargs) if len(resp) == 3: status, headers, body = resp else: status, body = resp headers = {} return TestResponse({ "status_code": status, "text": body, "headers": headers, }) python-keystoneclient-0.7.1/keystoneclient/openstack/common/apiclient/base.py0000664000175400017540000003713112315030450031005 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # Copyright 2012 Grid Dynamics # Copyright 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Base utilities to build API operation managers and objects on top of. """ # E1102: %s is not callable # pylint: disable=E1102 import abc import copy import six from six.moves.urllib import parse from keystoneclient.openstack.common.apiclient import exceptions from keystoneclient.openstack.common import strutils def getid(obj): """Return id if argument is a Resource. Abstracts the common pattern of allowing both an object or an object's ID (UUID) as a parameter when dealing with relationships. """ try: if obj.uuid: return obj.uuid except AttributeError: pass try: return obj.id except AttributeError: return obj # TODO(aababilov): call run_hooks() in HookableMixin's child classes class HookableMixin(object): """Mixin so classes can register and run hooks.""" _hooks_map = {} @classmethod def add_hook(cls, hook_type, hook_func): """Add a new hook of specified type. :param cls: class that registers hooks :param hook_type: hook type, e.g., '__pre_parse_args__' :param hook_func: hook function """ if hook_type not in cls._hooks_map: cls._hooks_map[hook_type] = [] cls._hooks_map[hook_type].append(hook_func) @classmethod def run_hooks(cls, hook_type, *args, **kwargs): """Run all hooks of specified type. :param cls: class that registers hooks :param hook_type: hook type, e.g., '__pre_parse_args__' :param **args: args to be passed to every hook function :param **kwargs: kwargs to be passed to every hook function """ hook_funcs = cls._hooks_map.get(hook_type) or [] for hook_func in hook_funcs: hook_func(*args, **kwargs) class BaseManager(HookableMixin): """Basic manager type providing common operations. Managers interact with a particular type of API (servers, flavors, images, etc.) and provide CRUD operations for them. """ resource_class = None def __init__(self, client): """Initializes BaseManager with `client`. :param client: instance of BaseClient descendant for HTTP requests """ super(BaseManager, self).__init__() self.client = client def _list(self, url, response_key, obj_class=None, json=None): """List the collection. :param url: a partial URL, e.g., '/servers' :param response_key: the key to be looked up in response dictionary, e.g., 'servers' :param obj_class: class for constructing the returned objects (self.resource_class will be used by default) :param json: data that will be encoded as JSON and passed in POST request (GET will be sent by default) """ if json: body = self.client.post(url, json=json).json() else: body = self.client.get(url).json() if obj_class is None: obj_class = self.resource_class data = body[response_key] # NOTE(ja): keystone returns values as list as {'values': [ ... ]} # unlike other services which just return the list... try: data = data['values'] except (KeyError, TypeError): pass return [obj_class(self, res, loaded=True) for res in data if res] def _get(self, url, response_key): """Get an object from collection. :param url: a partial URL, e.g., '/servers' :param response_key: the key to be looked up in response dictionary, e.g., 'server' """ body = self.client.get(url).json() return self.resource_class(self, body[response_key], loaded=True) def _head(self, url): """Retrieve request headers for an object. :param url: a partial URL, e.g., '/servers' """ resp = self.client.head(url) return resp.status_code == 204 def _post(self, url, json, response_key, return_raw=False): """Create an object. :param url: a partial URL, e.g., '/servers' :param json: data that will be encoded as JSON and passed in POST request (GET will be sent by default) :param response_key: the key to be looked up in response dictionary, e.g., 'servers' :param return_raw: flag to force returning raw JSON instead of Python object of self.resource_class """ body = self.client.post(url, json=json).json() if return_raw: return body[response_key] return self.resource_class(self, body[response_key]) def _put(self, url, json=None, response_key=None): """Update an object with PUT method. :param url: a partial URL, e.g., '/servers' :param json: data that will be encoded as JSON and passed in POST request (GET will be sent by default) :param response_key: the key to be looked up in response dictionary, e.g., 'servers' """ resp = self.client.put(url, json=json) # PUT requests may not return a body if resp.content: body = resp.json() if response_key is not None: return self.resource_class(self, body[response_key]) else: return self.resource_class(self, body) def _patch(self, url, json=None, response_key=None): """Update an object with PATCH method. :param url: a partial URL, e.g., '/servers' :param json: data that will be encoded as JSON and passed in POST request (GET will be sent by default) :param response_key: the key to be looked up in response dictionary, e.g., 'servers' """ body = self.client.patch(url, json=json).json() if response_key is not None: return self.resource_class(self, body[response_key]) else: return self.resource_class(self, body) def _delete(self, url): """Delete an object. :param url: a partial URL, e.g., '/servers/my-server' """ return self.client.delete(url) @six.add_metaclass(abc.ABCMeta) class ManagerWithFind(BaseManager): """Manager with additional `find()`/`findall()` methods.""" @abc.abstractmethod def list(self): pass def find(self, **kwargs): """Find a single item with attributes matching ``**kwargs``. This isn't very efficient: it loads the entire list then filters on the Python side. """ matches = self.findall(**kwargs) num_matches = len(matches) if num_matches == 0: msg = "No %s matching %s." % (self.resource_class.__name__, kwargs) raise exceptions.NotFound(msg) elif num_matches > 1: raise exceptions.NoUniqueMatch() else: return matches[0] def findall(self, **kwargs): """Find all items with attributes matching ``**kwargs``. This isn't very efficient: it loads the entire list then filters on the Python side. """ found = [] searches = kwargs.items() for obj in self.list(): try: if all(getattr(obj, attr) == value for (attr, value) in searches): found.append(obj) except AttributeError: continue return found class CrudManager(BaseManager): """Base manager class for manipulating entities. Children of this class are expected to define a `collection_key` and `key`. - `collection_key`: Usually a plural noun by convention (e.g. `entities`); used to refer collections in both URL's (e.g. `/v3/entities`) and JSON objects containing a list of member resources (e.g. `{'entities': [{}, {}, {}]}`). - `key`: Usually a singular noun by convention (e.g. `entity`); used to refer to an individual member of the collection. """ collection_key = None key = None def build_url(self, base_url=None, **kwargs): """Builds a resource URL for the given kwargs. Given an example collection where `collection_key = 'entities'` and `key = 'entity'`, the following URL's could be generated. By default, the URL will represent a collection of entities, e.g.:: /entities If kwargs contains an `entity_id`, then the URL will represent a specific member, e.g.:: /entities/{entity_id} :param base_url: if provided, the generated URL will be appended to it """ url = base_url if base_url is not None else '' url += '/%s' % self.collection_key # do we have a specific entity? entity_id = kwargs.get('%s_id' % self.key) if entity_id is not None: url += '/%s' % entity_id return url def _filter_kwargs(self, kwargs): """Drop null values and handle ids.""" for key, ref in six.iteritems(kwargs.copy()): if ref is None: kwargs.pop(key) else: if isinstance(ref, Resource): kwargs.pop(key) kwargs['%s_id' % key] = getid(ref) return kwargs def create(self, **kwargs): kwargs = self._filter_kwargs(kwargs) return self._post( self.build_url(**kwargs), {self.key: kwargs}, self.key) def get(self, **kwargs): kwargs = self._filter_kwargs(kwargs) return self._get( self.build_url(**kwargs), self.key) def head(self, **kwargs): kwargs = self._filter_kwargs(kwargs) return self._head(self.build_url(**kwargs)) def list(self, base_url=None, **kwargs): """List the collection. :param base_url: if provided, the generated URL will be appended to it """ kwargs = self._filter_kwargs(kwargs) return self._list( '%(base_url)s%(query)s' % { 'base_url': self.build_url(base_url=base_url, **kwargs), 'query': '?%s' % parse.urlencode(kwargs) if kwargs else '', }, self.collection_key) def put(self, base_url=None, **kwargs): """Update an element. :param base_url: if provided, the generated URL will be appended to it """ kwargs = self._filter_kwargs(kwargs) return self._put(self.build_url(base_url=base_url, **kwargs)) def update(self, **kwargs): kwargs = self._filter_kwargs(kwargs) params = kwargs.copy() params.pop('%s_id' % self.key) return self._patch( self.build_url(**kwargs), {self.key: params}, self.key) def delete(self, **kwargs): kwargs = self._filter_kwargs(kwargs) return self._delete( self.build_url(**kwargs)) def find(self, base_url=None, **kwargs): """Find a single item with attributes matching ``**kwargs``. :param base_url: if provided, the generated URL will be appended to it """ kwargs = self._filter_kwargs(kwargs) rl = self._list( '%(base_url)s%(query)s' % { 'base_url': self.build_url(base_url=base_url, **kwargs), 'query': '?%s' % parse.urlencode(kwargs) if kwargs else '', }, self.collection_key) num = len(rl) if num == 0: msg = "No %s matching %s." % (self.resource_class.__name__, kwargs) raise exceptions.NotFound(404, msg) elif num > 1: raise exceptions.NoUniqueMatch else: return rl[0] class Extension(HookableMixin): """Extension descriptor.""" SUPPORTED_HOOKS = ('__pre_parse_args__', '__post_parse_args__') manager_class = None def __init__(self, name, module): super(Extension, self).__init__() self.name = name self.module = module self._parse_extension_module() def _parse_extension_module(self): self.manager_class = None for attr_name, attr_value in self.module.__dict__.items(): if attr_name in self.SUPPORTED_HOOKS: self.add_hook(attr_name, attr_value) else: try: if issubclass(attr_value, BaseManager): self.manager_class = attr_value except TypeError: pass def __repr__(self): return "" % self.name class Resource(object): """Base class for OpenStack resources (tenant, user, etc.). This is pretty much just a bag for attributes. """ HUMAN_ID = False NAME_ATTR = 'name' def __init__(self, manager, info, loaded=False): """Populate and bind to a manager. :param manager: BaseManager object :param info: dictionary representing resource attributes :param loaded: prevent lazy-loading if set to True """ self.manager = manager self._info = info self._add_details(info) self._loaded = loaded def __repr__(self): reprkeys = sorted(k for k in self.__dict__.keys() if k[0] != '_' and k != 'manager') info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys) return "<%s %s>" % (self.__class__.__name__, info) @property def human_id(self): """Human-readable ID which can be used for bash completion. """ if self.NAME_ATTR in self.__dict__ and self.HUMAN_ID: return strutils.to_slug(getattr(self, self.NAME_ATTR)) return None def _add_details(self, info): for (k, v) in six.iteritems(info): try: setattr(self, k, v) self._info[k] = v except AttributeError: # In this case we already defined the attribute on the class pass def __getattr__(self, k): if k not in self.__dict__: #NOTE(bcwaldon): disallow lazy-loading if already loaded once if not self.is_loaded: self._get() return self.__getattr__(k) raise AttributeError(k) else: return self.__dict__[k] def _get(self): # set _loaded first ... so if we have to bail, we know we tried. self._loaded = True if not hasattr(self.manager, 'get'): return new = self.manager.get(self.id) if new: self._add_details(new._info) def __eq__(self, other): if not isinstance(other, Resource): return NotImplemented # two resources of different types are not equal if not isinstance(other, self.__class__): return False if hasattr(self, 'id') and hasattr(other, 'id'): return self.id == other.id return self._info == other._info @property def is_loaded(self): return self._loaded def to_dict(self): return copy.deepcopy(self._info) python-keystoneclient-0.7.1/keystoneclient/openstack/common/apiclient/exceptions.py0000664000175400017540000002767412315030450032267 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 Nebula, Inc. # Copyright 2013 Alessio Ababilov # Copyright 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Exception definitions. """ import inspect import sys import six class ClientException(Exception): """The base exception class for all exceptions this library raises. """ pass class MissingArgs(ClientException): """Supplied arguments are not sufficient for calling a function.""" def __init__(self, missing): self.missing = missing msg = "Missing argument(s): %s" % ", ".join(missing) super(MissingArgs, self).__init__(msg) class ValidationError(ClientException): """Error in validation on API client side.""" pass class UnsupportedVersion(ClientException): """User is trying to use an unsupported version of the API.""" pass class CommandError(ClientException): """Error in CLI tool.""" pass class AuthorizationFailure(ClientException): """Cannot authorize API client.""" pass class ConnectionRefused(ClientException): """Cannot connect to API service.""" pass class AuthPluginOptionsMissing(AuthorizationFailure): """Auth plugin misses some options.""" def __init__(self, opt_names): super(AuthPluginOptionsMissing, self).__init__( "Authentication failed. Missing options: %s" % ", ".join(opt_names)) self.opt_names = opt_names class AuthSystemNotFound(AuthorizationFailure): """User has specified a AuthSystem that is not installed.""" def __init__(self, auth_system): super(AuthSystemNotFound, self).__init__( "AuthSystemNotFound: %s" % repr(auth_system)) self.auth_system = auth_system class NoUniqueMatch(ClientException): """Multiple entities found instead of one.""" pass class EndpointException(ClientException): """Something is rotten in Service Catalog.""" pass class EndpointNotFound(EndpointException): """Could not find requested endpoint in Service Catalog.""" pass class AmbiguousEndpoints(EndpointException): """Found more than one matching endpoint in Service Catalog.""" def __init__(self, endpoints=None): super(AmbiguousEndpoints, self).__init__( "AmbiguousEndpoints: %s" % repr(endpoints)) self.endpoints = endpoints class HttpError(ClientException): """The base exception class for all HTTP exceptions. """ http_status = 0 message = "HTTP Error" def __init__(self, message=None, details=None, response=None, request_id=None, url=None, method=None, http_status=None): self.http_status = http_status or self.http_status self.message = message or self.message self.details = details self.request_id = request_id self.response = response self.url = url self.method = method formatted_string = "%s (HTTP %s)" % (self.message, self.http_status) if request_id: formatted_string += " (Request-ID: %s)" % request_id super(HttpError, self).__init__(formatted_string) class HTTPRedirection(HttpError): """HTTP Redirection.""" message = "HTTP Redirection" class HTTPClientError(HttpError): """Client-side HTTP error. Exception for cases in which the client seems to have erred. """ message = "HTTP Client Error" class HttpServerError(HttpError): """Server-side HTTP error. Exception for cases in which the server is aware that it has erred or is incapable of performing the request. """ message = "HTTP Server Error" class MultipleChoices(HTTPRedirection): """HTTP 300 - Multiple Choices. Indicates multiple options for the resource that the client may follow. """ http_status = 300 message = "Multiple Choices" class BadRequest(HTTPClientError): """HTTP 400 - Bad Request. The request cannot be fulfilled due to bad syntax. """ http_status = 400 message = "Bad Request" class Unauthorized(HTTPClientError): """HTTP 401 - Unauthorized. Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet been provided. """ http_status = 401 message = "Unauthorized" class PaymentRequired(HTTPClientError): """HTTP 402 - Payment Required. Reserved for future use. """ http_status = 402 message = "Payment Required" class Forbidden(HTTPClientError): """HTTP 403 - Forbidden. The request was a valid request, but the server is refusing to respond to it. """ http_status = 403 message = "Forbidden" class NotFound(HTTPClientError): """HTTP 404 - Not Found. The requested resource could not be found but may be available again in the future. """ http_status = 404 message = "Not Found" class MethodNotAllowed(HTTPClientError): """HTTP 405 - Method Not Allowed. A request was made of a resource using a request method not supported by that resource. """ http_status = 405 message = "Method Not Allowed" class NotAcceptable(HTTPClientError): """HTTP 406 - Not Acceptable. The requested resource is only capable of generating content not acceptable according to the Accept headers sent in the request. """ http_status = 406 message = "Not Acceptable" class ProxyAuthenticationRequired(HTTPClientError): """HTTP 407 - Proxy Authentication Required. The client must first authenticate itself with the proxy. """ http_status = 407 message = "Proxy Authentication Required" class RequestTimeout(HTTPClientError): """HTTP 408 - Request Timeout. The server timed out waiting for the request. """ http_status = 408 message = "Request Timeout" class Conflict(HTTPClientError): """HTTP 409 - Conflict. Indicates that the request could not be processed because of conflict in the request, such as an edit conflict. """ http_status = 409 message = "Conflict" class Gone(HTTPClientError): """HTTP 410 - Gone. Indicates that the resource requested is no longer available and will not be available again. """ http_status = 410 message = "Gone" class LengthRequired(HTTPClientError): """HTTP 411 - Length Required. The request did not specify the length of its content, which is required by the requested resource. """ http_status = 411 message = "Length Required" class PreconditionFailed(HTTPClientError): """HTTP 412 - Precondition Failed. The server does not meet one of the preconditions that the requester put on the request. """ http_status = 412 message = "Precondition Failed" class RequestEntityTooLarge(HTTPClientError): """HTTP 413 - Request Entity Too Large. The request is larger than the server is willing or able to process. """ http_status = 413 message = "Request Entity Too Large" def __init__(self, *args, **kwargs): try: self.retry_after = int(kwargs.pop('retry_after')) except (KeyError, ValueError): self.retry_after = 0 super(RequestEntityTooLarge, self).__init__(*args, **kwargs) class RequestUriTooLong(HTTPClientError): """HTTP 414 - Request-URI Too Long. The URI provided was too long for the server to process. """ http_status = 414 message = "Request-URI Too Long" class UnsupportedMediaType(HTTPClientError): """HTTP 415 - Unsupported Media Type. The request entity has a media type which the server or resource does not support. """ http_status = 415 message = "Unsupported Media Type" class RequestedRangeNotSatisfiable(HTTPClientError): """HTTP 416 - Requested Range Not Satisfiable. The client has asked for a portion of the file, but the server cannot supply that portion. """ http_status = 416 message = "Requested Range Not Satisfiable" class ExpectationFailed(HTTPClientError): """HTTP 417 - Expectation Failed. The server cannot meet the requirements of the Expect request-header field. """ http_status = 417 message = "Expectation Failed" class UnprocessableEntity(HTTPClientError): """HTTP 422 - Unprocessable Entity. The request was well-formed but was unable to be followed due to semantic errors. """ http_status = 422 message = "Unprocessable Entity" class InternalServerError(HttpServerError): """HTTP 500 - Internal Server Error. A generic error message, given when no more specific message is suitable. """ http_status = 500 message = "Internal Server Error" # NotImplemented is a python keyword. class HttpNotImplemented(HttpServerError): """HTTP 501 - Not Implemented. The server either does not recognize the request method, or it lacks the ability to fulfill the request. """ http_status = 501 message = "Not Implemented" class BadGateway(HttpServerError): """HTTP 502 - Bad Gateway. The server was acting as a gateway or proxy and received an invalid response from the upstream server. """ http_status = 502 message = "Bad Gateway" class ServiceUnavailable(HttpServerError): """HTTP 503 - Service Unavailable. The server is currently unavailable. """ http_status = 503 message = "Service Unavailable" class GatewayTimeout(HttpServerError): """HTTP 504 - Gateway Timeout. The server was acting as a gateway or proxy and did not receive a timely response from the upstream server. """ http_status = 504 message = "Gateway Timeout" class HttpVersionNotSupported(HttpServerError): """HTTP 505 - HttpVersion Not Supported. The server does not support the HTTP protocol version used in the request. """ http_status = 505 message = "HTTP Version Not Supported" # _code_map contains all the classes that have http_status attribute. _code_map = dict( (getattr(obj, 'http_status', None), obj) for name, obj in six.iteritems(vars(sys.modules[__name__])) if inspect.isclass(obj) and getattr(obj, 'http_status', False) ) def from_response(response, method, url): """Returns an instance of :class:`HttpError` or subclass based on response. :param response: instance of `requests.Response` class :param method: HTTP method used for request :param url: URL used for request """ kwargs = { "http_status": response.status_code, "response": response, "method": method, "url": url, "request_id": response.headers.get("x-compute-request-id"), } if "retry-after" in response.headers: kwargs["retry_after"] = response.headers["retry-after"] content_type = response.headers.get("Content-Type", "") if content_type.startswith("application/json"): try: body = response.json() except ValueError: pass else: if isinstance(body, dict): error = list(body.values())[0] kwargs["message"] = error.get("message") kwargs["details"] = error.get("details") elif content_type.startswith("text/"): kwargs["details"] = response.text try: cls = _code_map[response.status_code] except KeyError: if 500 <= response.status_code < 600: cls = HttpServerError elif 400 <= response.status_code < 500: cls = HTTPClientError else: cls = HttpError return cls(**kwargs) python-keystoneclient-0.7.1/keystoneclient/openstack/common/apiclient/client.py0000664000175400017540000003070212315030450031346 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # Copyright 2011 Piston Cloud Computing, Inc. # Copyright 2013 Alessio Ababilov # Copyright 2013 Grid Dynamics # Copyright 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ OpenStack Client interface. Handles the REST calls and responses. """ # E0202: An attribute inherited from %s hide this method # pylint: disable=E0202 import logging import time try: import simplejson as json except ImportError: import json import requests from keystoneclient.openstack.common.apiclient import exceptions from keystoneclient.openstack.common import importutils _logger = logging.getLogger(__name__) class HTTPClient(object): """This client handles sending HTTP requests to OpenStack servers. Features: - share authentication information between several clients to different services (e.g., for compute and image clients); - reissue authentication request for expired tokens; - encode/decode JSON bodies; - raise exceptions on HTTP errors; - pluggable authentication; - store authentication information in a keyring; - store time spent for requests; - register clients for particular services, so one can use `http_client.identity` or `http_client.compute`; - log requests and responses in a format that is easy to copy-and-paste into terminal and send the same request with curl. """ user_agent = "keystoneclient.openstack.common.apiclient" def __init__(self, auth_plugin, region_name=None, endpoint_type="publicURL", original_ip=None, verify=True, cert=None, timeout=None, timings=False, keyring_saver=None, debug=False, user_agent=None, http=None): self.auth_plugin = auth_plugin self.endpoint_type = endpoint_type self.region_name = region_name self.original_ip = original_ip self.timeout = timeout self.verify = verify self.cert = cert self.keyring_saver = keyring_saver self.debug = debug self.user_agent = user_agent or self.user_agent self.times = [] # [("item", starttime, endtime), ...] self.timings = timings # requests within the same session can reuse TCP connections from pool self.http = http or requests.Session() self.cached_token = None def _http_log_req(self, method, url, kwargs): if not self.debug: return string_parts = [ "curl -i", "-X '%s'" % method, "'%s'" % url, ] for element in kwargs['headers']: header = "-H '%s: %s'" % (element, kwargs['headers'][element]) string_parts.append(header) _logger.debug("REQ: %s" % " ".join(string_parts)) if 'data' in kwargs: _logger.debug("REQ BODY: %s\n" % (kwargs['data'])) def _http_log_resp(self, resp): if not self.debug: return _logger.debug( "RESP: [%s] %s\n", resp.status_code, resp.headers) if resp._content_consumed: _logger.debug( "RESP BODY: %s\n", resp.text) def serialize(self, kwargs): if kwargs.get('json') is not None: kwargs['headers']['Content-Type'] = 'application/json' kwargs['data'] = json.dumps(kwargs['json']) try: del kwargs['json'] except KeyError: pass def get_timings(self): return self.times def reset_timings(self): self.times = [] def request(self, method, url, **kwargs): """Send an http request with the specified characteristics. Wrapper around `requests.Session.request` to handle tasks such as setting headers, JSON encoding/decoding, and error handling. :param method: method of HTTP request :param url: URL of HTTP request :param kwargs: any other parameter that can be passed to ' requests.Session.request (such as `headers`) or `json` that will be encoded as JSON and used as `data` argument """ kwargs.setdefault("headers", kwargs.get("headers", {})) kwargs["headers"]["User-Agent"] = self.user_agent if self.original_ip: kwargs["headers"]["Forwarded"] = "for=%s;by=%s" % ( self.original_ip, self.user_agent) if self.timeout is not None: kwargs.setdefault("timeout", self.timeout) kwargs.setdefault("verify", self.verify) if self.cert is not None: kwargs.setdefault("cert", self.cert) self.serialize(kwargs) self._http_log_req(method, url, kwargs) if self.timings: start_time = time.time() resp = self.http.request(method, url, **kwargs) if self.timings: self.times.append(("%s %s" % (method, url), start_time, time.time())) self._http_log_resp(resp) if resp.status_code >= 400: _logger.debug( "Request returned failure status: %s", resp.status_code) raise exceptions.from_response(resp, method, url) return resp @staticmethod def concat_url(endpoint, url): """Concatenate endpoint and final URL. E.g., "http://keystone/v2.0/" and "/tokens" are concatenated to "http://keystone/v2.0/tokens". :param endpoint: the base URL :param url: the final URL """ return "%s/%s" % (endpoint.rstrip("/"), url.strip("/")) def client_request(self, client, method, url, **kwargs): """Send an http request using `client`'s endpoint and specified `url`. If request was rejected as unauthorized (possibly because the token is expired), issue one authorization attempt and send the request once again. :param client: instance of BaseClient descendant :param method: method of HTTP request :param url: URL of HTTP request :param kwargs: any other parameter that can be passed to ' `HTTPClient.request` """ filter_args = { "endpoint_type": client.endpoint_type or self.endpoint_type, "service_type": client.service_type, } token, endpoint = (self.cached_token, client.cached_endpoint) just_authenticated = False if not (token and endpoint): try: token, endpoint = self.auth_plugin.token_and_endpoint( **filter_args) except exceptions.EndpointException: pass if not (token and endpoint): self.authenticate() just_authenticated = True token, endpoint = self.auth_plugin.token_and_endpoint( **filter_args) if not (token and endpoint): raise exceptions.AuthorizationFailure( "Cannot find endpoint or token for request") old_token_endpoint = (token, endpoint) kwargs.setdefault("headers", {})["X-Auth-Token"] = token self.cached_token = token client.cached_endpoint = endpoint # Perform the request once. If we get Unauthorized, then it # might be because the auth token expired, so try to # re-authenticate and try again. If it still fails, bail. try: return self.request( method, self.concat_url(endpoint, url), **kwargs) except exceptions.Unauthorized as unauth_ex: if just_authenticated: raise self.cached_token = None client.cached_endpoint = None self.authenticate() try: token, endpoint = self.auth_plugin.token_and_endpoint( **filter_args) except exceptions.EndpointException: raise unauth_ex if (not (token and endpoint) or old_token_endpoint == (token, endpoint)): raise unauth_ex self.cached_token = token client.cached_endpoint = endpoint kwargs["headers"]["X-Auth-Token"] = token return self.request( method, self.concat_url(endpoint, url), **kwargs) def add_client(self, base_client_instance): """Add a new instance of :class:`BaseClient` descendant. `self` will store a reference to `base_client_instance`. Example: >>> def test_clients(): ... from keystoneclient.auth import keystone ... from openstack.common.apiclient import client ... auth = keystone.KeystoneAuthPlugin( ... username="user", password="pass", tenant_name="tenant", ... auth_url="http://auth:5000/v2.0") ... openstack_client = client.HTTPClient(auth) ... # create nova client ... from novaclient.v1_1 import client ... client.Client(openstack_client) ... # create keystone client ... from keystoneclient.v2_0 import client ... client.Client(openstack_client) ... # use them ... openstack_client.identity.tenants.list() ... openstack_client.compute.servers.list() """ service_type = base_client_instance.service_type if service_type and not hasattr(self, service_type): setattr(self, service_type, base_client_instance) def authenticate(self): self.auth_plugin.authenticate(self) # Store the authentication results in the keyring for later requests if self.keyring_saver: self.keyring_saver.save(self) class BaseClient(object): """Top-level object to access the OpenStack API. This client uses :class:`HTTPClient` to send requests. :class:`HTTPClient` will handle a bunch of issues such as authentication. """ service_type = None endpoint_type = None # "publicURL" will be used cached_endpoint = None def __init__(self, http_client, extensions=None): self.http_client = http_client http_client.add_client(self) # Add in any extensions... if extensions: for extension in extensions: if extension.manager_class: setattr(self, extension.name, extension.manager_class(self)) def client_request(self, method, url, **kwargs): return self.http_client.client_request( self, method, url, **kwargs) def head(self, url, **kwargs): return self.client_request("HEAD", url, **kwargs) def get(self, url, **kwargs): return self.client_request("GET", url, **kwargs) def post(self, url, **kwargs): return self.client_request("POST", url, **kwargs) def put(self, url, **kwargs): return self.client_request("PUT", url, **kwargs) def delete(self, url, **kwargs): return self.client_request("DELETE", url, **kwargs) def patch(self, url, **kwargs): return self.client_request("PATCH", url, **kwargs) @staticmethod def get_class(api_name, version, version_map): """Returns the client class for the requested API version :param api_name: the name of the API, e.g. 'compute', 'image', etc :param version: the requested API version :param version_map: a dict of client classes keyed by version :rtype: a client class for the requested API version """ try: client_path = version_map[str(version)] except (KeyError, ValueError): msg = "Invalid %s client version '%s'. must be one of: %s" % ( (api_name, version, ', '.join(version_map.keys()))) raise exceptions.UnsupportedVersion(msg) return importutils.import_class(client_path) python-keystoneclient-0.7.1/keystoneclient/openstack/common/apiclient/auth.py0000664000175400017540000001560612315030450031037 0ustar jenkinsjenkins00000000000000# Copyright 2013 OpenStack Foundation # Copyright 2013 Spanish National Research Council. # 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. # E0202: An attribute inherited from %s hide this method # pylint: disable=E0202 import abc import argparse import os import six from stevedore import extension from keystoneclient.openstack.common.apiclient import exceptions _discovered_plugins = {} def discover_auth_systems(): """Discover the available auth-systems. This won't take into account the old style auth-systems. """ global _discovered_plugins _discovered_plugins = {} def add_plugin(ext): _discovered_plugins[ext.name] = ext.plugin ep_namespace = "keystoneclient.openstack.common.apiclient.auth" mgr = extension.ExtensionManager(ep_namespace) mgr.map(add_plugin) def load_auth_system_opts(parser): """Load options needed by the available auth-systems into a parser. This function will try to populate the parser with options from the available plugins. """ group = parser.add_argument_group("Common auth options") BaseAuthPlugin.add_common_opts(group) for name, auth_plugin in six.iteritems(_discovered_plugins): group = parser.add_argument_group( "Auth-system '%s' options" % name, conflict_handler="resolve") auth_plugin.add_opts(group) def load_plugin(auth_system): try: plugin_class = _discovered_plugins[auth_system] except KeyError: raise exceptions.AuthSystemNotFound(auth_system) return plugin_class(auth_system=auth_system) def load_plugin_from_args(args): """Load required plugin and populate it with options. Try to guess auth system if it is not specified. Systems are tried in alphabetical order. :type args: argparse.Namespace :raises: AuthPluginOptionsMissing """ auth_system = args.os_auth_system if auth_system: plugin = load_plugin(auth_system) plugin.parse_opts(args) plugin.sufficient_options() return plugin for plugin_auth_system in sorted(six.iterkeys(_discovered_plugins)): plugin_class = _discovered_plugins[plugin_auth_system] plugin = plugin_class() plugin.parse_opts(args) try: plugin.sufficient_options() except exceptions.AuthPluginOptionsMissing: continue return plugin raise exceptions.AuthPluginOptionsMissing(["auth_system"]) @six.add_metaclass(abc.ABCMeta) class BaseAuthPlugin(object): """Base class for authentication plugins. An authentication plugin needs to override at least the authenticate method to be a valid plugin. """ auth_system = None opt_names = [] common_opt_names = [ "auth_system", "username", "password", "tenant_name", "token", "auth_url", ] def __init__(self, auth_system=None, **kwargs): self.auth_system = auth_system or self.auth_system self.opts = dict((name, kwargs.get(name)) for name in self.opt_names) @staticmethod def _parser_add_opt(parser, opt): """Add an option to parser in two variants. :param opt: option name (with underscores) """ dashed_opt = opt.replace("_", "-") env_var = "OS_%s" % opt.upper() arg_default = os.environ.get(env_var, "") arg_help = "Defaults to env[%s]." % env_var parser.add_argument( "--os-%s" % dashed_opt, metavar="<%s>" % dashed_opt, default=arg_default, help=arg_help) parser.add_argument( "--os_%s" % opt, metavar="<%s>" % dashed_opt, help=argparse.SUPPRESS) @classmethod def add_opts(cls, parser): """Populate the parser with the options for this plugin. """ for opt in cls.opt_names: # use `BaseAuthPlugin.common_opt_names` since it is never # changed in child classes if opt not in BaseAuthPlugin.common_opt_names: cls._parser_add_opt(parser, opt) @classmethod def add_common_opts(cls, parser): """Add options that are common for several plugins. """ for opt in cls.common_opt_names: cls._parser_add_opt(parser, opt) @staticmethod def get_opt(opt_name, args): """Return option name and value. :param opt_name: name of the option, e.g., "username" :param args: parsed arguments """ return (opt_name, getattr(args, "os_%s" % opt_name, None)) def parse_opts(self, args): """Parse the actual auth-system options if any. This method is expected to populate the attribute `self.opts` with a dict containing the options and values needed to make authentication. """ self.opts.update(dict(self.get_opt(opt_name, args) for opt_name in self.opt_names)) def authenticate(self, http_client): """Authenticate using plugin defined method. The method usually analyses `self.opts` and performs a request to authentication server. :param http_client: client object that needs authentication :type http_client: HTTPClient :raises: AuthorizationFailure """ self.sufficient_options() self._do_authenticate(http_client) @abc.abstractmethod def _do_authenticate(self, http_client): """Protected method for authentication. """ def sufficient_options(self): """Check if all required options are present. :raises: AuthPluginOptionsMissing """ missing = [opt for opt in self.opt_names if not self.opts.get(opt)] if missing: raise exceptions.AuthPluginOptionsMissing(missing) @abc.abstractmethod def token_and_endpoint(self, endpoint_type, service_type): """Return token and endpoint. :param service_type: Service type of the endpoint :type service_type: string :param endpoint_type: Type of endpoint. Possible values: public or publicURL, internal or internalURL, admin or adminURL :type endpoint_type: string :returns: tuple of token and endpoint strings :raises: EndpointException """ python-keystoneclient-0.7.1/keystoneclient/openstack/common/apiclient/__init__.py0000664000175400017540000000000012315030450031613 0ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/openstack/common/importutils.py0000664000175400017540000000421512315030450030513 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Import related utilities and helper functions. """ import sys import traceback def import_class(import_str): """Returns a class from a string including module and class.""" mod_str, _sep, class_str = import_str.rpartition('.') try: __import__(mod_str) return getattr(sys.modules[mod_str], class_str) except (ValueError, AttributeError): raise ImportError('Class %s cannot be found (%s)' % (class_str, traceback.format_exception(*sys.exc_info()))) def import_object(import_str, *args, **kwargs): """Import a class and return an instance of it.""" return import_class(import_str)(*args, **kwargs) def import_object_ns(name_space, import_str, *args, **kwargs): """Tries to import object from default namespace. Imports a class and return an instance of it, first by trying to find the class in a default namespace, then failing back to a full path if not found in the default namespace. """ import_value = "%s.%s" % (name_space, import_str) try: return import_class(import_value)(*args, **kwargs) except ImportError: return import_class(import_str)(*args, **kwargs) def import_module(import_str): """Import a module.""" __import__(import_str) return sys.modules[import_str] def try_import(import_str, default=None): """Try to import a module and if it fails return default.""" try: return import_module(import_str) except ImportError: return default python-keystoneclient-0.7.1/keystoneclient/openstack/common/strutils.py0000664000175400017540000001654412315030450030021 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ System-level utilities and helper functions. """ import re import sys import unicodedata import six from keystoneclient.openstack.common.gettextutils import _ # noqa # Used for looking up extensions of text # to their 'multiplied' byte amount BYTE_MULTIPLIERS = { '': 1, 't': 1024 ** 4, 'g': 1024 ** 3, 'm': 1024 ** 2, 'k': 1024, } BYTE_REGEX = re.compile(r'(^-?\d+)(\D*)') TRUE_STRINGS = ('1', 't', 'true', 'on', 'y', 'yes') FALSE_STRINGS = ('0', 'f', 'false', 'off', 'n', 'no') SLUGIFY_STRIP_RE = re.compile(r"[^\w\s-]") SLUGIFY_HYPHENATE_RE = re.compile(r"[-\s]+") def int_from_bool_as_string(subject): """Interpret a string as a boolean and return either 1 or 0. Any string value in: ('True', 'true', 'On', 'on', '1') is interpreted as a boolean True. Useful for JSON-decoded stuff and config file parsing """ return bool_from_string(subject) and 1 or 0 def bool_from_string(subject, strict=False): """Interpret a string as a boolean. A case-insensitive match is performed such that strings matching 't', 'true', 'on', 'y', 'yes', or '1' are considered True and, when `strict=False`, anything else is considered False. Useful for JSON-decoded stuff and config file parsing. If `strict=True`, unrecognized values, including None, will raise a ValueError which is useful when parsing values passed in from an API call. Strings yielding False are 'f', 'false', 'off', 'n', 'no', or '0'. """ if not isinstance(subject, six.string_types): subject = str(subject) lowered = subject.strip().lower() if lowered in TRUE_STRINGS: return True elif lowered in FALSE_STRINGS: return False elif strict: acceptable = ', '.join( "'%s'" % s for s in sorted(TRUE_STRINGS + FALSE_STRINGS)) msg = _("Unrecognized value '%(val)s', acceptable values are:" " %(acceptable)s") % {'val': subject, 'acceptable': acceptable} raise ValueError(msg) else: return False def safe_decode(text, incoming=None, errors='strict'): """Decodes incoming str using `incoming` if they're not already unicode. :param incoming: Text's current encoding :param errors: Errors handling policy. See here for valid values http://docs.python.org/2/library/codecs.html :returns: text or a unicode `incoming` encoded representation of it. :raises TypeError: If text is not an instance of str """ if not isinstance(text, six.string_types): raise TypeError("%s can't be decoded" % type(text)) if isinstance(text, six.text_type): return text if not incoming: incoming = (sys.stdin.encoding or sys.getdefaultencoding()) try: return text.decode(incoming, errors) except UnicodeDecodeError: # Note(flaper87) If we get here, it means that # sys.stdin.encoding / sys.getdefaultencoding # didn't return a suitable encoding to decode # text. This happens mostly when global LANG # var is not set correctly and there's no # default encoding. In this case, most likely # python will use ASCII or ANSI encoders as # default encodings but they won't be capable # of decoding non-ASCII characters. # # Also, UTF-8 is being used since it's an ASCII # extension. return text.decode('utf-8', errors) def safe_encode(text, incoming=None, encoding='utf-8', errors='strict'): """Encodes incoming str/unicode using `encoding`. If incoming is not specified, text is expected to be encoded with current python's default encoding. (`sys.getdefaultencoding`) :param incoming: Text's current encoding :param encoding: Expected encoding for text (Default UTF-8) :param errors: Errors handling policy. See here for valid values http://docs.python.org/2/library/codecs.html :returns: text or a bytestring `encoding` encoded representation of it. :raises TypeError: If text is not an instance of str """ if not isinstance(text, six.string_types): raise TypeError("%s can't be encoded" % type(text)) if not incoming: incoming = (sys.stdin.encoding or sys.getdefaultencoding()) if isinstance(text, six.text_type): if six.PY3: return text.encode(encoding, errors).decode(incoming) else: return text.encode(encoding, errors) elif text and encoding != incoming: # Decode text before encoding it with `encoding` text = safe_decode(text, incoming, errors) if six.PY3: return text.encode(encoding, errors).decode(incoming) else: return text.encode(encoding, errors) return text def to_bytes(text, default=0): """Converts a string into an integer of bytes. Looks at the last characters of the text to determine what conversion is needed to turn the input text into a byte number. Supports "B, K(B), M(B), G(B), and T(B)". (case insensitive) :param text: String input for bytes size conversion. :param default: Default return value when text is blank. """ match = BYTE_REGEX.search(text) if match: magnitude = int(match.group(1)) mult_key_org = match.group(2) if not mult_key_org: return magnitude elif text: msg = _('Invalid string format: %s') % text raise TypeError(msg) else: return default mult_key = mult_key_org.lower().replace('b', '', 1) multiplier = BYTE_MULTIPLIERS.get(mult_key) if multiplier is None: msg = _('Unknown byte multiplier: %s') % mult_key_org raise TypeError(msg) return magnitude * multiplier def to_slug(value, incoming=None, errors="strict"): """Normalize string. Convert to lowercase, remove non-word characters, and convert spaces to hyphens. Inspired by Django's `slugify` filter. :param value: Text to slugify :param incoming: Text's current encoding :param errors: Errors handling policy. See here for valid values http://docs.python.org/2/library/codecs.html :returns: slugified unicode representation of `value` :raises TypeError: If text is not an instance of str """ value = safe_decode(value, incoming, errors) # NOTE(aababilov): no need to use safe_(encode|decode) here: # encodings are always "ascii", error handling is always "ignore" # and types are always known (first: unicode; second: str) value = unicodedata.normalize("NFKD", value).encode( "ascii", "ignore").decode("ascii") value = SLUGIFY_STRIP_RE.sub("", value).strip().lower() return SLUGIFY_HYPHENATE_RE.sub("-", value) python-keystoneclient-0.7.1/keystoneclient/openstack/common/timeutils.py0000664000175400017540000001406412315030450030142 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Time related utilities and helper functions. """ import calendar import datetime import time import iso8601 import six # ISO 8601 extended time format with microseconds _ISO8601_TIME_FORMAT_SUBSECOND = '%Y-%m-%dT%H:%M:%S.%f' _ISO8601_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S' PERFECT_TIME_FORMAT = _ISO8601_TIME_FORMAT_SUBSECOND def isotime(at=None, subsecond=False): """Stringify time in ISO 8601 format.""" if not at: at = utcnow() st = at.strftime(_ISO8601_TIME_FORMAT if not subsecond else _ISO8601_TIME_FORMAT_SUBSECOND) tz = at.tzinfo.tzname(None) if at.tzinfo else 'UTC' st += ('Z' if tz == 'UTC' else tz) return st def parse_isotime(timestr): """Parse time from ISO 8601 format.""" try: return iso8601.parse_date(timestr) except iso8601.ParseError as e: raise ValueError(six.text_type(e)) except TypeError as e: raise ValueError(six.text_type(e)) def strtime(at=None, fmt=PERFECT_TIME_FORMAT): """Returns formatted utcnow.""" if not at: at = utcnow() return at.strftime(fmt) def parse_strtime(timestr, fmt=PERFECT_TIME_FORMAT): """Turn a formatted time back into a datetime.""" return datetime.datetime.strptime(timestr, fmt) def normalize_time(timestamp): """Normalize time in arbitrary timezone to UTC naive object.""" offset = timestamp.utcoffset() if offset is None: return timestamp return timestamp.replace(tzinfo=None) - offset def is_older_than(before, seconds): """Return True if before is older than seconds.""" if isinstance(before, six.string_types): before = parse_strtime(before).replace(tzinfo=None) return utcnow() - before > datetime.timedelta(seconds=seconds) def is_newer_than(after, seconds): """Return True if after is newer than seconds.""" if isinstance(after, six.string_types): after = parse_strtime(after).replace(tzinfo=None) return after - utcnow() > datetime.timedelta(seconds=seconds) def utcnow_ts(): """Timestamp version of our utcnow function.""" if utcnow.override_time is None: # NOTE(kgriffs): This is several times faster # than going through calendar.timegm(...) return int(time.time()) return calendar.timegm(utcnow().timetuple()) def utcnow(): """Overridable version of utils.utcnow.""" if utcnow.override_time: try: return utcnow.override_time.pop(0) except AttributeError: return utcnow.override_time return datetime.datetime.utcnow() def iso8601_from_timestamp(timestamp): """Returns a iso8601 formated date from timestamp.""" return isotime(datetime.datetime.utcfromtimestamp(timestamp)) utcnow.override_time = None def set_time_override(override_time=None): """Overrides utils.utcnow. Make it return a constant time or a list thereof, one at a time. :param override_time: datetime instance or list thereof. If not given, defaults to the current UTC time. """ utcnow.override_time = override_time or datetime.datetime.utcnow() def advance_time_delta(timedelta): """Advance overridden time using a datetime.timedelta.""" assert(not utcnow.override_time is None) try: for dt in utcnow.override_time: dt += timedelta except TypeError: utcnow.override_time += timedelta def advance_time_seconds(seconds): """Advance overridden time by seconds.""" advance_time_delta(datetime.timedelta(0, seconds)) def clear_time_override(): """Remove the overridden time.""" utcnow.override_time = None def marshall_now(now=None): """Make an rpc-safe datetime with microseconds. Note: tzinfo is stripped, but not required for relative times. """ if not now: now = utcnow() return dict(day=now.day, month=now.month, year=now.year, hour=now.hour, minute=now.minute, second=now.second, microsecond=now.microsecond) def unmarshall_time(tyme): """Unmarshall a datetime dict.""" return datetime.datetime(day=tyme['day'], month=tyme['month'], year=tyme['year'], hour=tyme['hour'], minute=tyme['minute'], second=tyme['second'], microsecond=tyme['microsecond']) def delta_seconds(before, after): """Return the difference between two timing objects. Compute the difference in seconds between two date, time, or datetime objects (as a float, to microsecond resolution). """ delta = after - before return total_seconds(delta) def total_seconds(delta): """Return the total seconds of datetime.timedelta object. Compute total seconds of datetime.timedelta, datetime.timedelta doesn't have method total_seconds in Python2.6, calculate it manually. """ try: return delta.total_seconds() except AttributeError: return ((delta.days * 24 * 3600) + delta.seconds + float(delta.microseconds) / (10 ** 6)) def is_soon(dt, window): """Determines if time is going to happen in the next window seconds. :params dt: the time :params window: minimum seconds to remain to consider the time not soon :return: True if expiration is within the given duration """ soon = (utcnow() + datetime.timedelta(seconds=window)) return normalize_time(dt) <= soon python-keystoneclient-0.7.1/keystoneclient/openstack/common/jsonutils.py0000664000175400017540000001511712315030450030155 0ustar jenkinsjenkins00000000000000# Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. # Copyright 2011 Justin Santa Barbara # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. ''' JSON related utilities. This module provides a few things: 1) A handy function for getting an object down to something that can be JSON serialized. See to_primitive(). 2) Wrappers around loads() and dumps(). The dumps() wrapper will automatically use to_primitive() for you if needed. 3) This sets up anyjson to use the loads() and dumps() wrappers if anyjson is available. ''' import datetime import functools import inspect import itertools import json try: import xmlrpclib except ImportError: # NOTE(jaypipes): xmlrpclib was renamed to xmlrpc.client in Python3 # however the function and object call signatures # remained the same. This whole try/except block should # be removed and replaced with a call to six.moves once # six 1.4.2 is released. See http://bit.ly/1bqrVzu import xmlrpc.client as xmlrpclib import six from keystoneclient.openstack.common import gettextutils from keystoneclient.openstack.common import importutils from keystoneclient.openstack.common import timeutils netaddr = importutils.try_import("netaddr") _nasty_type_tests = [inspect.ismodule, inspect.isclass, inspect.ismethod, inspect.isfunction, inspect.isgeneratorfunction, inspect.isgenerator, inspect.istraceback, inspect.isframe, inspect.iscode, inspect.isbuiltin, inspect.isroutine, inspect.isabstract] _simple_types = (six.string_types + six.integer_types + (type(None), bool, float)) def to_primitive(value, convert_instances=False, convert_datetime=True, level=0, max_depth=3): """Convert a complex object into primitives. Handy for JSON serialization. We can optionally handle instances, but since this is a recursive function, we could have cyclical data structures. To handle cyclical data structures we could track the actual objects visited in a set, but not all objects are hashable. Instead we just track the depth of the object inspections and don't go too deep. Therefore, convert_instances=True is lossy ... be aware. """ # handle obvious types first - order of basic types determined by running # full tests on nova project, resulting in the following counts: # 572754 # 460353 # 379632 # 274610 # 199918 # 114200 # 51817 # 26164 # 6491 # 283 # 19 if isinstance(value, _simple_types): return value if isinstance(value, datetime.datetime): if convert_datetime: return timeutils.strtime(value) else: return value # value of itertools.count doesn't get caught by nasty_type_tests # and results in infinite loop when list(value) is called. if type(value) == itertools.count: return six.text_type(value) # FIXME(vish): Workaround for LP bug 852095. Without this workaround, # tests that raise an exception in a mocked method that # has a @wrap_exception with a notifier will fail. If # we up the dependency to 0.5.4 (when it is released) we # can remove this workaround. if getattr(value, '__module__', None) == 'mox': return 'mock' if level > max_depth: return '?' # The try block may not be necessary after the class check above, # but just in case ... try: recursive = functools.partial(to_primitive, convert_instances=convert_instances, convert_datetime=convert_datetime, level=level, max_depth=max_depth) if isinstance(value, dict): return dict((k, recursive(v)) for k, v in six.iteritems(value)) elif isinstance(value, (list, tuple)): return [recursive(lv) for lv in value] # It's not clear why xmlrpclib created their own DateTime type, but # for our purposes, make it a datetime type which is explicitly # handled if isinstance(value, xmlrpclib.DateTime): value = datetime.datetime(*tuple(value.timetuple())[:6]) if convert_datetime and isinstance(value, datetime.datetime): return timeutils.strtime(value) elif isinstance(value, gettextutils.Message): return value.data elif hasattr(value, 'iteritems'): return recursive(dict(value.iteritems()), level=level + 1) elif hasattr(value, '__iter__'): return recursive(list(value)) elif convert_instances and hasattr(value, '__dict__'): # Likely an instance of something. Watch for cycles. # Ignore class member vars. return recursive(value.__dict__, level=level + 1) elif netaddr and isinstance(value, netaddr.IPAddress): return six.text_type(value) else: if any(test(value) for test in _nasty_type_tests): return six.text_type(value) return value except TypeError: # Class objects are tricky since they may define something like # __iter__ defined but it isn't callable as list(). return six.text_type(value) def dumps(value, default=to_primitive, **kwargs): return json.dumps(value, default=default, **kwargs) def loads(s): return json.loads(s) def load(s): return json.load(s) try: import anyjson except ImportError: pass else: anyjson._modules.append((__name__, 'dumps', TypeError, 'loads', ValueError, 'load')) anyjson.force_implementation(__name__) python-keystoneclient-0.7.1/keystoneclient/openstack/common/gettextutils.py0000664000175400017540000003153512315030450030672 0ustar jenkinsjenkins00000000000000# Copyright 2012 Red Hat, Inc. # Copyright 2013 IBM Corp. # 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. """ gettext for openstack-common modules. Usual usage in an openstack.common module: from keystoneclient.openstack.common.gettextutils import _ """ import copy import gettext import logging import os import re try: import UserString as _userString except ImportError: import collections as _userString from babel import localedata import six _localedir = os.environ.get('keystoneclient'.upper() + '_LOCALEDIR') _t = gettext.translation('keystoneclient', localedir=_localedir, fallback=True) _AVAILABLE_LANGUAGES = {} USE_LAZY = False def enable_lazy(): """Convenience function for configuring _() to use lazy gettext Call this at the start of execution to enable the gettextutils._ function to use lazy gettext functionality. This is useful if your project is importing _ directly instead of using the gettextutils.install() way of importing the _ function. """ global USE_LAZY USE_LAZY = True def _(msg): if USE_LAZY: return Message(msg, 'keystoneclient') else: if six.PY3: return _t.gettext(msg) return _t.ugettext(msg) def install(domain, lazy=False): """Install a _() function using the given translation domain. Given a translation domain, install a _() function using gettext's install() function. The main difference from gettext.install() is that we allow overriding the default localedir (e.g. /usr/share/locale) using a translation-domain-specific environment variable (e.g. NOVA_LOCALEDIR). :param domain: the translation domain :param lazy: indicates whether or not to install the lazy _() function. The lazy _() introduces a way to do deferred translation of messages by installing a _ that builds Message objects, instead of strings, which can then be lazily translated into any available locale. """ if lazy: # NOTE(mrodden): Lazy gettext functionality. # # The following introduces a deferred way to do translations on # messages in OpenStack. We override the standard _() function # and % (format string) operation to build Message objects that can # later be translated when we have more information. # # Also included below is an example LocaleHandler that translates # Messages to an associated locale, effectively allowing many logs, # each with their own locale. def _lazy_gettext(msg): """Create and return a Message object. Lazy gettext function for a given domain, it is a factory method for a project/module to get a lazy gettext function for its own translation domain (i.e. nova, glance, cinder, etc.) Message encapsulates a string so that we can translate it later when needed. """ return Message(msg, domain) from six import moves moves.builtins.__dict__['_'] = _lazy_gettext else: localedir = '%s_LOCALEDIR' % domain.upper() if six.PY3: gettext.install(domain, localedir=os.environ.get(localedir)) else: gettext.install(domain, localedir=os.environ.get(localedir), unicode=True) class Message(_userString.UserString, object): """Class used to encapsulate translatable messages.""" def __init__(self, msg, domain): # _msg is the gettext msgid and should never change self._msg = msg self._left_extra_msg = '' self._right_extra_msg = '' self._locale = None self.params = None self.domain = domain @property def data(self): # NOTE(mrodden): this should always resolve to a unicode string # that best represents the state of the message currently localedir = os.environ.get(self.domain.upper() + '_LOCALEDIR') if self.locale: lang = gettext.translation(self.domain, localedir=localedir, languages=[self.locale], fallback=True) else: # use system locale for translations lang = gettext.translation(self.domain, localedir=localedir, fallback=True) if six.PY3: ugettext = lang.gettext else: ugettext = lang.ugettext full_msg = (self._left_extra_msg + ugettext(self._msg) + self._right_extra_msg) if self.params is not None: full_msg = full_msg % self.params return six.text_type(full_msg) @property def locale(self): return self._locale @locale.setter def locale(self, value): self._locale = value if not self.params: return # This Message object may have been constructed with one or more # Message objects as substitution parameters, given as a single # Message, or a tuple or Map containing some, so when setting the # locale for this Message we need to set it for those Messages too. if isinstance(self.params, Message): self.params.locale = value return if isinstance(self.params, tuple): for param in self.params: if isinstance(param, Message): param.locale = value return if isinstance(self.params, dict): for param in self.params.values(): if isinstance(param, Message): param.locale = value def _save_dictionary_parameter(self, dict_param): full_msg = self.data # look for %(blah) fields in string; # ignore %% and deal with the # case where % is first character on the line keys = re.findall('(?:[^%]|^)?%\((\w*)\)[a-z]', full_msg) # if we don't find any %(blah) blocks but have a %s if not keys and re.findall('(?:[^%]|^)%[a-z]', full_msg): # apparently the full dictionary is the parameter params = copy.deepcopy(dict_param) else: params = {} for key in keys: try: params[key] = copy.deepcopy(dict_param[key]) except TypeError: # cast uncopyable thing to unicode string params[key] = six.text_type(dict_param[key]) return params def _save_parameters(self, other): # we check for None later to see if # we actually have parameters to inject, # so encapsulate if our parameter is actually None if other is None: self.params = (other, ) elif isinstance(other, dict): self.params = self._save_dictionary_parameter(other) else: # fallback to casting to unicode, # this will handle the problematic python code-like # objects that cannot be deep-copied try: self.params = copy.deepcopy(other) except TypeError: self.params = six.text_type(other) return self # overrides to be more string-like def __unicode__(self): return self.data def __str__(self): if six.PY3: return self.__unicode__() return self.data.encode('utf-8') def __getstate__(self): to_copy = ['_msg', '_right_extra_msg', '_left_extra_msg', 'domain', 'params', '_locale'] new_dict = self.__dict__.fromkeys(to_copy) for attr in to_copy: new_dict[attr] = copy.deepcopy(self.__dict__[attr]) return new_dict def __setstate__(self, state): for (k, v) in state.items(): setattr(self, k, v) # operator overloads def __add__(self, other): copied = copy.deepcopy(self) copied._right_extra_msg += other.__str__() return copied def __radd__(self, other): copied = copy.deepcopy(self) copied._left_extra_msg += other.__str__() return copied def __mod__(self, other): # do a format string to catch and raise # any possible KeyErrors from missing parameters self.data % other copied = copy.deepcopy(self) return copied._save_parameters(other) def __mul__(self, other): return self.data * other def __rmul__(self, other): return other * self.data def __getitem__(self, key): return self.data[key] def __getslice__(self, start, end): return self.data.__getslice__(start, end) def __getattribute__(self, name): # NOTE(mrodden): handle lossy operations that we can't deal with yet # These override the UserString implementation, since UserString # uses our __class__ attribute to try and build a new message # after running the inner data string through the operation. # At that point, we have lost the gettext message id and can just # safely resolve to a string instead. ops = ['capitalize', 'center', 'decode', 'encode', 'expandtabs', 'ljust', 'lstrip', 'replace', 'rjust', 'rstrip', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill'] if name in ops: return getattr(self.data, name) else: return _userString.UserString.__getattribute__(self, name) def get_available_languages(domain): """Lists the available languages for the given translation domain. :param domain: the domain to get languages for """ if domain in _AVAILABLE_LANGUAGES: return copy.copy(_AVAILABLE_LANGUAGES[domain]) localedir = '%s_LOCALEDIR' % domain.upper() find = lambda x: gettext.find(domain, localedir=os.environ.get(localedir), languages=[x]) # NOTE(mrodden): en_US should always be available (and first in case # order matters) since our in-line message strings are en_US language_list = ['en_US'] # NOTE(luisg): Babel <1.0 used a function called list(), which was # renamed to locale_identifiers() in >=1.0, the requirements master list # requires >=0.9.6, uncapped, so defensively work with both. We can remove # this check when the master list updates to >=1.0, and update all projects list_identifiers = (getattr(localedata, 'list', None) or getattr(localedata, 'locale_identifiers')) locale_identifiers = list_identifiers() for i in locale_identifiers: if find(i) is not None: language_list.append(i) _AVAILABLE_LANGUAGES[domain] = language_list return copy.copy(language_list) def get_localized_message(message, user_locale): """Gets a localized version of the given message in the given locale. If the message is not a Message object the message is returned as-is. If the locale is None the message is translated to the default locale. :returns: the translated message in unicode, or the original message if it could not be translated """ translated = message if isinstance(message, Message): original_locale = message.locale message.locale = user_locale translated = six.text_type(message) message.locale = original_locale return translated class LocaleHandler(logging.Handler): """Handler that can have a locale associated to translate Messages. A quick example of how to utilize the Message class above. LocaleHandler takes a locale and a target logging.Handler object to forward LogRecord objects to after translating the internal Message. """ def __init__(self, locale, target): """Initialize a LocaleHandler :param locale: locale to use for translating messages :param target: logging.Handler object to forward LogRecord objects to after translation """ logging.Handler.__init__(self) self.locale = locale self.target = target def emit(self, record): if isinstance(record.msg, Message): # set the locale and resolve to a string record.msg.locale = self.locale self.target.emit(record) python-keystoneclient-0.7.1/keystoneclient/openstack/common/memorycache.py0000664000175400017540000000560012315030450030413 0ustar jenkinsjenkins00000000000000# Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. # 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. """Super simple fake memcache client.""" from oslo.config import cfg from keystoneclient.openstack.common import timeutils memcache_opts = [ cfg.ListOpt('memcached_servers', default=None, help='Memcached servers or None for in process cache.'), ] CONF = cfg.CONF CONF.register_opts(memcache_opts) def get_client(memcached_servers=None): client_cls = Client if not memcached_servers: memcached_servers = CONF.memcached_servers if memcached_servers: try: import memcache client_cls = memcache.Client except ImportError: pass return client_cls(memcached_servers, debug=0) class Client(object): """Replicates a tiny subset of memcached client interface.""" def __init__(self, *args, **kwargs): """Ignores the passed in args.""" self.cache = {} def get(self, key): """Retrieves the value for a key or None. This expunges expired keys during each get. """ now = timeutils.utcnow_ts() for k in list(self.cache): (timeout, _value) = self.cache[k] if timeout and now >= timeout: del self.cache[k] return self.cache.get(key, (0, None))[1] def set(self, key, value, time=0, min_compress_len=0): """Sets the value for a key.""" timeout = 0 if time != 0: timeout = timeutils.utcnow_ts() + time self.cache[key] = (timeout, value) return True def add(self, key, value, time=0, min_compress_len=0): """Sets the value for a key if it doesn't exist.""" if self.get(key) is not None: return False return self.set(key, value, time, min_compress_len) def incr(self, key, delta=1): """Increments the value for a key.""" value = self.get(key) if value is None: return None new_value = int(value) + delta self.cache[key] = (self.cache[key][0], str(new_value)) return new_value def delete(self, key, time=0): """Deletes the value associated with a key.""" if key in self.cache: del self.cache[key] python-keystoneclient-0.7.1/keystoneclient/openstack/common/__init__.py0000664000175400017540000000010312315030450027647 0ustar jenkinsjenkins00000000000000import six six.add_move(six.MovedModule('mox', 'mox', 'mox3.mox')) python-keystoneclient-0.7.1/keystoneclient/openstack/__init__.py0000664000175400017540000000000012315030450026353 0ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/httpclient.py0000664000175400017540000006226112315030450025024 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # Copyright 2011 Piston Cloud Computing, Inc. # Copyright 2011 Nebula, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ OpenStack Client interface. Handles the REST calls and responses. """ import logging from six.moves.urllib import parse as urlparse try: import keyring import pickle except ImportError: keyring = None pickle = None # Python 2.5 compat fix if not hasattr(urlparse, 'parse_qsl'): import cgi urlparse.parse_qsl = cgi.parse_qsl from keystoneclient import access from keystoneclient.auth import base from keystoneclient import baseclient from keystoneclient import exceptions from keystoneclient.openstack.common import jsonutils from keystoneclient import session as client_session from keystoneclient import utils _logger = logging.getLogger(__name__) # These variables are moved and using them via httpclient is deprecated. # Maintain here for compatibility. USER_AGENT = client_session.USER_AGENT request = client_session.request class HTTPClient(baseclient.Client, base.BaseAuthPlugin): @utils.positional(enforcement=utils.positional.WARN) def __init__(self, username=None, tenant_id=None, tenant_name=None, password=None, auth_url=None, region_name=None, endpoint=None, token=None, debug=False, auth_ref=None, use_keyring=False, force_new_token=False, stale_duration=None, user_id=None, user_domain_id=None, user_domain_name=None, domain_id=None, domain_name=None, project_id=None, project_name=None, project_domain_id=None, project_domain_name=None, trust_id=None, session=None, **kwargs): """Construct a new http client :param string user_id: User ID for authentication. (optional) :param string username: Username for authentication. (optional) :param string user_domain_id: User's domain ID for authentication. (optional) :param string user_domain_name: User's domain name for authentication. (optional) :param string password: Password for authentication. (optional) :param string domain_id: Domain ID for domain scoping. (optional) :param string domain_name: Domain name for domain scoping. (optional) :param string project_id: Project ID for project scoping. (optional) :param string project_name: Project name for project scoping. (optional) :param string project_domain_id: Project's domain ID for project scoping. (optional) :param string project_domain_name: Project's domain name for project scoping. (optional) :param string auth_url: Identity service endpoint for authorization. :param string region_name: Name of a region to select when choosing an endpoint from the service catalog. :param integer timeout: DEPRECATED: use session. (optional) :param string endpoint: A user-supplied endpoint URL for the identity service. Lazy-authentication is possible for API service calls if endpoint is set at instantiation. (optional) :param string token: Token for authentication. (optional) :param string cacert: DEPRECATED: use session. (optional) :param string key: DEPRECATED: use session. (optional) :param string cert: DEPRECATED: use session. (optional) :param boolean insecure: DEPRECATED: use session. (optional) :param string original_ip: DEPRECATED: use session. (optional) :param boolean debug: DEPRECATED: use logging configuration. (optional) :param dict auth_ref: To allow for consumers of the client to manage their own caching strategy, you may initialize a client with a previously captured auth_reference (token). If there are keyword arguments passed that also exist in auth_ref, the value from the argument will take precedence. :param boolean use_keyring: Enables caching auth_ref into keyring. default: False (optional) :param boolean force_new_token: Keyring related parameter, forces request for new token. default: False (optional) :param integer stale_duration: Gap in seconds to determine if token from keyring is about to expire. default: 30 (optional) :param string tenant_name: Tenant name. (optional) The tenant_name keyword argument is deprecated, use project_name instead. :param string tenant_id: Tenant id. (optional) The tenant_id keyword argument is deprecated, use project_id instead. :param string trust_id: Trust ID for trust scoping. (optional) :param object session: A Session object to be used for communicating with the identity service. """ # set baseline defaults self.user_id = None self.username = None self.user_domain_id = None self.user_domain_name = None self.domain_id = None self.domain_name = None self.project_id = None self.project_name = None self.project_domain_id = None self.project_domain_name = None self.region_name = None self.auth_url = None self._endpoint = None self._management_url = None self.trust_id = None # if loading from a dictionary passed in via auth_ref, # load values from AccessInfo parsing that dictionary if auth_ref: self.auth_ref = access.AccessInfo.factory(**auth_ref) self.version = self.auth_ref.version self.user_id = self.auth_ref.user_id self.username = self.auth_ref.username self.user_domain_id = self.auth_ref.user_domain_id self.domain_id = self.auth_ref.domain_id self.domain_name = self.auth_ref.domain_name self.project_id = self.auth_ref.project_id self.project_name = self.auth_ref.project_name self.project_domain_id = self.auth_ref.project_domain_id self.auth_url = self.auth_ref.auth_url[0] self._management_url = self.auth_ref.management_url[0] self.auth_token_from_user = self.auth_ref.auth_token self.trust_id = self.auth_ref.trust_id if self.auth_ref.has_service_catalog(): self.region_name = self.auth_ref.service_catalog.region_name else: self.auth_ref = None # allow override of the auth_ref defaults from explicit # values provided to the client # apply deprecated variables first, so modern variables override them if tenant_id: self.project_id = tenant_id if tenant_name: self.project_name = tenant_name # user-related attributes self.password = password if user_id: self.user_id = user_id if username: self.username = username if user_domain_id: self.user_domain_id = user_domain_id elif not (user_id or user_domain_name): self.user_domain_id = 'default' if user_domain_name: self.user_domain_name = user_domain_name # domain-related attributes if domain_id: self.domain_id = domain_id if domain_name: self.domain_name = domain_name # project-related attributes if project_id: self.project_id = project_id if project_name: self.project_name = project_name if project_domain_id: self.project_domain_id = project_domain_id elif not (project_id or project_domain_name): self.project_domain_id = 'default' if project_domain_name: self.project_domain_name = project_domain_name # trust-related attributes if trust_id: self.trust_id = trust_id # endpoint selection if auth_url: self.auth_url = auth_url.rstrip('/') if token: self.auth_token_from_user = token else: self.auth_token_from_user = None if endpoint: self._endpoint = endpoint.rstrip('/') if region_name: self.region_name = region_name self._auth_token = None if not session: session = client_session.Session.construct(kwargs) session.auth = self super(HTTPClient, self).__init__(session=session) self.domain = '' self.debug_log = debug # keyring setup if use_keyring and keyring is None: _logger.warning('Failed to load keyring modules.') self.use_keyring = use_keyring and keyring is not None self.force_new_token = force_new_token self.stale_duration = stale_duration or access.STALE_TOKEN_DURATION self.stale_duration = int(self.stale_duration) def get_token(self, session, **kwargs): return self.auth_token @property def auth_token(self): if self._auth_token: return self._auth_token if self.auth_ref: if self.auth_ref.will_expire_soon(self.stale_duration): self.authenticate() return self.auth_ref.auth_token if self.auth_token_from_user: return self.auth_token_from_user def get_endpoint(self, session, interface=None, **kwargs): if interface == 'public': return self.auth_url else: return self.management_url @auth_token.setter def auth_token(self, value): """Override the auth_token. If an application sets auth_token explicitly then it will always be used and override any past or future retrieved token. """ self._auth_token = value @auth_token.deleter def auth_token(self): self._auth_token = None self.auth_token_from_user = None @property def service_catalog(self): """Returns this client's service catalog.""" return self.auth_ref.service_catalog def has_service_catalog(self): """Returns True if this client provides a service catalog.""" return self.auth_ref.has_service_catalog() @property def tenant_id(self): """Provide read-only backwards compatibility for tenant_id. This is deprecated, use project_id instead. """ return self.project_id @property def tenant_name(self): """Provide read-only backwards compatibility for tenant_name. This is deprecated, use project_name instead. """ return self.project_name @utils.positional(enforcement=utils.positional.WARN) def authenticate(self, username=None, password=None, tenant_name=None, tenant_id=None, auth_url=None, token=None, user_id=None, domain_name=None, domain_id=None, project_name=None, project_id=None, user_domain_id=None, user_domain_name=None, project_domain_id=None, project_domain_name=None, trust_id=None, region_name=None): """Authenticate user. Uses the data provided at instantiation to authenticate against the Identity server. This may use either a username and password or token for authentication. If a tenant name or id was provided then the resulting authenticated client will be scoped to that tenant and contain a service catalog of available endpoints. With the v2.0 API, if a tenant name or ID is not provided, the authentication token returned will be 'unscoped' and limited in capabilities until a fully-scoped token is acquired. With the v3 API, if a domain name or id was provided then the resulting authenticated client will be scoped to that domain. If a project name or ID is not provided, and the authenticating user has a default project configured, the authentication token returned will be 'scoped' to the default project. Otherwise, the authentication token returned will be 'unscoped' and limited in capabilities until a fully-scoped token is acquired. With the v3 API, with the OS-TRUST extension enabled, the trust_id can be provided to allow project-specific role delegation between users If successful, sets the self.auth_ref and self.auth_token with the returned token. If not already set, will also set self.management_url from the details provided in the token. :returns: ``True`` if authentication was successful. :raises: AuthorizationFailure if unable to authenticate or validate the existing authorization token :raises: ValueError if insufficient parameters are used. If keyring is used, token is retrieved from keyring instead. Authentication will only be necessary if any of the following conditions are met: * keyring is not used * if token is not found in keyring * if token retrieved from keyring is expired or about to expired (as determined by stale_duration) * if force_new_token is true """ auth_url = auth_url or self.auth_url user_id = user_id or self.user_id username = username or self.username password = password or self.password user_domain_id = user_domain_id or self.user_domain_id user_domain_name = user_domain_name or self.user_domain_name domain_id = domain_id or self.domain_id domain_name = domain_name or self.domain_name project_id = project_id or tenant_id or self.project_id project_name = project_name or tenant_name or self.project_name project_domain_id = project_domain_id or self.project_domain_id project_domain_name = project_domain_name or self.project_domain_name trust_id = trust_id or self.trust_id region_name = region_name or self.region_name if not token: token = self.auth_token_from_user if (not token and self.auth_ref and not self.auth_ref.will_expire_soon(self.stale_duration)): token = self.auth_ref.auth_token kwargs = { 'auth_url': auth_url, 'user_id': user_id, 'username': username, 'user_domain_id': user_domain_id, 'user_domain_name': user_domain_name, 'domain_id': domain_id, 'domain_name': domain_name, 'project_id': project_id, 'project_name': project_name, 'project_domain_id': project_domain_id, 'project_domain_name': project_domain_name, 'token': token, 'trust_id': trust_id, } (keyring_key, auth_ref) = self.get_auth_ref_from_keyring(**kwargs) new_token_needed = False if auth_ref is None or self.force_new_token: new_token_needed = True kwargs['password'] = password resp = self.get_raw_token_from_identity_service(**kwargs) if isinstance(resp, access.AccessInfo): self.auth_ref = resp else: self.auth_ref = access.AccessInfo.factory(*resp) # NOTE(jamielennox): The original client relies on being able to # push the region name into the service catalog but new auth # it in. if region_name: self.auth_ref.service_catalog._region_name = region_name else: self.auth_ref = auth_ref self.process_token(region_name=region_name) if new_token_needed: self.store_auth_ref_into_keyring(keyring_key) return True def _build_keyring_key(self, **kwargs): """Create a unique key for keyring. Used to store and retrieve auth_ref from keyring. Returns a slash-separated string of values ordered by key name. """ return '/'.join([kwargs[k] or '?' for k in sorted(kwargs.keys())]) def get_auth_ref_from_keyring(self, **kwargs): """Retrieve auth_ref from keyring. If auth_ref is found in keyring, (keyring_key, auth_ref) is returned. Otherwise, (keyring_key, None) is returned. :returns: (keyring_key, auth_ref) or (keyring_key, None) :returns: or (None, None) if use_keyring is not set in the object """ keyring_key = None auth_ref = None if self.use_keyring: keyring_key = self._build_keyring_key(**kwargs) try: auth_ref = keyring.get_password("keystoneclient_auth", keyring_key) if auth_ref: auth_ref = pickle.loads(auth_ref) if auth_ref.will_expire_soon(self.stale_duration): # token has expired, don't use it auth_ref = None except Exception as e: auth_ref = None _logger.warning('Unable to retrieve token from keyring %s' % ( e)) return (keyring_key, auth_ref) def store_auth_ref_into_keyring(self, keyring_key): """Store auth_ref into keyring. """ if self.use_keyring: try: keyring.set_password("keystoneclient_auth", keyring_key, pickle.dumps(self.auth_ref)) except Exception as e: _logger.warning("Failed to store token into keyring %s" % (e)) def _process_management_url(self, region_name): try: self._management_url = self.auth_ref.service_catalog.url_for( service_type='identity', endpoint_type='admin', region_name=region_name) except exceptions.EndpointNotFound: _logger.warning("Failed to retrieve management_url from token") def process_token(self, region_name=None): """Extract and process information from the new auth_ref. And set the relevant authentication information. """ # if we got a response without a service catalog, set the local # list of tenants for introspection, and leave to client user # to determine what to do. Otherwise, load up the service catalog if self.auth_ref.project_scoped: if not self.auth_ref.tenant_id: raise exceptions.AuthorizationFailure( "Token didn't provide tenant_id") self._process_management_url(region_name) self.project_name = self.auth_ref.tenant_name self.project_id = self.auth_ref.tenant_id if not self.auth_ref.user_id: raise exceptions.AuthorizationFailure( "Token didn't provide user_id") self.user_id = self.auth_ref.user_id self.auth_domain_id = self.auth_ref.domain_id self.auth_tenant_id = self.auth_ref.tenant_id self.auth_user_id = self.auth_ref.user_id @property def management_url(self): return self._endpoint or self._management_url @management_url.setter def management_url(self, value): # NOTE(jamielennox): it's debatable here whether we should set # _endpoint or _management_url. As historically management_url was set # permanently setting _endpoint would better match that behaviour. self._endpoint = value @utils.positional(enforcement=utils.positional.WARN) def get_raw_token_from_identity_service(self, auth_url, username=None, password=None, tenant_name=None, tenant_id=None, token=None, user_id=None, user_domain_id=None, user_domain_name=None, domain_id=None, domain_name=None, project_id=None, project_name=None, project_domain_id=None, project_domain_name=None, trust_id=None): """Authenticate against the Identity API and get a token. Not implemented here because auth protocols should be API version-specific. Expected to authenticate or validate an existing authentication reference already associated with the client. Invoking this call *always* makes a call to the Identity service. :returns: (``resp``, ``body``) """ raise NotImplementedError def serialize(self, entity): return jsonutils.dumps(entity) @staticmethod def _decode_body(resp): if resp.text: try: body_resp = jsonutils.loads(resp.text) except (ValueError, TypeError): body_resp = None _logger.debug("Could not decode JSON from body: %s" % resp.text) else: _logger.debug("No body was returned.") body_resp = None return body_resp def request(self, url, method, **kwargs): """Send an http request with the specified characteristics. Wrapper around requests.request to handle tasks such as setting headers, JSON encoding/decoding, and error handling. """ try: kwargs['json'] = kwargs.pop('body') except KeyError: pass kwargs.setdefault('authenticated', False) resp = super(HTTPClient, self).request(url, method, **kwargs) return resp, self._decode_body(resp) def _cs_request(self, url, method, management=True, **kwargs): """Makes an authenticated request to keystone endpoint by concatenating self.management_url and url and passing in method and any associated kwargs. """ interface = 'admin' if management else 'public' endpoint_filter = kwargs.setdefault('endpoint_filter', {}) endpoint_filter.setdefault('service_type', 'identity') endpoint_filter.setdefault('interface', interface) if self.region_name: endpoint_filter.setdefault('region_name', self.region_name) kwargs.setdefault('authenticated', None) try: return self.request(url, method, **kwargs) except exceptions.MissingAuthPlugin: _logger.info('Cannot get authenticated endpoint without an ' 'auth plugin') raise exceptions.AuthorizationFailure( 'Current authorization does not have a known management url') def get(self, url, **kwargs): return self._cs_request(url, 'GET', **kwargs) def head(self, url, **kwargs): return self._cs_request(url, 'HEAD', **kwargs) def post(self, url, **kwargs): return self._cs_request(url, 'POST', **kwargs) def put(self, url, **kwargs): return self._cs_request(url, 'PUT', **kwargs) def patch(self, url, **kwargs): return self._cs_request(url, 'PATCH', **kwargs) def delete(self, url, **kwargs): return self._cs_request(url, 'DELETE', **kwargs) # DEPRECATIONS: The following methods are no longer directly supported # but maintained for compatibility purposes. deprecated_session_variables = {'original_ip': None, 'cert': None, 'timeout': None, 'verify_cert': 'verify'} def __getattr__(self, name): # FIXME(jamielennox): provide a proper deprecated warning try: var_name = self.deprecated_session_variables[name] except KeyError: raise AttributeError("Unknown Attribute: %s" % name) return getattr(self.session, var_name or name) def __setattr__(self, name, val): # FIXME(jamielennox): provide a proper deprecated warning try: var_name = self.deprecated_session_variables[name] except KeyError: super(HTTPClient, self).__setattr__(name, val) else: setattr(self.session, var_name or name) python-keystoneclient-0.7.1/keystoneclient/base.py0000664000175400017540000003017212315030450023554 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # Copyright 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Base utilities to build API operation managers and objects on top of. """ import abc import functools import six from six.moves import urllib from keystoneclient import exceptions from keystoneclient.openstack.common.apiclient import base def getid(obj): """Return id if argument is a Resource. Abstracts the common pattern of allowing both an object or an object's ID (UUID) as a parameter when dealing with relationships. """ try: if obj.uuid: return obj.uuid except AttributeError: pass try: return obj.id except AttributeError: return obj def filter_kwargs(f): @functools.wraps(f) def func(*args, **kwargs): new_kwargs = {} for key, ref in six.iteritems(kwargs): if ref is None: # drop null values continue id_value = getid(ref) if id_value != ref: # If an object with an id was passed, then use the id, e.g: # user: user(id=1) becomes user_id: 1 key = '%s_id' % key new_kwargs[key] = id_value return f(*args, **new_kwargs) return func class Manager(object): """Basic manager type providing common operations. Managers interact with a particular type of API (servers, flavors, images, etc.) and provide CRUD operations for them. """ resource_class = None def __init__(self, client): """Initializes Manager with `client`. :param client: instance of BaseClient descendant for HTTP requests """ super(Manager, self).__init__() self.client = client @property def api(self): """Deprecated. Use `client` instead. """ return self.client def _list(self, url, response_key, obj_class=None, body=None): """List the collection. :param url: a partial URL, e.g., '/servers' :param response_key: the key to be looked up in response dictionary, e.g., 'servers' :param obj_class: class for constructing the returned objects (self.resource_class will be used by default) :param body: data that will be encoded as JSON and passed in POST request (GET will be sent by default) """ if body: resp, body = self.client.post(url, body=body) else: resp, body = self.client.get(url) if obj_class is None: obj_class = self.resource_class data = body[response_key] # NOTE(ja): keystone returns values as list as {'values': [ ... ]} # unlike other services which just return the list... try: data = data['values'] except (KeyError, TypeError): pass return [obj_class(self, res, loaded=True) for res in data if res] def _get(self, url, response_key): """Get an object from collection. :param url: a partial URL, e.g., '/servers' :param response_key: the key to be looked up in response dictionary, e.g., 'server' """ resp, body = self.client.get(url) return self.resource_class(self, body[response_key], loaded=True) def _head(self, url): """Retrieve request headers for an object. :param url: a partial URL, e.g., '/servers' """ resp, body = self.client.head(url) return resp.status_code == 204 def _create(self, url, body, response_key, return_raw=False): """Deprecated. Use `_post` instead. """ return self._post(url, body, response_key, return_raw) def _post(self, url, body, response_key, return_raw=False): """Create an object. :param url: a partial URL, e.g., '/servers' :param body: data that will be encoded as JSON and passed in POST request (GET will be sent by default) :param response_key: the key to be looked up in response dictionary, e.g., 'servers' :param return_raw: flag to force returning raw JSON instead of Python object of self.resource_class """ resp, body = self.client.post(url, body=body) if return_raw: return body[response_key] return self.resource_class(self, body[response_key]) def _put(self, url, body=None, response_key=None): """Update an object with PUT method. :param url: a partial URL, e.g., '/servers' :param body: data that will be encoded as JSON and passed in POST request (GET will be sent by default) :param response_key: the key to be looked up in response dictionary, e.g., 'servers' """ resp, body = self.client.put(url, body=body) # PUT requests may not return a body if body is not None: if response_key is not None: return self.resource_class(self, body[response_key]) else: return self.resource_class(self, body) def _patch(self, url, body=None, response_key=None): """Update an object with PATCH method. :param url: a partial URL, e.g., '/servers' :param body: data that will be encoded as JSON and passed in POST request (GET will be sent by default) :param response_key: the key to be looked up in response dictionary, e.g., 'servers' """ resp, body = self.client.patch(url, body=body) if response_key is not None: return self.resource_class(self, body[response_key]) else: return self.resource_class(self, body) def _delete(self, url): """Delete an object. :param url: a partial URL, e.g., '/servers/my-server' """ return self.client.delete(url) def _update(self, url, body=None, response_key=None, method="PUT", management=True): methods = {"PUT": self.client.put, "POST": self.client.post, "PATCH": self.client.patch} try: resp, body = methods[method](url, body=body, management=management) except KeyError: raise exceptions.ClientException("Invalid update method: %s" % method) # PUT requests may not return a body if body: return self.resource_class(self, body[response_key]) @six.add_metaclass(abc.ABCMeta) class ManagerWithFind(Manager): """Manager with additional `find()`/`findall()` methods.""" @abc.abstractmethod def list(self): pass def find(self, **kwargs): """Find a single item with attributes matching ``**kwargs``. This isn't very efficient: it loads the entire list then filters on the Python side. """ rl = self.findall(**kwargs) num = len(rl) if num == 0: msg = "No %s matching %s." % (self.resource_class.__name__, kwargs) raise exceptions.NotFound(404, msg) elif num > 1: raise exceptions.NoUniqueMatch else: return rl[0] def findall(self, **kwargs): """Find all items with attributes matching ``**kwargs``. This isn't very efficient: it loads the entire list then filters on the Python side. """ found = [] searches = kwargs.items() for obj in self.list(): try: if all(getattr(obj, attr) == value for (attr, value) in searches): found.append(obj) except AttributeError: continue return found class CrudManager(Manager): """Base manager class for manipulating Keystone entities. Children of this class are expected to define a `collection_key` and `key`. - `collection_key`: Usually a plural noun by convention (e.g. `entities`); used to refer collections in both URL's (e.g. `/v3/entities`) and JSON objects containing a list of member resources (e.g. `{'entities': [{}, {}, {}]}`). - `key`: Usually a singular noun by convention (e.g. `entity`); used to refer to an individual member of the collection. """ collection_key = None key = None base_url = None def build_url(self, dict_args_in_out=None): """Builds a resource URL for the given kwargs. Given an example collection where `collection_key = 'entities'` and `key = 'entity'`, the following URL's could be generated. By default, the URL will represent a collection of entities, e.g.:: /entities If kwargs contains an `entity_id`, then the URL will represent a specific member, e.g.:: /entities/{entity_id} If a `base_url` is provided, the generated URL will be appended to it. """ if dict_args_in_out is None: dict_args_in_out = {} url = dict_args_in_out.pop('base_url', None) or self.base_url or '' url += '/%s' % self.collection_key # do we have a specific entity? entity_id = dict_args_in_out.pop('%s_id' % self.key, None) if entity_id is not None: url += '/%s' % entity_id return url @filter_kwargs def create(self, **kwargs): url = self.build_url(dict_args_in_out=kwargs) return self._create( url, {self.key: kwargs}, self.key) @filter_kwargs def get(self, **kwargs): return self._get( self.build_url(dict_args_in_out=kwargs), self.key) @filter_kwargs def head(self, **kwargs): return self._head(self.build_url(dict_args_in_out=kwargs)) @filter_kwargs def list(self, **kwargs): url = self.build_url(dict_args_in_out=kwargs) if kwargs: query = '?%s' % urllib.parse.urlencode(kwargs) else: query = '' return self._list( '%(url)s%(query)s' % { 'url': url, 'query': query, }, self.collection_key) @filter_kwargs def put(self, **kwargs): return self._update( self.build_url(dict_args_in_out=kwargs), method='PUT') @filter_kwargs def update(self, **kwargs): url = self.build_url(dict_args_in_out=kwargs) return self._update( url, {self.key: kwargs}, self.key, method='PATCH') @filter_kwargs def delete(self, **kwargs): return self._delete( self.build_url(dict_args_in_out=kwargs)) @filter_kwargs def find(self, **kwargs): """Find a single item with attributes matching ``**kwargs``.""" url = self.build_url(dict_args_in_out=kwargs) if kwargs: query = '?%s' % urllib.parse.urlencode(kwargs) else: query = '' rl = self._list( '%(url)s%(query)s' % { 'url': url, 'query': query, }, self.collection_key) num = len(rl) if num == 0: msg = "No %s matching %s." % (self.resource_class.__name__, kwargs) raise exceptions.NotFound(404, msg) elif num > 1: raise exceptions.NoUniqueMatch else: return rl[0] class Resource(base.Resource): """Base class for OpenStack resources (tenant, user, etc.). This is pretty much just a bag for attributes. """ def delete(self): return self.manager.delete(self) python-keystoneclient-0.7.1/keystoneclient/service_catalog.py0000664000175400017540000003022712315030450025775 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # Copyright 2011, Piston Cloud Computing, Inc. # Copyright 2011 Nebula, Inc. # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import abc import six from keystoneclient import exceptions from keystoneclient import utils @six.add_metaclass(abc.ABCMeta) class ServiceCatalog(object): """Helper methods for dealing with a Keystone Service Catalog.""" @classmethod def factory(cls, resource_dict, token=None, region_name=None): """Create ServiceCatalog object given a auth token.""" if ServiceCatalogV3.is_valid(resource_dict): return ServiceCatalogV3(token, resource_dict, region_name) elif ServiceCatalogV2.is_valid(resource_dict): return ServiceCatalogV2(resource_dict, region_name) else: raise NotImplementedError('Unrecognized auth response') def __init__(self, region_name=None): self._region_name = region_name @property def region_name(self): # FIXME(jamielennox): Having region_name set on the service catalog # directly is deprecated. It should instead be provided as a parameter # to calls made to the service_catalog. Provide appropriate warning. return self._region_name @abc.abstractmethod def get_token(self): """Fetch token details from service catalog. Returns a dictionary containing the following:: - `id`: Token's ID - `expires`: Token's expiration - `user_id`: Authenticated user's ID - `tenant_id`: Authorized project's ID - `domain_id`: Authorized domain's ID """ raise NotImplementedError() @abc.abstractmethod def _is_endpoint_type_match(self, endpoint, endpoint_type): """Helper function to normalize endpoint matching across v2 and v3. :returns: True if the provided endpoint matches the required endpoint_type otherwise False. """ @abc.abstractmethod def _normalize_endpoint_type(self, endpoint_type): """Handle differences in the way v2 and v3 catalogs specify endpoint. Both v2 and v3 must be able to handle the endpoint style of the other. For example v2 must be able to handle a 'public' endpoint_type and v3 must be able to handle a 'publicURL' endpoint_type. :returns: the endpoint string in the format appropriate for this service catalog. """ def get_endpoints(self, service_type=None, endpoint_type=None, region_name=None): """Fetch and filter endpoints for the specified service(s). Returns endpoints for the specified service (or all) containing the specified type (or all) and region (or all). """ endpoint_type = self._normalize_endpoint_type(endpoint_type) region_name = region_name or self._region_name sc = {} for service in (self.get_data() or []): try: st = service['type'] except KeyError: continue if service_type and service_type != st: continue sc[st] = [] for endpoint in service.get('endpoints', []): if (endpoint_type and not self._is_endpoint_type_match(endpoint, endpoint_type)): continue if region_name and region_name != endpoint.get('region'): continue sc[st].append(endpoint) return sc def _get_service_endpoints(self, attr, filter_value, service_type, endpoint_type, region_name): """Fetch the endpoints of a particular service_type and handle the filtering. """ sc_endpoints = self.get_endpoints(service_type=service_type, endpoint_type=endpoint_type, region_name=region_name) try: endpoints = sc_endpoints[service_type] except KeyError: return # TODO(jamielennox): at least swiftclient is known to set attr and not # filter_value and expects that to mean that filtering is ignored, so # we can't check for the presence of attr. This behaviour should be # deprecated and an appropriate warning provided. if filter_value: return [endpoint for endpoint in endpoints if endpoint.get(attr) == filter_value] return endpoints @abc.abstractmethod @utils.positional(enforcement=utils.positional.WARN) def get_urls(self, attr=None, filter_value=None, service_type='identity', endpoint_type='publicURL', region_name=None): """Fetch endpoint urls from the service catalog. Fetch the endpoints from the service catalog for a particular endpoint attribute. If no attribute is given, return the first endpoint of the specified type. :param string attr: Endpoint attribute name. :param string filter_value: Endpoint attribute value. :param string service_type: Service type of the endpoint. :param string endpoint_type: Type of endpoint. Possible values: public or publicURL, internal or internalURL, admin or adminURL :param string region_name: Region of the endpoint. :returns: tuple of urls or None (if no match found) """ raise NotImplementedError() @utils.positional(3, enforcement=utils.positional.WARN) def url_for(self, attr=None, filter_value=None, service_type='identity', endpoint_type='publicURL', region_name=None): """Fetch an endpoint from the service catalog. Fetch the specified endpoint from the service catalog for a particular endpoint attribute. If no attribute is given, return the first endpoint of the specified type. Valid endpoint types: `public` or `publicURL`, `internal` or `internalURL`, `admin` or 'adminURL` """ if not self.get_data(): raise exceptions.EmptyCatalog('The service catalog is empty.') urls = self.get_urls(attr=attr, filter_value=filter_value, service_type=service_type, endpoint_type=endpoint_type, region_name=region_name) try: return urls[0] except Exception: pass msg = '%(endpoint)s endpoint for %(service)s%(region)s not found' region = ' in %s region' % region_name if region_name else '' msg = msg % {'endpoint': endpoint_type, 'service': service_type, 'region': region} raise exceptions.EndpointNotFound(msg) @abc.abstractmethod def get_data(self): """Get the raw catalog structure. Get the version dependent catalog structure as it is presented within the resource. :returns: list containing raw catalog data entries or None """ raise NotImplementedError() class ServiceCatalogV2(ServiceCatalog): """An object for encapsulating the service catalog using raw v2 auth token from Keystone. """ def __init__(self, resource_dict, region_name=None): self.catalog = resource_dict super(ServiceCatalogV2, self).__init__(region_name=region_name) @classmethod def is_valid(cls, resource_dict): # This class is also used for reading token info of an unscoped token. # Unscoped token does not have 'serviceCatalog' in V2, checking this # will not work. Use 'token' attribute instead. return 'token' in resource_dict def _normalize_endpoint_type(self, endpoint_type): if endpoint_type and 'URL' not in endpoint_type: endpoint_type = endpoint_type + 'URL' return endpoint_type def _is_endpoint_type_match(self, endpoint, endpoint_type): return endpoint_type in endpoint def get_data(self): return self.catalog.get('serviceCatalog') def get_token(self): token = {'id': self.catalog['token']['id'], 'expires': self.catalog['token']['expires']} try: token['user_id'] = self.catalog['user']['id'] token['tenant_id'] = self.catalog['token']['tenant']['id'] except Exception: # just leave the tenant and user out if it doesn't exist pass return token @utils.positional(enforcement=utils.positional.WARN) def get_urls(self, attr=None, filter_value=None, service_type='identity', endpoint_type='publicURL', region_name=None): endpoint_type = self._normalize_endpoint_type(endpoint_type) endpoints = self._get_service_endpoints(attr=attr, filter_value=filter_value, service_type=service_type, endpoint_type=endpoint_type, region_name=region_name) if endpoints: return tuple([endpoint[endpoint_type] for endpoint in endpoints]) else: return None class ServiceCatalogV3(ServiceCatalog): """An object for encapsulating the service catalog using raw v3 auth token from Keystone. """ def __init__(self, token, resource_dict, region_name=None): super(ServiceCatalogV3, self).__init__(region_name=region_name) self._auth_token = token self.catalog = resource_dict @classmethod def is_valid(cls, resource_dict): # This class is also used for reading token info of an unscoped token. # Unscoped token does not have 'catalog', checking this # will not work. Use 'methods' attribute instead. return 'methods' in resource_dict def _normalize_endpoint_type(self, endpoint_type): if endpoint_type: endpoint_type = endpoint_type.rstrip('URL') return endpoint_type def _is_endpoint_type_match(self, endpoint, endpoint_type): try: return endpoint_type == endpoint['interface'] except KeyError: return False def get_data(self): return self.catalog.get('catalog') def get_token(self): token = {'id': self._auth_token, 'expires': self.catalog['expires_at']} try: token['user_id'] = self.catalog['user']['id'] domain = self.catalog.get('domain') if domain: token['domain_id'] = domain['id'] project = self.catalog.get('project') if project: token['tenant_id'] = project['id'] except Exception: # just leave the domain, project and user out if it doesn't exist pass return token @utils.positional(enforcement=utils.positional.WARN) def get_urls(self, attr=None, filter_value=None, service_type='identity', endpoint_type='public', region_name=None): endpoints = self._get_service_endpoints(attr=attr, filter_value=filter_value, service_type=service_type, endpoint_type=endpoint_type, region_name=region_name) if endpoints: return tuple([endpoint['url'] for endpoint in endpoints]) else: return None python-keystoneclient-0.7.1/keystoneclient/v2_0/0000775000175400017540000000000012315030547023042 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/v2_0/roles.py0000664000175400017540000000610012315030451024527 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # Copyright 2011 Nebula, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from keystoneclient import base class Role(base.Resource): """Represents a Keystone role.""" def __repr__(self): return "" % self._info def delete(self): return self.manager.delete(self) class RoleManager(base.ManagerWithFind): """Manager class for manipulating Keystone roles.""" resource_class = Role def get(self, role): return self._get("/OS-KSADM/roles/%s" % base.getid(role), "role") def create(self, name): """Create a role.""" params = {"role": {"name": name}} return self._create('/OS-KSADM/roles', params, "role") def delete(self, role): """Delete a role.""" return self._delete("/OS-KSADM/roles/%s" % base.getid(role)) def list(self): """List all available roles.""" return self._list("/OS-KSADM/roles", "roles") def roles_for_user(self, user, tenant=None): user_id = base.getid(user) if tenant: tenant_id = base.getid(tenant) route = "/tenants/%s/users/%s/roles" return self._list(route % (tenant_id, user_id), "roles") else: return self._list("/users/%s/roles" % user_id, "roles") def add_user_role(self, user, role, tenant=None): """Adds a role to a user. If tenant is specified, the role is added just for that tenant, otherwise the role is added globally. """ user_id = base.getid(user) role_id = base.getid(role) if tenant: route = "/tenants/%s/users/%s/roles/OS-KSADM/%s" params = (base.getid(tenant), user_id, role_id) return self._update(route % params, None, "role") else: route = "/users/%s/roles/OS-KSADM/%s" return self._update(route % (user_id, role_id), None, "roles") def remove_user_role(self, user, role, tenant=None): """Removes a role from a user. If tenant is specified, the role is removed just for that tenant, otherwise the role is removed from the user's global roles. """ user_id = base.getid(user) role_id = base.getid(role) if tenant: route = "/tenants/%s/users/%s/roles/OS-KSADM/%s" params = (base.getid(tenant), user_id, role_id) return self._delete(route % params) else: route = "/users/%s/roles/OS-KSADM/%s" return self._delete(route % (user_id, role_id)) python-keystoneclient-0.7.1/keystoneclient/v2_0/endpoints.py0000664000175400017540000000311312315030451025407 0ustar jenkinsjenkins00000000000000# Copyright 2012 Canonical Ltd. # 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. from keystoneclient import base class Endpoint(base.Resource): """Represents a Keystone endpoint.""" def __repr__(self): return "" % self._info class EndpointManager(base.ManagerWithFind): """Manager class for manipulating Keystone endpoints.""" resource_class = Endpoint def list(self): """List all available endpoints.""" return self._list('/endpoints', 'endpoints') def create(self, region, service_id, publicurl, adminurl, internalurl): """Create a new endpoint.""" body = {'endpoint': {'region': region, 'service_id': service_id, 'publicurl': publicurl, 'adminurl': adminurl, 'internalurl': internalurl}} return self._create('/endpoints', body, 'endpoint') def delete(self, id): """Delete an endpoint.""" return self._delete('/endpoints/%s' % id) python-keystoneclient-0.7.1/keystoneclient/v2_0/shell.py0000775000175400017540000004701212315030451024524 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # Copyright 2011 Nebula, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import argparse import getpass import sys import six from keystoneclient.openstack.common import strutils from keystoneclient import utils from keystoneclient.v2_0 import client CLIENT_CLASS = client.Client ASK_FOR_PASSWORD = object() def require_service_catalog(f): msg = ('Configuration error: Client configured to run without a service ' 'catalog. Run the client using --os-auth-url or OS_AUTH_URL, ' 'instead of --os-endpoint or OS_SERVICE_ENDPOINT, for example.') def wrapped(kc, args): if not kc.has_service_catalog(): raise Exception(msg) return f(kc, args) # Change __doc__ attribute back to origin function's __doc__ wrapped.__doc__ = f.__doc__ return wrapped @utils.arg('--tenant', '--tenant-id', metavar='', help='Tenant; lists all users if not specified.') @utils.arg('--tenant_id', help=argparse.SUPPRESS) def do_user_list(kc, args): """List users.""" if args.tenant: tenant_id = utils.find_resource(kc.tenants, args.tenant).id else: tenant_id = None users = kc.users.list(tenant_id=tenant_id) utils.print_list(users, ['id', 'name', 'enabled', 'email'], order_by='name') @utils.arg('user', metavar='', help='Name or ID of user to display.') def do_user_get(kc, args): """Display user details.""" user = utils.find_resource(kc.users, args.user) utils.print_dict(user._info) @utils.arg('--name', metavar='', required=True, help='New user name (must be unique).') @utils.arg('--tenant', '--tenant-id', metavar='', help='New user default tenant.') @utils.arg('--tenant_id', help=argparse.SUPPRESS) @utils.arg('--pass', metavar='', dest='passwd', nargs='?', const=ASK_FOR_PASSWORD, help='New user password; ' 'required for some auth backends.') @utils.arg('--email', metavar='', help='New user email address.') @utils.arg('--enabled', metavar='', default=True, help='Initial user enabled status. Default is true.') def do_user_create(kc, args): """Create new user""" if args.tenant: tenant_id = utils.find_resource(kc.tenants, args.tenant).id elif args.tenant_id: tenant_id = args.tenant_id else: tenant_id = None new_passwd = args.passwd if args.passwd is ASK_FOR_PASSWORD: new_passwd = utils.prompt_for_password() user = kc.users.create(args.name, new_passwd, args.email, tenant_id=tenant_id, enabled=strutils.bool_from_string(args.enabled)) utils.print_dict(user._info) @utils.arg('--name', metavar='', help='Desired new user name.') @utils.arg('--email', metavar='', help='Desired new email address.') @utils.arg('--enabled', metavar='', help='Enable or disable user.') @utils.arg('user', metavar='', help='Name or ID of user to update.') def do_user_update(kc, args): """Update user's name, email, and enabled status.""" kwargs = {} if args.name: kwargs['name'] = args.name if args.email is not None: kwargs['email'] = args.email if args.enabled: kwargs['enabled'] = strutils.bool_from_string(args.enabled) if not len(kwargs): print("User not updated, no arguments present.") return user = utils.find_resource(kc.users, args.user) try: kc.users.update(user, **kwargs) print('User has been updated.') except Exception as e: print('Unable to update user: %s' % e) @utils.arg('--pass', metavar='', dest='passwd', required=False, help='Desired new password.') @utils.arg('user', metavar='', help='Name or ID of user to update password.') def do_user_password_update(kc, args): """Update user password.""" user = utils.find_resource(kc.users, args.user) new_passwd = args.passwd or utils.prompt_for_password() if new_passwd is None: msg = ("\nPlease specify password using the --pass option " "or using the prompt") sys.exit(msg) kc.users.update_password(user, new_passwd) @utils.arg('--current-password', metavar='', dest='currentpasswd', required=False, help='Current password, ' 'Defaults to the password as set by --os-password or ' 'env[OS_PASSWORD].') @utils.arg('--new-password ', metavar='', dest='newpasswd', required=False, help='Desired new password.') def do_password_update(kc, args): """Update own password.""" # we are prompting for these passwords if they are not passed in # this gives users the option not to have their password # appear in bash history etc.. currentpasswd = args.os_password if args.currentpasswd is not None: currentpasswd = args.currentpasswd if currentpasswd is None: currentpasswd = getpass.getpass('Current Password: ') newpasswd = args.newpasswd while newpasswd is None: passwd1 = getpass.getpass('New Password: ') passwd2 = getpass.getpass('Repeat New Password: ') if passwd1 == passwd2: newpasswd = passwd1 kc.users.update_own_password(currentpasswd, newpasswd) if args.os_password != newpasswd: print("You should update the password you are using to authenticate " "to match your new password") @utils.arg('user', metavar='', help='Name or ID of user to delete.') def do_user_delete(kc, args): """Delete user.""" user = utils.find_resource(kc.users, args.user) kc.users.delete(user) def do_tenant_list(kc, args): """List all tenants.""" tenants = kc.tenants.list() utils.print_list(tenants, ['id', 'name', 'enabled'], order_by='name') @utils.arg('tenant', metavar='', help='Name or ID of tenant to display.') def do_tenant_get(kc, args): """Display tenant details.""" tenant = utils.find_resource(kc.tenants, args.tenant) utils.print_dict(tenant._info) @utils.arg('--name', metavar='', required=True, help='New tenant name (must be unique).') @utils.arg('--description', metavar='', default=None, help='Description of new tenant. Default is none.') @utils.arg('--enabled', metavar='', default=True, help='Initial tenant enabled status. Default is true.') def do_tenant_create(kc, args): """Create new tenant.""" tenant = kc.tenants.create(args.name, description=args.description, enabled=strutils.bool_from_string(args.enabled)) utils.print_dict(tenant._info) @utils.arg('--name', metavar='', help='Desired new name of tenant.') @utils.arg('--description', metavar='', default=None, help='Desired new description of tenant.') @utils.arg('--enabled', metavar='', help='Enable or disable tenant.') @utils.arg('tenant', metavar='', help='Name or ID of tenant to update.') def do_tenant_update(kc, args): """Update tenant name, description, enabled status.""" tenant = utils.find_resource(kc.tenants, args.tenant) kwargs = {} if args.name: kwargs.update({'name': args.name}) if args.description is not None: kwargs.update({'description': args.description}) if args.enabled: kwargs.update({'enabled': strutils.bool_from_string(args.enabled)}) if kwargs == {}: print("Tenant not updated, no arguments present.") return tenant.update(**kwargs) @utils.arg('tenant', metavar='', help='Name or ID of tenant to delete.') def do_tenant_delete(kc, args): """Delete tenant.""" tenant = utils.find_resource(kc.tenants, args.tenant) kc.tenants.delete(tenant) @utils.arg('--name', metavar='', required=True, help='Name of new service (must be unique).') @utils.arg('--type', metavar='', required=True, help='Service type (one of: identity, compute, network, ' 'image, object-store, or other service identifier string).') @utils.arg('--description', metavar='', help='Description of service.') def do_service_create(kc, args): """Add service to Service Catalog.""" service = kc.services.create(args.name, args.type, args.description) utils.print_dict(service._info) def do_service_list(kc, args): """List all services in Service Catalog.""" services = kc.services.list() utils.print_list(services, ['id', 'name', 'type', 'description'], order_by='name') @utils.arg('service', metavar='', help='Name or ID of service to display.') def do_service_get(kc, args): """Display service from Service Catalog.""" service = utils.find_resource(kc.services, args.service) utils.print_dict(service._info) @utils.arg('service', metavar='', help='Name or ID of service to delete.') def do_service_delete(kc, args): """Delete service from Service Catalog.""" service = utils.find_resource(kc.services, args.service) kc.services.delete(service.id) def do_role_list(kc, args): """List all roles.""" roles = kc.roles.list() utils.print_list(roles, ['id', 'name'], order_by='name') @utils.arg('role', metavar='', help='Name or ID of role to display.') def do_role_get(kc, args): """Display role details.""" role = utils.find_resource(kc.roles, args.role) utils.print_dict(role._info) @utils.arg('--name', metavar='', required=True, help='Name of new role.') def do_role_create(kc, args): """Create new role.""" role = kc.roles.create(args.name) utils.print_dict(role._info) @utils.arg('role', metavar='', help='Name or ID of role to delete.') def do_role_delete(kc, args): """Delete role.""" role = utils.find_resource(kc.roles, args.role) kc.roles.delete(role) @utils.arg('--user', '--user-id', '--user_id', metavar='', required=True, help='Name or ID of user.') @utils.arg('--role', '--role-id', '--role_id', metavar='', required=True, help='Name or ID of role.') @utils.arg('--tenant', '--tenant-id', metavar='', help='Name or ID of tenant.') @utils.arg('--tenant_id', help=argparse.SUPPRESS) def do_user_role_add(kc, args): """Add role to user.""" user = utils.find_resource(kc.users, args.user) role = utils.find_resource(kc.roles, args.role) if args.tenant: tenant = utils.find_resource(kc.tenants, args.tenant) elif args.tenant_id: tenant = args.tenant_id else: tenant = None kc.roles.add_user_role(user, role, tenant) @utils.arg('--user', '--user-id', '--user_id', metavar='', required=True, help='Name or ID of user.') @utils.arg('--role', '--role-id', '--role_id', metavar='', required=True, help='Name or ID of role.') @utils.arg('--tenant', '--tenant-id', metavar='', help='Name or ID of tenant.') @utils.arg('--tenant_id', help=argparse.SUPPRESS) def do_user_role_remove(kc, args): """Remove role from user.""" user = utils.find_resource(kc.users, args.user) role = utils.find_resource(kc.roles, args.role) if args.tenant: tenant = utils.find_resource(kc.tenants, args.tenant) elif args.tenant_id: tenant = args.tenant_id else: tenant = None kc.roles.remove_user_role(user, role, tenant) @utils.arg('--user', '--user-id', metavar='', help='List roles granted to specified user.') @utils.arg('--user_id', help=argparse.SUPPRESS) @utils.arg('--tenant', '--tenant-id', metavar='', help='List only roles granted on specified tenant.') @utils.arg('--tenant_id', help=argparse.SUPPRESS) def do_user_role_list(kc, args): """List roles granted to a user.""" if args.tenant: tenant_id = utils.find_resource(kc.tenants, args.tenant).id elif args.tenant_id: tenant_id = args.tenant_id else: # use the authenticated tenant id as a default tenant_id = kc.auth_tenant_id if args.user: user_id = utils.find_resource(kc.users, args.user).id elif args.user_id: user_id = args.user_id else: # use the authenticated user id as a default user_id = kc.auth_user_id roles = kc.roles.roles_for_user(user=user_id, tenant=tenant_id) # this makes the command output a bit more intuitive for role in roles: role.user_id = user_id role.tenant_id = tenant_id utils.print_list(roles, ['id', 'name', 'user_id', 'tenant_id'], order_by='name') @utils.arg('--user-id', metavar='', help='User ID for which to create credentials. If not specified, ' 'the authenticated user will be used.') @utils.arg('--user_id', help=argparse.SUPPRESS) @utils.arg('--tenant-id', metavar='', help='Tenant ID for which to to create credentials. If not ' 'specified, the authenticated tenant ID will be used.') @utils.arg('--tenant_id', help=argparse.SUPPRESS) def do_ec2_credentials_create(kc, args): """Create EC2-compatible credentials for user per tenant.""" if not args.tenant_id: # use the authenticated tenant id as a default args.tenant_id = kc.auth_tenant_id if not args.user_id: # use the authenticated user id as a default args.user_id = kc.auth_user_id credentials = kc.ec2.create(args.user_id, args.tenant_id) utils.print_dict(credentials._info) @utils.arg('--user-id', metavar='', help='User ID.') @utils.arg('--user_id', help=argparse.SUPPRESS) @utils.arg('--access', metavar='', required=True, help='Access Key.') def do_ec2_credentials_get(kc, args): """Display EC2-compatible credentials.""" if not args.user_id: # use the authenticated user id as a default args.user_id = kc.auth_user_id cred = kc.ec2.get(args.user_id, args.access) if cred: utils.print_dict(cred._info) @utils.arg('--user-id', metavar='', help='User ID.') @utils.arg('--user_id', help=argparse.SUPPRESS) def do_ec2_credentials_list(kc, args): """List EC2-compatible credentials for a user.""" if not args.user_id: # use the authenticated user id as a default args.user_id = kc.auth_user_id credentials = kc.ec2.list(args.user_id) for cred in credentials: try: cred.tenant = getattr(kc.tenants.get(cred.tenant_id), 'name') except Exception: # FIXME(dtroyer): Retrieving the tenant name fails for normal # users; stuff in the tenant_id instead. cred.tenant = cred.tenant_id utils.print_list(credentials, ['tenant', 'access', 'secret']) @utils.arg('--user-id', metavar='', help='User ID.') @utils.arg('--user_id', help=argparse.SUPPRESS) @utils.arg('--access', metavar='', required=True, help='Access Key.') def do_ec2_credentials_delete(kc, args): """Delete EC2-compatible credentials.""" if not args.user_id: # use the authenticated user id as a default args.user_id = kc.auth_user_id try: kc.ec2.delete(args.user_id, args.access) print('Credential has been deleted.') except Exception as e: print('Unable to delete credential: %s' % e) @utils.arg('--service', metavar='', default=None, help='Service type to return.') @require_service_catalog def do_catalog(kc, args): """List service catalog, possibly filtered by service.""" endpoints = kc.service_catalog.get_endpoints(service_type=args.service) for (service, service_endpoints) in six.iteritems(endpoints): if len(service_endpoints) > 0: print("Service: %s" % service) for ep in service_endpoints: utils.print_dict(ep) @utils.arg('--service', metavar='', required=True, help='Service type to select.') @utils.arg('--endpoint-type', metavar='', default='publicURL', help='Endpoint type to select.') @utils.arg('--endpoint_type', default='publicURL', help=argparse.SUPPRESS) @utils.arg('--attr', metavar='', help='Service attribute to match for selection.') @utils.arg('--value', metavar='', help='Value of attribute to match.') @require_service_catalog def do_endpoint_get(kc, args): """Find endpoint filtered by a specific attribute or service type.""" kwargs = { 'service_type': args.service, 'endpoint_type': args.endpoint_type, } if args.attr and args.value: kwargs.update({'attr': args.attr, 'filter_value': args.value}) elif args.attr or args.value: print('Both --attr and --value required.') return url = kc.service_catalog.url_for(**kwargs) utils.print_dict({'%s.%s' % (args.service, args.endpoint_type): url}) def do_endpoint_list(kc, args): """List configured service endpoints.""" endpoints = kc.endpoints.list() utils.print_list(endpoints, ['id', 'region', 'publicurl', 'internalurl', 'adminurl', 'service_id']) @utils.arg('--region', metavar='', help='Endpoint region.', default='regionOne') @utils.arg('--service', '--service-id', '--service_id', metavar='', required=True, help='Name or ID of service associated with endpoint.') @utils.arg('--publicurl', metavar='', required=True, help='Public URL endpoint.') @utils.arg('--adminurl', metavar='', help='Admin URL endpoint.') @utils.arg('--internalurl', metavar='', help='Internal URL endpoint.') def do_endpoint_create(kc, args): """Create a new endpoint associated with a service.""" service_id = utils.find_resource(kc.services, args.service).id endpoint = kc.endpoints.create(args.region, service_id, args.publicurl, args.adminurl, args.internalurl) utils.print_dict(endpoint._info) @utils.arg('id', metavar='', help='ID of endpoint to delete.') def do_endpoint_delete(kc, args): """Delete a service endpoint.""" try: kc.endpoints.delete(args.id) print('Endpoint has been deleted.') except Exception: print('Unable to delete endpoint.') @utils.arg('--wrap', metavar='', default=0, help='Wrap PKI tokens to a specified length, or 0 to disable.') @require_service_catalog def do_token_get(kc, args): """Display the current user token.""" utils.print_dict(kc.service_catalog.get_token(), wrap=int(args.wrap)) python-keystoneclient-0.7.1/keystoneclient/v2_0/ec2.py0000664000175400017540000000370112315030451024060 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from keystoneclient import base class EC2(base.Resource): def __repr__(self): return "" % self._info def delete(self): return self.manager.delete(self) class CredentialsManager(base.ManagerWithFind): resource_class = EC2 def create(self, user_id, tenant_id): """Create a new access/secret pair for the user/tenant pair. :rtype: object of type :class:`EC2` """ params = {'tenant_id': tenant_id} return self._create('/users/%s/credentials/OS-EC2' % user_id, params, "credential") def list(self, user_id): """Get a list of access/secret pairs for a user_id. :rtype: list of :class:`EC2` """ return self._list("/users/%s/credentials/OS-EC2" % user_id, "credentials") def get(self, user_id, access): """Get the access/secret pair for a given access key. :rtype: object of type :class:`EC2` """ return self._get("/users/%s/credentials/OS-EC2/%s" % (user_id, base.getid(access)), "credential") def delete(self, user_id, access): """Delete an access/secret pair for a user.""" return self._delete("/users/%s/credentials/OS-EC2/%s" % (user_id, base.getid(access))) python-keystoneclient-0.7.1/keystoneclient/v2_0/users.py0000664000175400017540000001056612315030451024557 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # Copyright 2011 Nebula, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from six.moves import urllib from keystoneclient import base class User(base.Resource): """Represents a Keystone user""" def __repr__(self): return "" % self._info def delete(self): return self.manager.delete(self) def list_roles(self, tenant=None): return self.manager.list_roles(self.id, base.getid(tenant)) class UserManager(base.ManagerWithFind): """Manager class for manipulating Keystone users.""" resource_class = User def get(self, user): return self._get("/users/%s" % base.getid(user), "user") def update(self, user, **kwargs): """Update user data. Supported arguments include ``name``, ``email``, and ``enabled``. """ # FIXME(gabriel): "tenantId" seems to be accepted by the API but # fails to actually update the default tenant. params = {"user": kwargs} params['user']['id'] = base.getid(user) url = "/users/%s" % base.getid(user) return self._update(url, params, "user") def update_enabled(self, user, enabled): """Update enabled-ness.""" params = {"user": {"id": base.getid(user), "enabled": enabled}} self._update("/users/%s/OS-KSADM/enabled" % base.getid(user), params, "user") def update_password(self, user, password): """Update password.""" params = {"user": {"id": base.getid(user), "password": password}} return self._update("/users/%s/OS-KSADM/password" % base.getid(user), params, "user") def update_own_password(self, origpasswd, passwd): """Update password.""" params = {"user": {"password": passwd, "original_password": origpasswd}} return self._update("/OS-KSCRUD/users/%s" % self.api.user_id, params, response_key="access", method="PATCH", management=False) def update_tenant(self, user, tenant): """Update default tenant.""" params = {"user": {"id": base.getid(user), "tenantId": base.getid(tenant)}} # FIXME(ja): seems like a bad url - default tenant is an attribute # not a subresource!??? return self._update("/users/%s/OS-KSADM/tenant" % base.getid(user), params, "user") def create(self, name, password, email=None, tenant_id=None, enabled=True): """Create a user.""" params = {"user": {"name": name, "password": password, "tenantId": tenant_id, "email": email, "enabled": enabled}} return self._create('/users', params, "user") def delete(self, user): """Delete a user.""" return self._delete("/users/%s" % base.getid(user)) def list(self, tenant_id=None, limit=None, marker=None): """Get a list of users (optionally limited to a tenant). :rtype: list of :class:`User` """ params = {} if limit: params['limit'] = int(limit) if marker: params['marker'] = marker query = "" if params: query = "?" + urllib.parse.urlencode(params) if not tenant_id: return self._list("/users%s" % query, "users") else: return self._list("/tenants/%s/users%s" % (tenant_id, query), "users") def list_roles(self, user, tenant=None): return self.api.roles.roles_for_user(base.getid(user), base.getid(tenant)) python-keystoneclient-0.7.1/keystoneclient/v2_0/tokens.py0000664000175400017540000000437412315030451024721 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from keystoneclient import base from keystoneclient import utils class Token(base.Resource): def __repr__(self): return "" % self._info @property def id(self): return self._info['token']['id'] @property def expires(self): return self._info['token']['expires'] @property def tenant(self): return self._info['token'].get('tenant') class TokenManager(base.Manager): resource_class = Token @utils.positional(enforcement=utils.positional.WARN) def authenticate(self, username=None, tenant_id=None, tenant_name=None, password=None, token=None, return_raw=False): if token: params = {"auth": {"token": {"id": token}}} elif username and password: params = {"auth": {"passwordCredentials": {"username": username, "password": password}}} else: raise ValueError('A username and password or token is required.') if tenant_id: params['auth']['tenantId'] = tenant_id elif tenant_name: params['auth']['tenantName'] = tenant_name reset = 0 if self.api.management_url is None: reset = 1 self.api.management_url = self.api.auth_url token_ref = self._create('/tokens', params, "access", return_raw=return_raw) if reset: self.api.management_url = None return token_ref def delete(self, token): return self._delete("/tokens/%s" % base.getid(token)) def endpoints(self, token): return self._get("/tokens/%s/endpoints" % base.getid(token), "token") python-keystoneclient-0.7.1/keystoneclient/v2_0/tenants.py0000664000175400017540000001326012315030451025064 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # Copyright 2011 Nebula, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import six from six.moves import urllib from keystoneclient import base class Tenant(base.Resource): """Represents a Keystone tenant Attributes: * id: a uuid that identifies the tenant * name: tenant name * description: tenant description * enabled: boolean to indicate if tenant is enabled """ def __repr__(self): return "" % self._info def delete(self): return self.manager.delete(self) def update(self, name=None, description=None, enabled=None): # Preserve the existing settings; keystone legacy resets these? new_name = name if name else self.name if description is not None: new_description = description else: new_description = self.description new_enabled = enabled if enabled is not None else self.enabled try: retval = self.manager.update(self.id, tenant_name=new_name, description=new_description, enabled=new_enabled) self = retval except Exception: retval = None return retval def add_user(self, user, role): return self.manager.api.roles.add_user_role(base.getid(user), base.getid(role), self.id) def remove_user(self, user, role): return self.manager.api.roles.remove_user_role(base.getid(user), base.getid(role), self.id) def list_users(self): return self.manager.list_users(self.id) class TenantManager(base.ManagerWithFind): """Manager class for manipulating Keystone tenants.""" resource_class = Tenant def get(self, tenant_id): return self._get("/tenants/%s" % tenant_id, "tenant") def create(self, tenant_name, description=None, enabled=True, **kwargs): """Create a new tenant.""" params = {"tenant": {"name": tenant_name, "description": description, "enabled": enabled}} #Allow Extras Passthru and ensure we don't clobber primary arguments. for k, v in six.iteritems(kwargs): if k not in params['tenant']: params['tenant'][k] = v return self._create('/tenants', params, "tenant") def list(self, limit=None, marker=None): """Get a list of tenants. :param integer limit: maximum number to return. (optional) :param string marker: use when specifying a limit and making multiple calls for querying. (optional) :rtype: list of :class:`Tenant` """ params = {} if limit: params['limit'] = limit if marker: params['marker'] = marker query = "" if params: query = "?" + urllib.parse.urlencode(params) reset = 0 if self.api.management_url is None: # special casing to allow tenant lists on the auth_url # for unscoped tokens reset = 1 self.api.management_url = self.api.auth_url tenant_list = self._list("/tenants%s" % query, "tenants") if reset: self.api.management_url = None return tenant_list def update(self, tenant_id, tenant_name=None, description=None, enabled=None, **kwargs): """Update a tenant with a new name and description.""" body = {"tenant": {'id': tenant_id}} if tenant_name is not None: body['tenant']['name'] = tenant_name if enabled is not None: body['tenant']['enabled'] = enabled if description is not None: body['tenant']['description'] = description #Allow Extras Passthru and ensure we don't clobber primary arguments. for k, v in six.iteritems(kwargs): if k not in body['tenant']: body['tenant'][k] = v # Keystone's API uses a POST rather than a PUT here. return self._create("/tenants/%s" % tenant_id, body, "tenant") def delete(self, tenant): """Delete a tenant.""" return self._delete("/tenants/%s" % (base.getid(tenant))) def list_users(self, tenant): """List users for a tenant.""" return self.api.users.list(base.getid(tenant)) def add_user(self, tenant, user, role): """Add a user to a tenant with the given role.""" return self.api.roles.add_user_role(base.getid(user), base.getid(role), base.getid(tenant)) def remove_user(self, tenant, user, role): """Remove the specified role from the user on the tenant.""" return self.api.roles.remove_user_role(base.getid(user), base.getid(role), base.getid(tenant)) python-keystoneclient-0.7.1/keystoneclient/v2_0/client.py0000664000175400017540000002005512315030451024666 0ustar jenkinsjenkins00000000000000# Copyright 2011 Nebula, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging from keystoneclient.auth.identity import v2 as v2_auth from keystoneclient import exceptions from keystoneclient import httpclient from keystoneclient.v2_0 import ec2 from keystoneclient.v2_0 import endpoints from keystoneclient.v2_0 import roles from keystoneclient.v2_0 import services from keystoneclient.v2_0 import tenants from keystoneclient.v2_0 import tokens from keystoneclient.v2_0 import users _logger = logging.getLogger(__name__) class Client(httpclient.HTTPClient): """Client for the OpenStack Keystone v2.0 API. :param string username: Username for authentication. (optional) :param string password: Password for authentication. (optional) :param string token: Token for authentication. (optional) :param string tenant_id: Tenant id. (optional) :param string tenant_name: Tenant name. (optional) :param string auth_url: Keystone service endpoint for authorization. :param string region_name: Name of a region to select when choosing an endpoint from the service catalog. :param string endpoint: A user-supplied endpoint URL for the keystone service. Lazy-authentication is possible for API service calls if endpoint is set at instantiation.(optional) :param integer timeout: Allows customization of the timeout for client http requests. (optional) :param string original_ip: The original IP of the requesting user which will be sent to Keystone in a 'Forwarded' header. (optional) :param string cert: Path to the Privacy Enhanced Mail (PEM) file which contains the corresponding X.509 client certificate needed to established two-way SSL connection with the identity service. (optional) :param string key: Path to the Privacy Enhanced Mail (PEM) file which contains the unencrypted client private key needed to established two-way SSL connection with the identity service. (optional) :param string cacert: Path to the Privacy Enhanced Mail (PEM) file which contains the trusted authority X.509 certificates needed to established SSL connection with the identity service. (optional) :param boolean insecure: Does not perform X.509 certificate validation when establishing SSL connection with identity service. default: False (optional) :param dict auth_ref: To allow for consumers of the client to manage their own caching strategy, you may initialize a client with a previously captured auth_reference (token) :param boolean debug: Enables debug logging of all request and responses to keystone. default False (option) .. warning:: If debug is enabled, it may show passwords in plain text as a part of its output. The client can be created and used like a user or in a strictly bootstrap mode. Normal operation expects a username, password, auth_url, and tenant_name or id to be provided. Other values will be lazily loaded as needed from the service catalog. Example:: >>> from keystoneclient.v2_0 import client >>> keystone = client.Client(username=USER, ... password=PASS, ... tenant_name=TENANT_NAME, ... auth_url=KEYSTONE_URL) >>> keystone.tenants.list() ... >>> user = keystone.users.get(USER_ID) >>> user.delete() Once authenticated, you can store and attempt to re-use the authenticated token. the auth_ref property on the client returns as a dictionary-like-object so that you can export and cache it, re-using it when initiating another client:: >>> from keystoneclient.v2_0 import client >>> keystone = client.Client(username=USER, ... password=PASS, ... tenant_name=TENANT_NAME, ... auth_url=KEYSTONE_URL) >>> auth_ref = keystone.auth_ref >>> # pickle or whatever you like here >>> new_client = client.Client(auth_ref=auth_ref) Alternatively, you can provide the administrative token configured in keystone and an endpoint to communicate with directly. See (``admin_token`` in ``keystone.conf``) In this case, authenticate() is not needed, and no service catalog will be loaded. Example:: >>> from keystoneclient.v2_0 import client >>> admin_client = client.Client( ... token='12345secret7890', ... endpoint='http://localhost:35357/v2.0') >>> keystone.tenants.list() """ version = 'v2.0' def __init__(self, **kwargs): """Initialize a new client for the Keystone v2.0 API.""" super(Client, self).__init__(**kwargs) self.endpoints = endpoints.EndpointManager(self) self.roles = roles.RoleManager(self) self.services = services.ServiceManager(self) self.tenants = tenants.TenantManager(self) self.tokens = tokens.TokenManager(self) self.users = users.UserManager(self) # extensions self.ec2 = ec2.CredentialsManager(self) # DEPRECATED: if session is passed then we go to the new behaviour of # authenticating on the first required call. if not kwargs.get('session') and self.management_url is None: self.authenticate() def get_raw_token_from_identity_service(self, auth_url, username=None, password=None, tenant_name=None, tenant_id=None, token=None, project_name=None, project_id=None, trust_id=None, **kwargs): """Authenticate against the v2 Identity API. :returns: access.AccessInfo if authentication was successful. :raises: AuthorizationFailure if unable to authenticate or validate the existing authorization token """ try: if auth_url is None: raise ValueError("Cannot authenticate without an auth_url") a = v2_auth.Auth._factory(auth_url, username=username, password=password, token=token, trust_id=trust_id, tenant_id=project_id or tenant_id, tenant_name=project_name or tenant_name) return a.get_auth_ref(self.session) except (exceptions.AuthorizationFailure, exceptions.Unauthorized): _logger.debug("Authorization Failed.") raise except exceptions.EndpointNotFound: msg = 'There was no suitable authentication url for this request' raise exceptions.AuthorizationFailure(msg) except Exception as e: raise exceptions.AuthorizationFailure("Authorization Failed: " "%s" % e) python-keystoneclient-0.7.1/keystoneclient/v2_0/__init__.py0000664000175400017540000000013312315030451025142 0ustar jenkinsjenkins00000000000000# flake8: noqa from keystoneclient.v2_0.client import Client __all__ = [ 'client', ] python-keystoneclient-0.7.1/keystoneclient/v2_0/services.py0000664000175400017540000000324612315030451025236 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # Copyright 2011 Nebula, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from keystoneclient import base class Service(base.Resource): """Represents a Keystone service.""" def __repr__(self): return "" % self._info class ServiceManager(base.ManagerWithFind): """Manager class for manipulating Keystone services.""" resource_class = Service def list(self): """List available services.""" return self._list("/OS-KSADM/services", "OS-KSADM:services") def get(self, id): """Retrieve a service by id.""" return self._get("/OS-KSADM/services/%s" % id, "OS-KSADM:service") def create(self, name, service_type, description): """Create a new service.""" body = {"OS-KSADM:service": {'name': name, 'type': service_type, 'description': description}} return self._create("/OS-KSADM/services", body, "OS-KSADM:service") def delete(self, id): """Delete a service.""" return self._delete("/OS-KSADM/services/%s" % id) python-keystoneclient-0.7.1/keystoneclient/contrib/0000775000175400017540000000000012315030547023734 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/contrib/ec2/0000775000175400017540000000000012315030547024405 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/contrib/ec2/utils.py0000664000175400017540000002620712315030450026117 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. # Copyright 2011 - 2012 Justin Santa Barbara # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import base64 import hashlib import hmac import re import six from six.moves import urllib class Ec2Signer(object): """Utility class which adds allows a request to be signed with an AWS style signature, which can then be used for authentication via the keystone ec2 authentication extension. """ def __init__(self, secret_key): self.secret_key = secret_key.encode() self.hmac = hmac.new(self.secret_key, digestmod=hashlib.sha1) if hashlib.sha256: self.hmac_256 = hmac.new(self.secret_key, digestmod=hashlib.sha256) def _v4_creds(self, credentials): """Detect if the credentials are for a v4 signed request, since AWS removed the SignatureVersion field from the v4 request spec... This expects a dict of the request headers to be passed in the credentials dict, since the recommended way to pass v4 creds is via the 'Authorization' header see http://docs.aws.amazon.com/general/latest/gr/ sigv4-signed-request-examples.html Alternatively X-Amz-Algorithm can be specified as a query parameter, and the authentication data can also passed as query parameters. Note a hash of the request body is also required in the credentials for v4 auth to work in the body_hash key, calculated via: hashlib.sha256(req.body).hexdigest() """ try: auth_str = credentials['headers']['Authorization'] if auth_str.startswith('AWS4-HMAC-SHA256'): return True except KeyError: # Alternatively the Authorization data can be passed via # the query params list, check X-Amz-Algorithm=AWS4-HMAC-SHA256 try: if (credentials['params']['X-Amz-Algorithm'] == 'AWS4-HMAC-SHA256'): return True except KeyError: pass return False def generate(self, credentials): """Generate auth string according to what SignatureVersion is given.""" signature_version = credentials['params'].get('SignatureVersion') if signature_version == '0': return self._calc_signature_0(credentials['params']) if signature_version == '1': return self._calc_signature_1(credentials['params']) if signature_version == '2': return self._calc_signature_2(credentials['params'], credentials['verb'], credentials['host'], credentials['path']) if self._v4_creds(credentials): return self._calc_signature_4(credentials['params'], credentials['verb'], credentials['host'], credentials['path'], credentials['headers'], credentials['body_hash']) if signature_version is not None: raise Exception('Unknown signature version: %s' % signature_version) else: raise Exception('Unexpected signature format') @staticmethod def _get_utf8_value(value): """Get the UTF8-encoded version of a value.""" if not isinstance(value, (six.binary_type, six.text_type)): value = str(value) if isinstance(value, six.text_type): return value.encode('utf-8') else: return value def _calc_signature_0(self, params): """Generate AWS signature version 0 string.""" s = (params['Action'] + params['Timestamp']).encode('utf-8') self.hmac.update(s) return base64.b64encode(self.hmac.digest()).decode('utf-8') def _calc_signature_1(self, params): """Generate AWS signature version 1 string.""" keys = list(params) keys.sort(key=six.text_type.lower) for key in keys: self.hmac.update(key.encode('utf-8')) val = self._get_utf8_value(params[key]) self.hmac.update(val) return base64.b64encode(self.hmac.digest()).decode('utf-8') @staticmethod def _canonical_qs(params): """Construct a sorted, correctly encoded query string as required for _calc_signature_2 and _calc_signature_4. """ keys = list(params) keys.sort() pairs = [] for key in keys: val = Ec2Signer._get_utf8_value(params[key]) val = urllib.parse.quote(val, safe='-_~') pairs.append(urllib.parse.quote(key, safe='') + '=' + val) qs = '&'.join(pairs) return qs def _calc_signature_2(self, params, verb, server_string, path): """Generate AWS signature version 2 string.""" string_to_sign = '%s\n%s\n%s\n' % (verb, server_string, path) if self.hmac_256: current_hmac = self.hmac_256 params['SignatureMethod'] = 'HmacSHA256' else: current_hmac = self.hmac params['SignatureMethod'] = 'HmacSHA1' string_to_sign += self._canonical_qs(params) current_hmac.update(string_to_sign.encode('utf-8')) b64 = base64.b64encode(current_hmac.digest()).decode('utf-8') return b64 def _calc_signature_4(self, params, verb, server_string, path, headers, body_hash): """Generate AWS signature version 4 string.""" def sign(key, msg): return hmac.new(key, self._get_utf8_value(msg), hashlib.sha256).digest() def signature_key(datestamp, region_name, service_name): """Signature key derivation, see http://docs.aws.amazon.com/general/latest/gr/ signature-v4-examples.html#signature-v4-examples-python """ k_date = sign(self._get_utf8_value(b"AWS4" + self.secret_key), datestamp) k_region = sign(k_date, region_name) k_service = sign(k_region, service_name) k_signing = sign(k_service, "aws4_request") return k_signing def auth_param(param_name): """Get specified auth parameter. Provided via one of: - the Authorization header - the X-Amz-* query parameters """ try: auth_str = headers['Authorization'] param_str = auth_str.partition( '%s=' % param_name)[2].split(',')[0] except KeyError: param_str = params.get('X-Amz-%s' % param_name) return param_str def date_param(): """Get the X-Amz-Date' value, which can be either a header or parameter. Note AWS supports parsing the Date header also, but this is not currently supported here as it will require some format mangling So the X-Amz-Date value must be YYYYMMDDTHHMMSSZ format, then it can be used to match against the YYYYMMDD format provided in the credential scope. see: http://docs.aws.amazon.com/general/latest/gr/ sigv4-date-handling.html """ try: return headers['X-Amz-Date'] except KeyError: return params.get('X-Amz-Date') def canonical_header_str(): # Get the list of headers to include, from either # - the Authorization header (SignedHeaders key) # - the X-Amz-SignedHeaders query parameter headers_lower = dict((k.lower().strip(), v.strip()) for (k, v) in six.iteritems(headers)) # Boto versions < 2.9.3 strip the port component of the host:port # header, so detect the user-agent via the header and strip the # port if we detect an old boto version. FIXME: remove when all # distros package boto >= 2.9.3, this is a transitional workaround user_agent = headers_lower.get('user-agent', '') strip_port = re.match('Boto/2.[0-9].[0-2]', user_agent) header_list = [] sh_str = auth_param('SignedHeaders') for h in sh_str.split(';'): if h not in headers_lower: continue if h == 'host' and strip_port: header_list.append('%s:%s' % (h, headers_lower[h].split(':')[0])) continue header_list.append('%s:%s' % (h, headers_lower[h])) return '\n'.join(header_list) + '\n' # Create canonical request: # http://docs.aws.amazon.com/general/latest/gr/ # sigv4-create-canonical-request.html # Get parameters and headers in expected string format cr = "\n".join((verb.upper(), path, self._canonical_qs(params), canonical_header_str(), auth_param('SignedHeaders'), body_hash)) # Check the date, reject any request where the X-Amz-Date doesn't # match the credential scope credential = auth_param('Credential') credential_split = credential.split('/') credential_scope = '/'.join(credential_split[1:]) credential_date = credential_split[1] param_date = date_param() if not param_date.startswith(credential_date): raise Exception('Request date mismatch error') # Create the string to sign # http://docs.aws.amazon.com/general/latest/gr/ # sigv4-create-string-to-sign.html cr = cr.encode('utf-8') string_to_sign = '\n'.join(('AWS4-HMAC-SHA256', param_date, credential_scope, hashlib.sha256(cr).hexdigest())) # Calculate the derived key, this requires a datestamp, region # and service, which can be extracted from the credential scope (req_region, req_service) = credential_split[2:4] s_key = signature_key(credential_date, req_region, req_service) # Finally calculate the signature! signature = hmac.new(s_key, self._get_utf8_value(string_to_sign), hashlib.sha256).hexdigest() return signature python-keystoneclient-0.7.1/keystoneclient/contrib/ec2/__init__.py0000664000175400017540000000000012315030450026475 0ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/contrib/bootstrap/0000775000175400017540000000000012315030547025751 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/contrib/bootstrap/shell.py0000664000175400017540000000346712315030450027435 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from keystoneclient import utils from keystoneclient.v2_0 import client @utils.arg('--user-name', metavar='', default='admin', dest='user', help='The name of the user to be created (default="admin").') @utils.arg('--pass', metavar='', required=True, dest='passwd', help='The password for the new user.') @utils.arg('--role-name', metavar='', default='admin', dest='role', help='The name of the role to be created and granted to the user ' '(default="admin").') @utils.arg('--tenant-name', metavar='', default='admin', dest='tenant', help='The name of the tenant to be created (default="admin").') def do_bootstrap(kc, args): """Grants a new role to a new user on a new tenant, after creating each.""" tenant = kc.tenants.create(tenant_name=args.tenant) role = kc.roles.create(name=args.role) user = kc.users.create(name=args.user, password=args.passwd, email=None) kc.roles.add_user_role(user=user, role=role, tenant=tenant) # verify the result user_client = client.Client( username=args.user, password=args.passwd, tenant_name=args.tenant, auth_url=kc.management_url) user_client.authenticate() python-keystoneclient-0.7.1/keystoneclient/contrib/bootstrap/__init__.py0000664000175400017540000000000012315030450030041 0ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/contrib/__init__.py0000664000175400017540000000000012315030450026024 0ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/discover.py0000664000175400017540000004434712315030450024471 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging import six from keystoneclient import exceptions from keystoneclient import session as client_session from keystoneclient.v2_0 import client as v2_client from keystoneclient.v3 import client as v3_client _logger = logging.getLogger(__name__) class _KeystoneVersion(object): """A factory object that holds all the information to create a client. Instances of this class are callable objects that hold all the kwargs that were passed to discovery so that a user may simply call it to create a new client object. Additional arguments passed to the call will override or add to those provided to the object. """ _CLIENT_VERSIONS = {2: v2_client.Client, 3: v3_client.Client} def __init__(self, version, status, client_class=None, **kwargs): """Create a new discovered version object. :param tuple version: the version of the available API. :param string status: the stability of the API. :param Class client_class: the client class that should be used to instantiate against this version of the API. (optional, will be matched against known) :param dict **kwargs: Additional arguments that should be passed on to the client when it is constructed. """ self.version = version self.status = status self.client_class = client_class self.client_kwargs = kwargs if not self.client_class: try: self.client_class = self._CLIENT_VERSIONS[self.version[0]] except KeyError: raise exceptions.DiscoveryFailure("No client available " "for version: %s" % self.version) def __lt__(self, other): """Version Ordering. Versions are ordered by major, then minor version number, then 'stable' is deemed the highest possible status, then they are just treated alphabetically (alpha < beta etc) """ if self.version == other.version: if self.status == 'stable': return False elif other.status == 'stable': return True else: return self.status < other.status return self.version < other.version def __eq__(self, other): return self.version == other.version and self.status == other.status def create_client(self, **kwargs): if kwargs: client_kwargs = self.client_kwargs.copy() client_kwargs.update(kwargs) else: client_kwargs = self.client_kwargs return self.client_class(**client_kwargs) def __call__(self, **kwargs): return self.create_client(**kwargs) @property def _str_ver(self): ver = ".".join([str(v) for v in self.version]) if self.status != 'stable': ver = "%s-%s" % (ver, self.status) return ver def _normalize_version_number(version): """Turn a version representation into a tuple.""" # trim the v from a 'v2.0' or similar try: version = version.lstrip('v') except AttributeError: pass # if it's an integer or a numeric as a string then normalize it # to a string, this ensures 1 decimal point try: num = float(version) except Exception: pass else: version = str(num) # if it's a string (or an integer) from above break it on . try: return tuple(map(int, version.split("."))) except Exception: pass # last attempt, maybe it's a list or iterable. try: return tuple(map(int, version)) except Exception: pass raise TypeError("Invalid version specified: %s" % version) def available_versions(url, session=None, **kwargs): headers = {'Accept': 'application/json'} if not session: session = client_session.Session.construct(kwargs) resp = session.get(url, headers=headers) try: body_resp = resp.json() except ValueError: pass else: # In the event of querying a root URL we will get back a list of # available versions. try: return body_resp['versions']['values'] except (KeyError, TypeError): pass # Most servers don't have a 'values' element so accept a simple # versions dict if available. try: return body_resp['versions'] except KeyError: pass # Otherwise if we query an endpoint like /v2.0 then we will get back # just the one available version. try: return [body_resp['version']] except KeyError: pass raise exceptions.DiscoveryFailure("Invalid Response - Bad version" " data returned: %s" % resp.text) class Discover(object): """A means to discover and create clients depending on the supported API versions on the server. Querying the server is done on object creation and every subsequent method operates upon the data that was retrieved. """ def __init__(self, session=None, **kwargs): """Construct a new discovery object. The connection parameters associated with this method are the same format and name as those used by a client (see keystoneclient.v2_0.client.Client and keystoneclient.v3.client.Client). If not overridden in subsequent methods they will also be what is passed to the constructed client. In the event that auth_url and endpoint is provided then auth_url will be used in accordance with how the client operates. The initialization process also queries the server. :param Session session: A session object that will be used for communication. Clients will also be constructed with this session. :param string auth_url: Identity service endpoint for authorization. (optional) :param string endpoint: A user-supplied endpoint URL for the identity service. (optional) :param string original_ip: The original IP of the requesting user which will be sent to identity service in a 'Forwarded' header. (optional) DEPRECATED: use the session object. This is ignored if a session is provided. :param boolean debug: Enables debug logging of all request and responses to the identity service. default False (optional) DEPRECATED: use the session object. This is ignored if a session is provided. :param string cacert: Path to the Privacy Enhanced Mail (PEM) file which contains the trusted authority X.509 certificates needed to established SSL connection with the identity service. (optional) DEPRECATED: use the session object. This is ignored if a session is provided. :param string key: Path to the Privacy Enhanced Mail (PEM) file which contains the unencrypted client private key needed to established two-way SSL connection with the identity service. (optional) DEPRECATED: use the session object. This is ignored if a session is provided. :param string cert: Path to the Privacy Enhanced Mail (PEM) file which contains the corresponding X.509 client certificate needed to established two-way SSL connection with the identity service. (optional) DEPRECATED: use the session object. This is ignored if a session is provided. :param boolean insecure: Does not perform X.509 certificate validation when establishing SSL connection with identity service. default: False (optional) DEPRECATED: use the session object. This is ignored if a session is provided. """ if not session: session = client_session.Session.construct(kwargs) kwargs['session'] = session url = kwargs.get('endpoint') or kwargs.get('auth_url') if not url: raise exceptions.DiscoveryFailure('Not enough information to ' 'determine URL. Provide either ' 'auth_url or endpoint') self._client_kwargs = kwargs self._available_versions = available_versions(url, session=session) def _get_client_constructor_kwargs(self, kwargs_dict={}, **kwargs): client_kwargs = self._client_kwargs.copy() client_kwargs.update(kwargs_dict) client_kwargs.update(**kwargs) return client_kwargs def available_versions(self, unstable=False): """Return a list of identity APIs available on the server and the data associated with them. :param bool unstable: Accept endpoints not marked 'stable'. (optional) :returns: A List of dictionaries as presented by the server. Each dict will contain the version and the URL to use for the version. It is a direct representation of the layout presented by the identity API. Example:: >>> from keystoneclient import discover >>> disc = discover.Discovery(auth_url='http://localhost:5000') >>> disc.available_versions() [{'id': 'v3.0', 'links': [{'href': u'http://127.0.0.1:5000/v3/', 'rel': u'self'}], 'media-types': [ {'base': 'application/json', 'type': 'application/vnd.openstack.identity-v3+json'}, {'base': 'application/xml', 'type': 'application/vnd.openstack.identity-v3+xml'}], 'status': 'stable', 'updated': '2013-03-06T00:00:00Z'}, {'id': 'v2.0', 'links': [{'href': u'http://127.0.0.1:5000/v2.0/', 'rel': u'self'}, {'href': u'...', 'rel': u'describedby', 'type': u'application/pdf'}], 'media-types': [ {'base': 'application/json', 'type': 'application/vnd.openstack.identity-v2.0+json'}, {'base': 'application/xml', 'type': 'application/vnd.openstack.identity-v2.0+xml'}], 'status': 'stable', 'updated': '2013-03-06T00:00:00Z'}] """ if unstable: # no need to determine the stable endpoints, just return everything return self._available_versions versions = [] for v in self._available_versions: try: status = v['status'] except KeyError: _logger.warning("Skipping over invalid version data. " "No stability status in version.") else: if status == 'stable': versions.append(v) return versions def _get_factory_from_response_entry(self, version_data, **kwargs): """Create a _KeystoneVersion factory object from a version response entry returned from a server. """ try: version_str = version_data['id'] status = version_data['status'] if not version_str.startswith('v'): raise exceptions.DiscoveryFailure('Skipping over invalid ' 'version string: %s. It ' 'should start with a v.' % version_str) for link in version_data['links']: # NOTE(jamielennox): there are plenty of links like with # documentation and such, we only care about the self # which is a link to the URL we should use. if link['rel'].lower() == 'self': version_number = _normalize_version_number(version_str) version_url = link['href'] break else: raise exceptions.DiscoveryFailure("Didn't find any links " "in version data.") except (KeyError, TypeError, ValueError): raise exceptions.DiscoveryFailure('Skipping over invalid ' 'version data.') # NOTE(jamielennox): the url might be the auth_url or the endpoint # depending on what was passed initially. Order is important, endpoint # needs to override auth_url. for url_type in ('auth_url', 'endpoint'): if self._client_kwargs.get(url_type, False): kwargs[url_type] = version_url else: kwargs[url_type] = None return _KeystoneVersion(status=status, version=version_number, **kwargs) def _available_clients(self, unstable=False, **kwargs): """Return a dictionary of factory functions for available API versions. :returns: A dictionary of available API endpoints with the version number as a tuple as the key, and a factory object that can be used to create an appropriate client as the value. To use the returned factory you simply call it with the parameters to pass to keystoneclient. These parameters will override those saved in the factory. Example:: >>> from keystoneclient import client >>> available_clients = client._available_clients(auth_url=url, ... **kwargs) >>> try: ... v2_factory = available_clients[(2, 0)] ... except KeyError: ... print "Version 2.0 unavailable" ... else: ... v2_client = v2_factory(token='abcdef') ... v2_client.tenants.list() :raises: DiscoveryFailure if the response is invalid :raises: VersionNotAvailable if a suitable client cannot be found. """ versions = dict() response_values = self.available_versions(unstable=unstable) client_kwargs = self._get_client_constructor_kwargs(kwargs_dict=kwargs) for version_data in response_values: try: v = self._get_factory_from_response_entry(version_data, **client_kwargs) except exceptions.DiscoveryFailure as e: _logger.warning("Invalid entry: %s", e, exc_info=True) else: versions[v.version] = v return versions def create_client(self, version=None, **kwargs): """Factory function to create a new identity service client. :param tuple version: The required version of the identity API. If specified the client will be selected such that the major version is equivalent and an endpoint provides at least the specified minor version. For example to specify the 3.1 API use (3, 1). (optional) :param bool unstable: Accept endpoints not marked 'stable'. (optional) :param kwargs: Additional arguments will override those provided to this object's constructor. :returns: An instantiated identity client object. :raises: DiscoveryFailure if the server response is invalid :raises: VersionNotAvailable if a suitable client cannot be found. """ versions = self._available_clients(**kwargs) chosen = None if version: version = _normalize_version_number(version) for keystone_version in six.itervalues(versions): # major versions must be the same (eg even though v2 is a lower # version than v3 we can't use it if v2 was requested) if version[0] != keystone_version.version[0]: continue # prevent selecting a minor version less than what is required if version <= keystone_version.version: chosen = keystone_version break elif versions: # if no version specified pick the latest one chosen = max(six.iteritems(versions))[1] if not chosen: msg = "Could not find a suitable endpoint" if version: msg = "%s for client version: %s" % (msg, version) if versions: available = ", ".join([v._str_ver for v in six.itervalues(versions)]) msg = "%s. Available_versions are: %s" % (msg, available) else: msg = "%s. No versions reported available" % msg raise exceptions.VersionNotAvailable(msg) return chosen.create_client() python-keystoneclient-0.7.1/keystoneclient/exceptions.py0000664000175400017540000000332112315030450025017 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 Nebula, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Exception definitions. """ #flake8: noqa from keystoneclient.apiclient.exceptions import * class CertificateConfigError(Exception): """Error reading the certificate""" def __init__(self, output): self.output = output msg = ("Unable to load certificate. " "Ensure your system is configured properly.") super(CertificateConfigError, self).__init__(msg) class ConnectionError(ClientException): """Something went wrong trying to connect to a server""" class SSLError(ConnectionError): """An SSL error occurred.""" class Timeout(ClientException): """The request timed out.""" class DiscoveryFailure(ClientException): """Discovery of client versions failed.""" class VersionNotAvailable(DiscoveryFailure): """Discovery failed as the version you requested is not available.""" class MissingAuthPlugin(ClientException): """An authenticated request is required but no plugin available.""" class NoMatchingPlugin(ClientException): """There were no auth plugins that could be created from the parameters provided.""" python-keystoneclient-0.7.1/keystoneclient/baseclient.py0000664000175400017540000000244112315030450024751 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. class Client(object): def __init__(self, session): self.session = session def request(self, url, method, **kwargs): kwargs.setdefault('authenticated', True) return self.session.request(url, method, **kwargs) def get(self, url, **kwargs): return self.request(url, 'GET', **kwargs) def head(self, url, **kwargs): return self.request(url, 'HEAD', **kwargs) def post(self, url, **kwargs): return self.request(url, 'POST', **kwargs) def put(self, url, **kwargs): return self.request(url, 'PUT', **kwargs) def patch(self, url, **kwargs): return self.request(url, 'PATCH', **kwargs) def delete(self, url, **kwargs): return self.request(url, 'DELETE', **kwargs) python-keystoneclient-0.7.1/keystoneclient/client.py0000664000175400017540000000414312315030450024117 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from keystoneclient import discover from keystoneclient import httpclient from keystoneclient import session as client_session # Using client.HTTPClient is deprecated. Use httpclient.HTTPClient instead. HTTPClient = httpclient.HTTPClient def Client(version=None, unstable=False, session=None, **kwargs): """Factory function to create a new identity service client. :param tuple version: The required version of the identity API. If specified the client will be selected such that the major version is equivalent and an endpoint provides at least the specified minor version. For example to specify the 3.1 API use (3, 1). :param bool unstable: Accept endpoints not marked as 'stable'. (optional) :param Session session: A session object to be used for communication. If one is not provided it will be constructed from the provided kwargs. (optional) :param kwargs: Additional arguments are passed through to the client that is being created. :returns: New keystone client object (keystoneclient.v2_0.Client or keystoneclient.v3.Client). :raises: DiscoveryFailure if the server's response is invalid :raises: VersionNotAvailable if a suitable client cannot be found. """ if not session: session = client_session.Session.construct(kwargs) d = discover.Discover(session=session, **kwargs) return d.create_client(version=version, unstable=unstable) python-keystoneclient-0.7.1/keystoneclient/generic/0000775000175400017540000000000012315030547023710 5ustar jenkinsjenkins00000000000000python-keystoneclient-0.7.1/keystoneclient/generic/shell.py0000664000175400017540000000334412315030450025366 0ustar jenkinsjenkins00000000000000# Copyright 2010 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import six from keystoneclient.generic import client from keystoneclient import utils CLIENT_CLASS = client.Client @utils.unauthenticated def do_discover(cs, args): """Discover Keystone servers, supported API versions and extensions. """ if cs.endpoint: versions = cs.discover(cs.endpoint) elif cs.auth_url: versions = cs.discover(cs.auth_url) else: versions = cs.discover() if versions: if 'message' in versions: print(versions['message']) for key, version in six.iteritems(versions): if key != 'message': print(" - supports version %s (%s) here %s" % (version['id'], version['status'], version['url'])) extensions = cs.discover_extensions(version['url']) if extensions: for key, extension in six.iteritems(extensions): if key != 'message': print(" - and %s: %s" % (key, extension)) else: print("No Keystone-compatible endpoint found") python-keystoneclient-0.7.1/keystoneclient/generic/client.py0000664000175400017540000001716012315030450025536 0ustar jenkinsjenkins00000000000000# Copyright 2010 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging from six.moves.urllib import parse as urlparse from keystoneclient import exceptions from keystoneclient import httpclient _logger = logging.getLogger(__name__) class Client(httpclient.HTTPClient): """Client for the OpenStack Keystone pre-version calls API. :param string endpoint: A user-supplied endpoint URL for the keystone service. :param integer timeout: Allows customization of the timeout for client http requests. (optional) Example:: >>> from keystoneclient.generic import client >>> root = client.Client(auth_url=KEYSTONE_URL) >>> versions = root.discover() ... >>> from keystoneclient.v2_0 import client as v2client >>> keystone = v2client.Client(auth_url=versions['v2.0']['url']) ... >>> user = keystone.users.get(USER_ID) >>> user.delete() """ def __init__(self, endpoint=None, **kwargs): """Initialize a new client for the Keystone v2.0 API.""" super(Client, self).__init__(endpoint=endpoint, **kwargs) self.endpoint = endpoint def discover(self, url=None): """Discover Keystone servers and return API versions supported. :param url: optional url to test (without version) Returns:: { 'message': 'Keystone found at http://127.0.0.1:5000/', 'v2.0': { 'status': 'beta', 'url': 'http://127.0.0.1:5000/v2.0/', 'id': 'v2.0' }, } """ if url: return self._check_keystone_versions(url) else: return self._local_keystone_exists() def _local_keystone_exists(self): """Checks if Keystone is available on default local port 35357.""" results = self._check_keystone_versions("http://localhost:35357") if results is None: results = self._check_keystone_versions("https://localhost:35357") return results def _check_keystone_versions(self, url): """Calls Keystone URL and detects the available API versions.""" try: resp, body = self.request(url, "GET", headers={'Accept': 'application/json'}) # Multiple Choices status code is returned by the root # identity endpoint, with references to one or more # Identity API versions -- v3 spec # some cases we get No Content if resp.status_code in (200, 204, 300): try: results = {} if 'version' in body: results['message'] = "Keystone found at %s" % url version = body['version'] # Stable/diablo incorrect format id, status, version_url = \ self._get_version_info(version, url) results[str(id)] = {"id": id, "status": status, "url": version_url} return results elif 'versions' in body: # Correct format results['message'] = "Keystone found at %s" % url for version in body['versions']['values']: id, status, version_url = \ self._get_version_info(version, url) results[str(id)] = {"id": id, "status": status, "url": version_url} return results else: results['message'] = ("Unrecognized response from %s" % url) return results except KeyError: raise exceptions.AuthorizationFailure() elif resp.status_code == 305: return self._check_keystone_versions(resp['location']) else: raise exceptions.from_response(resp, "GET", url) except Exception as e: _logger.exception(e) def discover_extensions(self, url=None): """Discover Keystone extensions supported. :param url: optional url to test (should have a version in it) Returns:: { 'message': 'Keystone extensions at http://127.0.0.1:35357/v2', 'OS-KSEC2': 'OpenStack EC2 Credentials Extension', } """ if url: return self._check_keystone_extensions(url) def _check_keystone_extensions(self, url): """Calls Keystone URL and detects the available extensions.""" try: if not url.endswith("/"): url += '/' resp, body = self.request("%sextensions" % url, "GET", headers={'Accept': 'application/json'}) if resp.status_code in (200, 204): # some cases we get No Content if 'extensions' in body and 'values' in body['extensions']: # Parse correct format (per contract) extensions = body['extensions']['values'] elif 'extensions' in body: # Support incorrect, but prevalent format extensions = body['extensions'] else: return dict(message=( 'Unrecognized extensions response from %s' % url)) return dict(self._get_extension_info(e) for e in extensions) elif resp.status_code == 305: return self._check_keystone_extensions(resp['location']) else: raise exceptions.from_response( resp, "GET", "%sextensions" % url) except Exception as e: _logger.exception(e) @staticmethod def _get_version_info(version, root_url): """Parses version information. :param version: a dict of a Keystone version response :param root_url: string url used to construct the version if no URL is provided. :returns: tuple - (verionId, versionStatus, versionUrl) """ id = version['id'] status = version['status'] ref = urlparse.urljoin(root_url, id) if 'links' in version: for link in version['links']: if link['rel'] == 'self': ref = link['href'] break return (id, status, ref) @staticmethod def _get_extension_info(extension): """Parses extension information. :param extension: a dict of a Keystone extension response :returns: tuple - (alias, name) """ alias = extension['alias'] name = extension['name'] return (alias, name) python-keystoneclient-0.7.1/keystoneclient/generic/__init__.py0000664000175400017540000000003512315030450026010 0ustar jenkinsjenkins00000000000000 __all__ = [ 'client', ] python-keystoneclient-0.7.1/keystoneclient/__init__.py0000664000175400017540000000161212315030450024376 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import pbr.version __version__ = pbr.version.VersionInfo('python-keystoneclient').version_string() __all__ = [ # Modules 'generic', 'v2_0', 'v3', # Packages 'access', 'client', 'exceptions', 'httpclient', 'service_catalog', ] python-keystoneclient-0.7.1/run_tests.sh0000775000175400017540000001231612315030451021616 0ustar jenkinsjenkins00000000000000#!/bin/bash set -eu function usage { echo "Usage: $0 [OPTION]..." echo "Run python-keystoneclient test suite" echo "" echo " -V, --virtual-env Always use virtualenv. Install automatically if not present" echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local environment" echo " -s, --no-site-packages Isolate the virtualenv from the global Python environment" echo " -x, --stop Stop running tests after the first error or failure." echo " -f, --force Force a clean re-build of the virtual environment. Useful when dependencies have been added." echo " -u, --update Update the virtual environment with any newer package versions" echo " -p, --pep8 Just run flake8" echo " -P, --no-pep8 Don't run flake8" echo " -c, --coverage Generate coverage report" echo " -d, --debug Run tests with testtools instead of testr. This allows you to use the debugger." echo " -h, --help Print this usage message" echo " --hide-elapsed Don't print the elapsed time for each test along with slow test list" echo "" echo "Note: with no options specified, the script will try to run the tests in a virtual environment," echo " If no virtualenv is found, the script will ask if you would like to create one. If you " echo " prefer to run tests NOT in a virtual environment, simply pass the -N option." exit } function process_option { case "$1" in -h|--help) usage;; -V|--virtual-env) always_venv=1; never_venv=0;; -N|--no-virtual-env) always_venv=0; never_venv=1;; -s|--no-site-packages) no_site_packages=1;; -f|--force) force=1;; -u|--update) update=1;; -p|--pep8) just_flake8=1;; -P|--no-pep8) no_flake8=1;; -c|--coverage) coverage=1;; -d|--debug) debug=1;; -*) testropts="$testropts $1";; *) testrargs="$testrargs $1" esac } venv=.venv with_venv=tools/with_venv.sh always_venv=0 never_venv=0 force=0 no_site_packages=0 installvenvopts= testrargs= testropts= wrapper="" just_flake8=0 no_flake8=0 coverage=0 debug=0 update=0 LANG=en_US.UTF-8 LANGUAGE=en_US:en LC_ALL=C OS_STDOUT_NOCAPTURE=False OS_STDERR_NOCAPTURE=False for arg in "$@"; do process_option $arg done if [ $no_site_packages -eq 1 ]; then installvenvopts="--no-site-packages" fi function run_tests { # Cleanup *.pyc ${wrapper} find . -type f -name "*.pyc" -delete if [ $debug -eq 1 ]; then if [ "$testropts" = "" ] && [ "$testrargs" = "" ]; then # Default to running all tests if specific test is not # provided. testrargs="discover ./keystoneclient/tests" fi ${wrapper} python -m testtools.run $testropts $testrargs # Short circuit because all of the testr and coverage stuff # below does not make sense when running testtools.run for # debugging purposes. return $? fi if [ $coverage -eq 1 ]; then TESTRTESTS="$TESTRTESTS --coverage" else TESTRTESTS="$TESTRTESTS" fi # Just run the test suites in current environment set +e testrargs=`echo "$testrargs" | sed -e's/^\s*\(.*\)\s*$/\1/'` TESTRTESTS="$TESTRTESTS --testr-args='$testropts $testrargs'" echo "Running \`${wrapper} $TESTRTESTS\`" bash -c "${wrapper} $TESTRTESTS" RESULT=$? set -e copy_subunit_log if [ $coverage -eq 1 ]; then echo "Generating coverage report in covhtml/" # Don't compute coverage for common code, which is tested elsewhere ${wrapper} coverage combine ${wrapper} coverage html -d covhtml -i fi return $RESULT } function copy_subunit_log { LOGNAME=`cat .testrepository/next-stream` LOGNAME=$(($LOGNAME - 1)) LOGNAME=".testrepository/${LOGNAME}" cp $LOGNAME subunit.log } function run_flake8 { echo "Running flake8 ..." srcfiles="keystoneclient" # Just run Flake8 in current environment ${wrapper} flake8 ${srcfiles} } TESTRTESTS="python setup.py testr" if [ $never_venv -eq 0 ] then # Remove the virtual environment if --force used if [ $force -eq 1 ]; then echo "Cleaning virtualenv..." rm -rf ${venv} fi if [ $update -eq 1 ]; then echo "Updating virtualenv..." python tools/install_venv.py fi if [ -e ${venv} ]; then wrapper="${with_venv}" else if [ $always_venv -eq 1 ]; then # Automatically install the virtualenv python tools/install_venv.py $installvenvopts wrapper="${with_venv}" else echo -e "No virtual environment found...create one? (Y/n) \c" read use_ve if [ "x$use_ve" = "xY" -o "x$use_ve" = "x" -o "x$use_ve" = "xy" ]; then # Install the virtualenv and run the test suite in it python tools/install_venv.py $installvenvopts wrapper=${with_venv} fi fi fi fi # Delete old coverage data from previous runs if [ $coverage -eq 1 ]; then ${wrapper} coverage erase fi if [ $just_flake8 -eq 1 ]; then run_flake8 exit fi run_tests # NOTE(sirp): we only want to run flake8 when we're running the full-test suite, # not when we're running tests individually. To handle this, we need to # distinguish between options (testropts), which begin with a '-', and # arguments (testrargs). if [ -z "$testrargs" ]; then if [ $no_flake8 -eq 0 ]; then run_flake8 fi fi python-keystoneclient-0.7.1/AUTHORS0000664000175400017540000000000112315030547020273 0ustar jenkinsjenkins00000000000000 python-keystoneclient-0.7.1/tox.ini0000664000175400017540000000167712315030451020554 0ustar jenkinsjenkins00000000000000[tox] minversion = 1.6 skipsdist = True envlist = py26,py27,py33,pep8 [testenv] usedevelop = True install_command = pip install -U {opts} {packages} setenv = VIRTUAL_ENV={envdir} OS_STDOUT_NOCAPTURE=False OS_STDERR_NOCAPTURE=False deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt commands = python setup.py testr --testr-args='{posargs}' [testenv:pep8] commands = flake8 [testenv:venv] commands = {posargs} [testenv:cover] commands = python setup.py testr --coverage --testr-args='{posargs}' [tox:jenkins] downloadcache = ~/cache/pip [testenv:debug] commands = {toxinidir}/tools/debug_helper.sh {posargs} [flake8] # F821: undefined name # H304: no relative imports # H803 Commit message should not end with a period (do not remove per list discussion) ignore = F821,H304,H803 show-source = True exclude = .venv,.tox,dist,doc,*egg,build [testenv:docs] commands= python setup.py build_sphinx