{ "info": { "author": "Jay Pipes", "author_email": "jaypipes@gmail.com", "bugtrack_url": null, "classifiers": [ "Intended Audience :: Developers", "Intended Audience :: Information Technology", "License :: OSI Approved :: Apache Software License", "Operating System :: POSIX :: Linux", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.3" ], "description": "# Talons == Falcon Hooks [![Build Status](https://travis-ci.org/talons/talons.png)](https://travis-ci.org/talons/talons)\n\nTalons is a library of WSGI middleware that is designed to work with\nthe [Falcon](http://github.com/racker/falcon) lightweight Python framework\nfor building RESTful APIs. Like Falcon, Talons aims to be fast, light, and\nflexible.\n\nThe first middleware in Talons is authentication middleware, enabling one\nor more backend identity plugins to handle authentication.\n\n# What is `talons.auth`?\n\n`talons.auth` is a namespace package that contains utilies for\nconstructing identifying and authenticating middleware and plugins\ndesigned for applications running the Falcon WSGI micro-framework\nfor building REST APIs.\n\n## A simple usage example\n\nA simple Falcon API application is constructed like so:\n\n```python\nimport falcon\n\n# falcon.API instances are callable WSGI apps\napp = falcon.API()\n```\n\nTo add middleware to a Falcon API application, we simply instantiate the\ndesired `talons.auth` middleware and supply it to the `falcon.API()` call:\n\n```python\nimport falcon\nfrom talons.auth import middleware\nfrom talons.auth import basicauth, httpheader, htpasswd\n\n# Assume getappconfig() returns a dictionary of application configuration\n# options that may have been read from some INI file...\nconfig = getappconfig()\n\nauth_middleware = middleware.create_middleware(identify_with=[\n basicauth.Identifier,\n httpheader.Identifier],\n authenticate_with=htpasswd.Authenticator,\n **config)\n\napp = falcon.API(before=[auth_middleware])\n```\n\n# Details\n\nThere are a variety of basic plugins that handle identification of the user making\nan API request and authenticating credentials with a number of common backends,\nincluding LDAP and SQL data stores.\n\nAuthentication involves two main tasks:\n\n * Identifying the user who wishes to be authenticated\n * Validating credentials for the identified user\n\nClasses that derive from `talons.auth.interfaces.Identifies` implement an `identify`\nmethod that takes the `falcon.request.Request` object from the WSGI pipeline and\nlooks at elements of the request to determine who the requesting user is.\n\nThe class that stores credential information -- including a login, password/key,\na set of roles or groups, as well as other metadata about the requesting user --\nis the `talons.auth.interfaces.Identity` class. `talons.auth.interfaces.Identifies`\nsubclasses store this `Identity` object in the WSGI environs' \"wsgi.identity\" bucket.\n\nClasses that derive from `talons.auth.interfaces.Authenticates` implement an\n`authenticate` method that takes a single argument -- a `talons.auth.interfaces.Identity`\nobject -- and attempts to validate that the identity is authentic.\n\nTo give your Falcon-based WSGI application authentication capabilities, you\nsimply create middleware that has one or more `talons.auth.identify` modules\nand one or more `talons.auth.authenticate` modules. We even give you a helper\nmethod -- `talons.auth.middleware.create_middleware` -- to create such middleware\nin a single call.\n\n## Identifiers\n\nEach class that derives from `talons.auth.interfaces.Identifies` is called an \"Identifier\". Each\nclass implements a single method, `identify()`, that takes the incoming `falcon.request.Request`\nobject as its sole parameter. If the identity of the authenticating user can be determined,\nthen the Identifier object stores a `talons.auth.interfaces.Identity` object in the WSGI environ's\n`wsgi.identity` key and returns True.\n\nMultiple Identifier classes can be supplied to the\n`talons.auth.middleware.create_middleware` method to support a variety of ways of\ngleaning identity information from the WSGI request. Each Identifier's\n`identify()` method checks to see if the `wsgi.identity` key is already\nset in the WSGI environs. If it is, the method simply returns True and does\nnot attempt to process anything further.\n\n### `talons.auth.basicauth.Identifier`\n\nThe most basic identifier, `talons.auth.basicauth.Identifier` has no\nconfiguration options and simply looks in the\n[`Authenticate`](http://en.wikipedia.org/wiki/Basic_access_authentication) HTTP\nheader for credential information. If the `Authenticate` HTTP header is found\nand contains valid credential information, then that identity information is\nstored in the `wsgi.identity` WSGI environs key.\n\n### `talons.auth.httpheader.Identifier`\n\nAnother simple identifier, `talons.auth.httpheader.Identifier` looks\nfor configurable HTTP headers in the incoming WSGI request, and uses the values\nof the HTTP headers to construct a `talons.auth.Identity` object.\n\nA set of configuration options control how this Identifier class behaves:\n\n * `httpheader_user`: HTTP header to look for user/login\n name (required)\n * `httpheader_key`: HTTP header to look for password/key\n (required)\n * `httpheader_$ATTRIBUTE`: HTTP header that, if found, will\n be used to add $ATTRIBUTE to the Identity object stored in the WSGI\n pipeline. (optional)\n\nThe above configuration options are supplied to the constructor as keyword\narguments.\n\n#### Example\n\nSuppose we wanted to extract identity information from the following HTTP\nHeaders:\n\n * `X-Auth-User` -- The value of this header will be the authenticating user's\n user name\n * `X-Auth-Password` -- The value of this header will be the authenticating\n user's password\n * `X-Auth-Domain` -- The value of this header should be considered the\n authentication domain that will be considered when authenticating the\n identity. We want to store this value on the `talons.auth.Identity` object's\n `domain` attribute.\n\nOur configuration options would look like this:\n\n```\nhttpheader_user=x-auth-user\nhttpheader_key=x-auth-password\nhttpheader_domain=x-auth-domain\n```\n\n## Authenticators\n\nEach class that derives from `talons.auth.interfaces.Authenticates` is\ncalled an \"Authenticator\". Each Authenticator implements a single method,\n`authenticate()`, that takes a `talons.auth.interfaces.Identity` object\nas its sole parameter.\n\nThe `authenticate` method verifies that the supplied identity can be\nverified (authenticated). Different implementations will rely on various\nbackend storage systems to validate the incoming identity/credentials.\nIf authentication was successful, the method returns True, False otherwise.\n\nTalons comes with a few simple examples of Authenticator plugins.\n\n### `talons.auth.external.Authenticator`\n\nA generic Authenticator plugin that has one main configuration option,\n`external_authn_callable` which should be the \"module.function\" or\n\"module.class.method\" dotted-import notation for a function or class\nmethod that accepts a single parameter. This function will be called by\nthe instance of `talons.auth.authenticate.external.Authenticator` to\nvalidate the credentials of a request.\n\nIn addition, there are two other configuration options that indicate\nwhether the `external_authfn` function may set the roles or groups\nattributes on the supplied identity:\n\n * `external_sets_roles`: Boolean (defaults to False). A True value\n indicates the plugin may set the roles attribute on the identity\n object.\n\n * `external_sets_groups`: Boolean (defaults to False). A True value\n indicates the plugin may set the groups attribute on the identity\n object.\n\n#### Example\n\nSuppose we have some application code that looks up a stored password\nfor a user in a [`Redis`](http://redis.io) Key-Value Store. Salted, encrypted\npasswords for each user are stored in the Redis KVS, along with a\ncomma-separated list of roles the user belongs to.\n\nOur application has a Python file called `/application/auth.py` that looks\nlike this:\n\n```python\nimport hashlib\n\nimport redis\n\n_AUTH_DB = redis.StrictRedis(host='localhost', port=6379, db=0)\n\n\ndef _pass_matches_stored_pass(pass, stored_pass):\n # Assume that passwords are stored in Redis in the following format:\n # salt:hashedpass\n # and that the passwords have been hashed with SHA-256\n salt, stored_hashed_pass = stored_pass.split(':')\n hashed_pass = hashlib.sha256(salt.encode() + pass.encode()).hexdigest()\n return hashed_pass == stored_hashed_pass\n\n\ndef authenticate(identity):\n user = identity.login\n pass = identity.key\n\n # Assume that user \"records\" are stored in Redid in the following format:\n # salt:hashedpass#roles\n # Where roles is a comma-separated list of roles\n user_record = _AUTH_DB.get(user)\n if user_record:\n stored_pass, role_list = user_record.split('#')\n auth_success = _pass_matches_stored_pass(pass, stored_pass)\n if auth_success:\n identity.roles = role_list.split(',')\n return auth_success\n```\n\nTo use the above `application.auth.authenticate` method for authenticating\nidentities, we'd supply the following configuration options to the\n`talons.auth.external.Authenticator` constructor:\n\n * `external_authn_callable=application.auth.authenticate`\n * `external_sets_roles=True`\n\n### `talons.auth.htpasswd.Authenticator`\n\nAn Authenticator plugin that queries an Apache htpasswd file to check\nthe credentials of a request. The plugin has a single configuration option:\n\n * `htpasswd_path`: The filepath to the Apache htpasswd file to\n use for authentication checks.\n\n## Authorizers\n\nEach class that derives from `talons.auth.interfaces.Authorizes` is\ncalled an \"Authorizer\". Each Authorizer implements a single method,\n`authorize()`, that takes a `talons.auth.interfaces.Identity` object,\nand a `talons.auth.interfaces.ResourceAction` object.\n\nThe `ResourceAction` object currently has a single method, `to_string`,\nthat returns a \"dotted-notation\" string describing the requested\nHTTP resource.\n\nFor instance, let's say the identity made an HTTP request to:\n\n POST /users/12345/groups\n\nThe `ResourceAction.to_string` method that is supplied to the `authorize`\nfunction would yield the string \"users.12345.groups.post\". This string is\nuseful to plugins that compare the string with the supplied identity object.\nSee below for an example that makes this more clear.\n\nAt present, there is only a single Authorizer built in to Talons: the\n`talons.auth.external.Authorizer` class. Like its sister, the\n`talons.auth.external.Authenticator`, it accepts an external callable that\naccepts the identity and resource action parameters and returns whether\nthe identity is allowed to perform the action on the resource. The single\nconfiguration parameter is called `external_authz_callable`.\n\nLet's continue the example from above and add an external callable that\nwill be used as an authorizer. This callable will compare the result of\nthe `ResourceAction`'s `to_string` method against the supplied identity\nobject and a hashmap of regular expressions in order to determine if the\nuser is permitted to perform an action.\n\nAssuming our application has a Python file called `/application/auth.py` that\ncontains the above authenticate code, as well as this:\nlike this:\n\n```python\nimport re\n\n\ndef self_or_admin(match, identity):\n \"\"\"\n Returns True if the identity has an admin role or the identity\n matches the requesting user.\n \"\"\"\n if \"admin\" in identity.roles:\n return True\n return match.groups(1) == identity.login\n\n\ndef anyone(*args):\n return True\n\n\n_POLICY_RULES = [\n (r'^users\\.(^\\.)+\\.get$', self_or_admin),\n (r'^users\\.post$', anyone),\n]\nPOLICIES = []\nfor regex, fn in _POLICY_RULES:\n POLICIES.append((re.compile(regex), fn))\n\n\ndef authorize(identity, resource_action):\n user = identity.login\n res_string = resource_action.to_string()\n for p, fn in _POLICIES:\n m = p.match(res_string)\n if m:\n return fn(m, identity)\n```\n\nTo use the above `application.auth.authorize` method for authorizing the\nidentity that was authenticated, we'd supply the following configuration\noptions to the `talons.auth.external.Authorizer` constructor:\n\n * `external_authz_callable=application.auth.authorize`\n\n\nWhy `talons.auth`?\n==================\n\nWhy not just use middleware like [repose.who](http://docs.repoze.org/who/2.0/index.html) for\nauthentication plugins? Why re-invent the wheel here?\n\nA few reasons, in no particular order:\n\n* Use of the Webob library. I'm not a fan of it, as I've run into numerous issues with\n this library over the years.\n* Use of zope.interfaces. Also not a fan of it. It's a library that seems to be designed\n for traditional C++ programmers instead of feeling like it's designed for Python developers.\n Just use the [abc](http://docs.python.org/2/library/abc.html) module if you absolutely must\n have strict interface enforcement.\n* Trying to override things like logging setup in constructors of middleware.\n* No Paste.\n* Wanted something that fit Falcon's app construction paradigm.\n\nBut hey, there's nothing inherently wrong with repoze.who. If you like it, and it works\nfor you, use it.\n\n## Contributing\n\n[Jay Pipes](http://joinfu.com) maintains the Talons library. You can usually find him on the Freenode IRC #openstack-dev\nchannel. Interested in improving and enhancing Talons? Pull requests are always welcome.\n\n## License and Copyright\n\nCopyright 2013-2014, Jay Pipes\n\nLicensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file\nexcept in compliance with the License. You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under the License\nis distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\nor implied. See the License for the specific language governing permissions and limitations under\nthe License.", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "http://github.com/talons/talons", "keywords": "falcon\nmiddleware", "license": "UNKNOWN", "maintainer": null, "maintainer_email": null, "name": "talons", "package_url": "https://pypi.org/project/talons/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/talons/", "project_urls": { "Download": "UNKNOWN", "Homepage": "http://github.com/talons/talons" }, "release_url": "https://pypi.org/project/talons/0.3/", "requires_dist": null, "requires_python": null, "summary": "Hooks for Falcon", "version": "0.3" }, "last_serial": 1278428, "releases": { "0.1": [ { "comment_text": "", "digests": { "md5": "c3e566a9ce42f1b89f948743547fa2be", "sha256": "a297de898beff50b75824e7f4bf99a2f5dda85b02239428abc41b46e1569d6dd" }, "downloads": -1, "filename": "talons-0.1-py2.7.egg", "has_sig": false, "md5_digest": "c3e566a9ce42f1b89f948743547fa2be", "packagetype": "bdist_egg", "python_version": "2.7", "requires_python": null, "size": 30501, "upload_time": "2014-01-05T04:05:17", "url": "https://files.pythonhosted.org/packages/a4/6b/c839d05251af54a65728a530c39a57d91aea6b80e796f6f38eed1d4471d1/talons-0.1-py2.7.egg" }, { "comment_text": "", "digests": { "md5": "fb142525bfea87bce6cd3e36ddb09685", "sha256": "06b8b520ec6ba883c6aeb22c522e3517c0441efdd5558436d4f9e33b016de7d6" }, "downloads": -1, "filename": "talons-0.1.tar.gz", "has_sig": false, "md5_digest": "fb142525bfea87bce6cd3e36ddb09685", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 21517, "upload_time": "2014-01-05T04:05:13", "url": "https://files.pythonhosted.org/packages/1b/d6/9f7b1edc355e32d1982e72a9bcabed1272ef397fedb3ca15a962c7087915/talons-0.1.tar.gz" } ], "0.2": [ { "comment_text": "", "digests": { "md5": "0fa8de29bee1ca11939395189d87dda3", "sha256": "5c64db17a54ed67508667bdcf41c623dbeb599a63582446189c9fe73408ee5de" }, "downloads": -1, "filename": "talons-0.2.tar.gz", "has_sig": false, "md5_digest": "0fa8de29bee1ca11939395189d87dda3", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 27042, "upload_time": "2014-08-14T03:03:03", "url": "https://files.pythonhosted.org/packages/be/1e/c6f10bbb9cc1c8809ec7386e636ca75cdc6a89c1eb441bdd53b43a3fd898/talons-0.2.tar.gz" } ], "0.3": [ { "comment_text": "", "digests": { "md5": "32ab25d7a48e742d7f0c116957085d96", "sha256": "4430be17f9cf378c4dbc7b736556400513325993d3ab398c592f3451ec380db1" }, "downloads": -1, "filename": "talons-0.3.tar.gz", "has_sig": false, "md5_digest": "32ab25d7a48e742d7f0c116957085d96", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 28920, "upload_time": "2014-10-22T03:40:28", "url": "https://files.pythonhosted.org/packages/bb/93/a13c810148c3472c863c5e96958c239a5fc70b915cc4b3fd3a7183da82a3/talons-0.3.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "32ab25d7a48e742d7f0c116957085d96", "sha256": "4430be17f9cf378c4dbc7b736556400513325993d3ab398c592f3451ec380db1" }, "downloads": -1, "filename": "talons-0.3.tar.gz", "has_sig": false, "md5_digest": "32ab25d7a48e742d7f0c116957085d96", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 28920, "upload_time": "2014-10-22T03:40:28", "url": "https://files.pythonhosted.org/packages/bb/93/a13c810148c3472c863c5e96958c239a5fc70b915cc4b3fd3a7183da82a3/talons-0.3.tar.gz" } ] }