{ "info": { "author": "LAZR Developers", "author_email": "lazr-developers@lists.launchpad.net", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)", "Operating System :: OS Independent", "Programming Language :: Python" ], "description": "..\n This file is part of lazr.authentication.\n\n lazr.authentication is free software: you can redistribute it and/or modify it\n under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, version 3 of the License.\n\n lazr.authentication is distributed in the hope that it will be useful, but\n WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\n or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public\n License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with lazr.authentication. If not, see .\n\nWSGI Middleware\n===============\n\nlazr.authentication defines some simple WSGI middleware for protecting\nresources with different kinds of HTTP authentication.\n\n >>> from __future__ import print_function\n >>> import lazr.authentication\n >>> print('VERSION: %s' % lazr.authentication.__version__)\n VERSION: ...\n\n\nBasicAuthMiddleware\n-------------------\n\nThe BasicAuthMiddleware implements HTTP Basic Auth. Its constructor\ntakes a number of arguments, including a callback function that\nperforms the actual authentication. This function returns an object\nidentifying the user who's trying to authenticate. If the\nauthentication credentials are invalid, it's supposed to return None.\n\nFirst, let's create a really simple WSGI application that responds to\nany request with a 200 response code.\n\n >>> def dummy_application(environ, start_response):\n ... start_response('200', [('Content-type','text/plain')])\n ... return [b'Success']\n\nNow let's protect that application. Here's an authentication callback\nfunction.\n\n >>> def authenticate(username, password):\n ... \"\"\"Accepts \"user/password\", rejects everything else.\n ...\n ... :return: The username, if the credentials are valid.\n ... None, otherwise.\n ... \"\"\"\n ... if username == \"user\" and password == \"password\":\n ... return username\n ... return None\n\n >>> print(authenticate(\"user\", \"password\"))\n user\n\n >>> print(authenticate(\"notuser\", \"password\"))\n None\n\nHere's a WSGI application that protects the application using\nBasicAuthMiddleware.\n\n >>> from lazr.authentication.wsgi import BasicAuthMiddleware\n >>> def protected_application():\n ... return BasicAuthMiddleware(\n ... dummy_application, realm=\"WSGI middleware test\",\n ... protect_path_pattern=\".*protected.*\",\n ... authenticate_with=authenticate)\n\n >>> import wsgi_intercept\n >>> from wsgi_intercept.httplib2_intercept import install\n >>> install()\n >>> wsgi_intercept.add_wsgi_intercept(\n ... 'basictest', 80, protected_application)\n\nMost of the application's URLs are not protected by the\nmiddleware. You can access them without providing credentials.\n\n >>> import httplib2\n >>> client = httplib2.Http()\n >>> response, body = client.request('http://basictest/')\n >>> print(response['status'])\n 200\n >>> print(body.decode())\n Success\n\nAny URL that includes the string \"protected\" is protected by the\nmiddleware, and cannot be accessed without credentials.\n\n >>> response, body = client.request('http://basictest/protected/')\n >>> print(response['status'])\n 401\n >>> print(response['www-authenticate'])\n Basic realm=\"WSGI middleware test\"\n\n >>> response, body = client.request(\n ... 'http://basictest/this-is-protected-as-well/')\n >>> print(response['status'])\n 401\n\nThe check_credentials() implementation given at the beginning of the\ntest will only accept the user/password combination \"user\"/\"password\".\nProvide a bad username or password and you'll get a 401.\n\n >>> client.add_credentials(\"baduser\", \"baspassword\")\n >>> response, body = client.request('http://basictest/protected/')\n >>> print(response['status'])\n 401\n\nProvide the correct credentials and you'll get a 200, even for the\nprotected URIs.\n\n >>> client.add_credentials(\"user\", \"password\")\n >>> response, body = client.request('http://basictest/protected/')\n >>> print(response['status'])\n 200\n\nTeardown.\n\n >>> wsgi_intercept.remove_wsgi_intercept('basictest', 80)\n\nStacking\n********\n\nBasicAuthMiddleware instances can be stacked, each instance protecting\na different path pattern. Here, we'll use stacking to protect\nthe two regexes \".*protected.*\" and \".*different.*\", without combining\nthem into one complex regex.\n\n >>> def return_user_application(environ, start_response):\n ... start_response('200', [('Content-type','text/plain')])\n ... return [str(environ['authenticated_user']).encode('utf-8')]\n\n >>> def protected_application():\n ... protected = BasicAuthMiddleware(\n ... return_user_application, realm=\"WSGI middleware test\",\n ... protect_path_pattern=\".*protected.*\",\n ... authenticate_with=authenticate)\n ... return BasicAuthMiddleware(\n ... protected, realm=\"Another middleware\",\n ... protect_path_pattern=\".*different.*\",\n ... authenticate_with=authenticate)\n\nSetup.\n\n >>> wsgi_intercept.add_wsgi_intercept(\n ... 'stacked', 80, protected_application)\n >>> client = httplib2.Http()\n\nBoth path patterns are protected:\n\n >>> response, body = client.request('http://stacked/protected')\n >>> print(response['status'])\n 401\n >>> response, body = client.request('http://stacked/different')\n >>> print(response['status'])\n 401\n\nBoth path patterns control respond to the same credentials.\n\n >>> client.add_credentials(\"user\", \"password\")\n\n >>> response, body = client.request('http://stacked/protected-resource')\n >>> print(response['status'])\n 200\n >>> print(body.decode())\n user\n\n >>> response, body = client.request('http://stacked/different-resource')\n >>> print(response['status'])\n 200\n >>> print(body.decode())\n user\n\n >>> wsgi_intercept.remove_wsgi_intercept('stacked', 80)\n\nOAuthMiddleware\n---------------\n\nThe OAuthMiddleware implements section 7 (\"Accessing Protected\nResources\") of the OAuth specification. That is, it makes sure that\nincoming consumer keys and access tokens pass some application-defined\ntest. It does not help you serve request tokens or exchange a request\ntoken for an access token.\n\nWe'll use OAuthMiddleware to protect the same simple application we\nprotected earlier with BasicAuthMiddleware. But since we're using\nOAuth, we'll be checking a consumer key and access token, instead of a\nusername and password.\n\n >>> from oauth.oauth import OAuthConsumer, OAuthToken\n\n >>> valid_consumer = OAuthConsumer(\"consumer\", '')\n >>> valid_token = OAuthToken(\"token\", \"secret\")\n\n >>> def authenticate(consumer, token, parameters):\n ... \"\"\"Accepts the valid consumer and token, rejects everything else.\n ...\n ... :return: The consumer, if the credentials are valid.\n ... None, otherwise.\n ... \"\"\"\n ... if consumer == valid_consumer and token == valid_token:\n ... return consumer\n ... return None\n\n >>> print(authenticate(valid_consumer, valid_token, None).key)\n consumer\n\n >>> invalid_consumer = OAuthConsumer(\"other consumer\", '')\n >>> print(authenticate(invalid_consumer, valid_token, None))\n None\n\nTo test the OAuthMiddleware's security features, we'll also need to\ncreate a data store. In a real application the data store would\nprobably be a database containing the registered consumer keys and\ntokens. We're using a simple data store designed for testing, and\ntelling it about the one valid consumer and token.\n\n >>> from lazr.authentication.testing.oauth import SimpleOAuthDataStore\n >>> data_store = SimpleOAuthDataStore(\n ... {valid_consumer.key : valid_consumer},\n ... {valid_token.key : valid_token})\n\n >>> print(data_store.lookup_consumer(\"consumer\").key)\n consumer\n >>> print(data_store.lookup_consumer(\"badconsumer\"))\n None\n\nThe data store tracks the use of OAuth nonces. If you call the data\nstore's lookup_nonce() twice with the same values, the first call will\nreturn False and the second call will return True.\n\n >>> print(data_store.lookup_nonce(\"consumer\", \"token\", \"nonce\"))\n False\n >>> print(data_store.lookup_nonce(\"consumer\", \"token\", \"nonce\"))\n True\n\n >>> print(data_store.lookup_nonce(\"newconsumer\", \"token\", \"nonce\"))\n False\n\nNow let's protect an application with lazr.authenticate's\nOAuthMiddleware, using our authentication technique and our simple\ndata store.\n\n >>> from lazr.authentication.wsgi import OAuthMiddleware\n >>> def protected_application():\n ... return OAuthMiddleware(\n ... dummy_application, realm=\"OAuth test\",\n ... authenticate_with=authenticate, data_store=data_store)\n\n >>> wsgi_intercept.add_wsgi_intercept(\n ... 'oauthtest', 80, protected_application)\n >>> client = httplib2.Http()\n\nA properly signed request will go through to the underlying WSGI\napplication.\n\n >>> from oauth.oauth import (\n ... OAuthRequest, OAuthSignatureMethod_PLAINTEXT)\n >>> def sign_request(url, consumer=valid_consumer, token=valid_token):\n ... request = OAuthRequest().from_consumer_and_token(\n ... consumer, token, http_url=url)\n ... request.sign_request(\n ... OAuthSignatureMethod_PLAINTEXT(), consumer, token)\n ... headers = request.to_header('OAuth test')\n ... return headers\n\n >>> url = 'http://oauthtest/'\n >>> headers = sign_request(url)\n >>> response, body = client.request(url, headers=headers)\n >>> print(response['status'])\n 200\n >>> print(body.decode())\n Success\n\nIf you replay a signed HTTP request that worked the first time, it\nwill fail the second time, because you'll be sending a nonce that was\nalready used.\n\n >>> response, body = client.request(url, headers=headers)\n >>> print(response['status'])\n 401\n\nAn unsigned request will fail.\n\n >>> response, body = client.request('http://oauthtest/')\n >>> print(response['status'])\n 401\n >>> print(response['www-authenticate'])\n OAuth realm=\"OAuth test\"\n\nA request signed with invalid credentials will fail.\n\n >>> bad_token = OAuthToken(\"token\", \"badsecret\")\n >>> headers = sign_request(url, token=bad_token)\n >>> response, body = client.request(url, headers=headers)\n >>> print(response['status'])\n 401\n\nTeardown.\n\n >>> wsgi_intercept.remove_wsgi_intercept('oauthtest', 80)\n\n============================\nNEWS for lazr.authentication\n============================\n\n0.1.3 (2014-11-08)\n==================\n\n- Provide Python 3 support.\n- Fix wsgi compatibility bug.\n\n0.1.2 (2010-02-09)\n==================\n\n- Fixed a bug that prevented middleware instances from stacking.\n\n0.1.1 (2009-10-19)\n==================\n\n- Fixed a bug that caused the OAuth middleware to specify the\n authentication realm as Basic.\n\n0.1.0 (2009-10-07)\n==================\n\n- Initial release", "description_content_type": null, "docs_url": null, "download_url": "https://launchpad.net/lazr.authentication/+download", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://launchpad.net/lazr.authentication", "keywords": null, "license": "LGPL v3", "maintainer": null, "maintainer_email": null, "name": "lazr.authentication", "package_url": "https://pypi.org/project/lazr.authentication/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/lazr.authentication/", "project_urls": { "Download": "https://launchpad.net/lazr.authentication/+download", "Homepage": "https://launchpad.net/lazr.authentication" }, "release_url": "https://pypi.org/project/lazr.authentication/0.1.3/", "requires_dist": null, "requires_python": null, "summary": "This is a template for your lazr package. To start your own lazr package,", "version": "0.1.3" }, "last_serial": 1329444, "releases": { "0.1.0": [], "0.1.1": [], "0.1.2": [ { "comment_text": "", "digests": { "md5": "a639ff9e95cb9e3ff50ca544193ffb20", "sha256": "23b66ba6a135168e22e0142f9c18b5fa3c1ed37b08c6ef71c8acd7adb244fa11" }, "downloads": -1, "filename": "lazr.authentication-0.1.3.tar.gz", "has_sig": false, "md5_digest": "a639ff9e95cb9e3ff50ca544193ffb20", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 17624, "upload_time": "2014-12-03T10:37:14", "url": "https://files.pythonhosted.org/packages/90/da/525645ece5afd54a22a7f95a194b84ada61cc3cbbc0eb98ac0af6e43367b/lazr.authentication-0.1.3.tar.gz" } ], "0.1.3": [] }, "urls": [] }