{ "info": { "author": "Oscar Martinez", "author_email": "omtinez@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 2 - Pre-Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Natural Language :: English", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5" ], "description": "===============================\nBottleShip\n===============================\n\n.. image:: https://img.shields.io/pypi/v/bottleship.svg\n :target: https://pypi.python.org/pypi/bottleship\n\n.. image:: https://img.shields.io/travis/omtinez/bottleship.svg\n :target: https://travis-ci.org/omtinez/bottleship\n\n.. image:: https://readthedocs.org/projects/bottleship/badge/?version=latest\n :target: http://bottleship.readthedocs.org/en/latest/?badge=latest\n :alt: Documentation Status\n\nAuthentication for the Bottle web framework made simple.\n\n* Free software: MIT license\n* Documentation: https://bottleship.readthedocs.org.\n\nIntroduction\n------------\n\nBottleShip is a very simple library for authentication using the Bottle web framework. It supports\nthe standard workflow of registration, login, and authentication required by simple applications\nthat need to maintain a state for individual users.\n\nFeatures\n--------\n\n* Very simple and easy to use\n* Works both on Python 2.x and 3.x\n* Very few dependencies\n\nGetting Started\n---------------\n\nThis documentation assumes that you already have a working Bottle application or that you are\nsomewhat familiar with the Bottle web framework. If you need to reference documentation for Bottle,\n`here is the link`_.\n\nThe easiest way to install BottleShip is using pip::\n\n $ pip install bottleship\n\nWith BottleShip installed, this is what it takes to use authentication to lock certain routes so\nthey can only be used by users who are logged in:\n\n.. code:: python\n\n # Instantiate class and register \"register\" and \"login\" routes\n bs = BottleShip()\n bs.route('/register', method=('GET', 'POST'), callback=bs.register)\n bs.route('/login', method=('GET', 'POST'), callback=bs.login)\n \n # This API endpoint can only be reached by users who have logged in\n @bs.require_auth('/testapi', method=('GET', 'POST'))\n def testapi(bottleship_user_record):\n return \"Hello, %s!\" % bottleship_user_record.get('Username')\n\nNew users can register by visiting the ``/register`` endpoint and sending their ``username`` and\n``password`` as part of their request. For example, a new user can be registered with the following\nrequest:\n\n >>> curl http://127.0.0.1:8080/register?Username=john&Password=1234\n ... HTTP/1.0 200 OK\n ... Content-Length: 155\n ... Content-Type: text/html; charset=UTF-8\n ... Date: Sun, 17 Jan 2016 23:36:02 GMT\n ... Server: WSGIServer/0.1 Python/2.7.10\n ... \n ... {\"Username\": \"john\", \"SecurityLevel\": \"plaintext\", \"Password\": \"1723328\n ... 704\", \"RemoteIpAddr\": \"127.0.0.1\", \"__id__\": \"040220e5-1cce-4cdd-af9d-2\n ... ad2885263aa\"}\n\nSimilarly, to log in, a user can make the following request:\n\n >>> curl http://127.0.0.1:8080/login?Username=john&Password=1234\n ... HTTP/1.0 200 OK\n ... Content-Length: 247\n ... Content-Type: text/html; charset=UTF-8\n ... Date: Sun, 17 Jan 2016 23:36:03 GMT\n ... Set-Cookie: Token=5f04ee43-83bb-46c0-96aa-65a2c585a796; Path=/\n ... Server: WSGIServer/0.1 Python/2.7.10\n ... \n ... {\"Username\": \"john\", \"SecurityLevel\": \"plaintext\", \"LastLogin\": \"145307\n ... 3842.72\", \"Token\": \"5f04ee43-83bb-46c0-96aa-65a2c585a796\", \"__id__\": \"0\n ... 40220e5-1cce-4cdd-af9d-2ad2885263aa\", \"Key\": null, \"Password\": \"1723328\n ... 704\", \"RemoteIpAddr\": \"127.0.0.1\"}\n\nBoth requests will return a JSON object that represents a record with all the information that the\nBottleShip server has about the user. A login request\\'s returned JSON also has a field named\n``Token`` that contains the user session token. In addition to that, the returned request will\nalso store the session token as part of the cookies in the request headers.\n\nIf the login was successful, the user can now make the following request:\n\n >>> curl http://127.0.0.1:8080/testapi3?Token=5f04ee43-83bb-46c0-96aa-65a2c\n 585a796\n ... HTTP/1.0 200 OK\n ... Content-Length: 12\n ... Content-Type: text/html; charset=UTF-8\n ... Date: Sun, 17 Jan 2016 23:36:04 GMT\n ... Server: WSGIServer/0.1 Python/2.7.10\n ... \n ... Hello, john!\n\nIf everything worked, the user will receive ``Hello, john!``.\n\nRoutes\n------\n\nBottleShip\\'s ``require_auth`` method has nearly identical signature compared to Bottle\\'s\n``route``. The main difference is that, instead of a single ``callback`` parameter, it has two:\n\n* ``callback_success``. Optional; roughly equivalent to Bottle\\'s ``callback``. When not set,\n defaults to sending a request back to the user with the status code of ``200`` and body ``OK``.\n* ``callback_failure``. Optional; when not set, defaults to sending a request back to the user with\n status code of ``403`` and body containing more details about the failure (but no stack trace).\n\nLike Bottle\\'s ``route`` method, ``require_auth`` can be used both as a regular function that takes\ncallable objects parameters for ``callback_success`` and ``callback_failure``, or as a decorator to\nwrap the function ``callback_success``.\n\nFor applications intended for web browsers that can rely on cookies for session tokens, this\nfunction is essentially a drop-in replacement for Bottle\\'s ``route``. For example, the following\nsnippet:\n\n.. code:: python\n\n app = Bottle()\n @app.route('/hello/')\n def hello(name):\n return 'Hello, %s!' % name\n\nBecomes this:\n\n.. code:: python\n\n app = BottleShip()\n @app.require_auth('/hello/')\n def hello(name):\n return 'Hello, %s!' % name\n\nFor convenience, and to avoid interfacing with the underlying data about the users at more than one\nlayer in the application, routes can receive a copy of the record representing a user by adding a\nparameter named ``bottleship_user_record`` to the function\\'s signature. The information will be\nrepresented as a ``dict`` and contains:\n\n* ``Username``\n* ``Password``, if any (hashed)\n* ``__id__``, used internally by the database engine\n* ``RemoteIpAddr``\n* Any other information added by the client during registration or login as part of the request\n\nThen, the previous example can be simplified further and changed to:\n\n.. code:: python\n\n app = BottleShip()\n @app.require_auth('/hello')\n def hello(bottleship_user_record):\n return 'Hello, %s!' % bottleship_user_record.get('Username')\n\nSecurity\n--------\n\nNeedless to say, you should not be transmitting passwords over a plain connection like it is done\nin the example above. If you cannot achieve a cryptographically secure connection between user and\nserver, your only hope is to implement a public key scheme to allow for secure transmission of user\npassword and token. Such scheme is not implemented in BottleShip, but it has a few mitigations in\nplace that yield a marginal increase in security.\n\nWhen registration takes place, all information provided by the user is recorded. Most of it is\nprovided by the user himself so it could be easily forged, but the IP address is slightly more\ndifficult to fake. Using the user IP address, along with some form of whitelisting (or\nblacklisting), allows for a relative improvement in the application security. To achieve this, one\nmust provide the whitelist upon instantiation like:\n\n.. code:: python\n\n valid_users = {\"RemoteIpAddr\": \"127.0.0.1\"}\n bs = BottleShip(whitelist_cond=valid_users)\n \nThen, when the user registers, BottleShip will make sure that only requests from the provided IP\naddresses have permission to reach the endpoint.\n\nAnother mitigation regarding the user IP address is the verification of addresses not changing\nbetween registration and login. This is achieved by appending ``+ipaddr`` to the desired security\nlevel upon registration. For example, a new user can be registered with the following request:\n\n >>> curl http://127.0.0.1:8080/register?Username=john&Password=1234&Securit\n yLevel=plaintext%2Bipaddr\n ... HTTP/1.0 200 OK\n ... Content-Length: 162\n ... Content-Type: text/html; charset=UTF-8\n ... Date: Sun, 17 Jan 2016 23:36:05 GMT\n ... Server: WSGIServer/0.1 Python/2.7.10\n ... \n ... {\"Username\": \"john\", \"SecurityLevel\": \"plaintext+ipaddr\", \"Password\": \"\n ... 1723328704\", \"RemoteIpAddr\": \"127.0.0.1\", \"__id__\": \"1b5ca834-f4fb-4f6a\n ... -96f3-5a427ca43270\"}\n\nNote that the ``+`` sign is URL encoded so ``plaintext`` becomes ``plaintext+ipaddr``, which is\nencoded into ``plaintext%2Bipaddr``. IP address verification is the only security feature that will\npersist between registration and login. Other than that, the security level during login can be\nwhatever the client chooses regardless of the security level during registration.\n\nA more sophisticated security mitigation is implementing HMAC signing for the information exchanged\nbetween client and server during registration and login. This requires an additional step to\nperform the key exchange prior to registration and/or login. The key exchange will provide the user\nwith a single-use token that can be utilized by the client to send the server information signed\nwith the secret key provided during the exchange.\n\n >>> curl http://127.0.0.1:8080/swapkeys/hmac/5f04ee43-83bb-46c0-96aa-65a2c5\n 85a796\n ... HTTP/1.0 200 OK\n ... Content-Length: 114\n ... Content-Type: text/html; charset=UTF-8\n ... Date: Sun, 17 Jan 2016 23:36:06 GMT\n ... Server: WSGIServer/0.1 Python/2.7.10\n ... \n ... !1ICg4mv4H8NGUyV5aveJU1fJ/wnFr0cOks+KMIvZuIo=?eyJUb2tlbiI6ICI0OGYyNWM4O\n ... S1mZDg2LTRhMzctOGYyNi00NmYxNmE0YzVlYWIifQ==\n\nNote that the token is encoded in base64 and later signed with the user-provided key. Decoding the\nabove string produces ``{\"Token\": \"48f25c89-fd86-4a37-8f26-46f16a4c5eab\"}``.\n\nWhich can then be hashed and the signature verified using the user-provided secret key. In the next\nstep, the client can send all the user information encoded and signed along with the single-use\ntoken so the server knows which key to verify the data with:\n\n >>> curl http://127.0.0.1:8080/register?Token=48f25c89-fd86-4a37-8f26-46f16\n a4c5eab&Data=!6uz1tJzSZX%2F0EhVqj4ZpTMiiNmONVPY601ZHCHLXu9M%3D%3FeyJVc2\n VybmFtZSI6ImpvaG4iLCJQYXNzd29yZCI6IjEyMzQifQ%3D%3D\n ... HTTP/1.0 200 OK\n ... Content-Length: 202\n ... Content-Type: text/html; charset=UTF-8\n ... Date: Sun, 17 Jan 2016 23:36:07 GMT\n ... Server: WSGIServer/0.1 Python/2.7.10\n ... \n ... {\"Username\": \"john\", \"SecurityLevel\": \"plaintext\", \"__id__\": \"3be4ed1c-\n ... d30d-4786-bfc7-97728120e7b2\", \"Key\": \"5f04ee43-83bb-46c0-96aa-65a2c585a\n ... 796\", \"Password\": \"1723328704\", \"RemoteIpAddr\": \"127.0.0.1\"}\n\nThe data returned by the server is in plaintext because a security level was not specified in the\nrequest. If the client wants the user information encoded, he must explicitly specify a security\nlevel that enforces signature verification.\n\nThe only other method in the authentication workflow other than registration that supports encoding\nis login. The function signature is identical and the token is also of single-use. After login, any\nfurther references of ``token`` in the APIs assume that it is the session token. It is worth noting\nthat, because the token and user key are expected to last as long as the session does, it is\npointless to encode, hash, or otherwise obscure the token or user key. Since the same string,\nencrypted or otherwise, will be sent in each request by the client, it makes no difference to an\nattacker to sniff the plaintext version or the encrypted version of the token; he can just present\nthe server with the same string and it will be accepted as valid. For similar reasons, the password\nis being sent in plaintext form to the server and it is only hashed internally.\n\nLicense\n-------\n\nCopyright (c) 2016 Oscar Martinez\nAll rights reserved.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and\nassociated documentation files (the \"Software\"), to deal in the Software without restriction,\nincluding without limitation the rights to use, copy, modify, merge, publish, distribute,\nsublicense, and/or sell copies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or\nsubstantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT\nNOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\nDAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT\nOF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n.. _here is the link: http://bottlepy.org/docs/dev/api.html\n\n\n=======\nHistory\n=======\n\n0.1.0 (2016-01-17)\n------------------\n\n* First release on PyPI.\n\n0.2.0 (2016-01-17)\n------------------\n\n* Fixed Python 2 vs 3 compatibility.\n* Updated documentation and setup TravisCI\n\n0.2.1 (2016-01-19)\n------------------\n\n* Update interface with pddb\n* Fix line endings\n\n0.2.2 (2016-02-14)\n------------------\n\n* Added logout() function and corresponding documentation\n* Updated example html file\n\n0.2.3 (2016-02-15)\n------------------\n\n* Added logout() tests\n* Added callback_success fallback for callback parameter in require_auth()\n\n0.2.4 (2016-02-17)\n------------------\n\n* Dynamically import all public methods and classes from bottle", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/omtinez/bottleship", "keywords": "bottleship", "license": "MIT", "maintainer": null, "maintainer_email": null, "name": "bottleship", "package_url": "https://pypi.org/project/bottleship/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/bottleship/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://github.com/omtinez/bottleship" }, "release_url": "https://pypi.org/project/bottleship/0.2.4/", "requires_dist": null, "requires_python": null, "summary": "Authentication for the Bottle web framework meant for very simple workflows and little-to-none user interaction", "version": "0.2.4" }, "last_serial": 1966428, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "33d0cefafb83aca80e52eb66aa584c7a", "sha256": "e24c3de5cb8421f4094d93096e6639a285ea3684972a0490d3f034dda5bcf379" }, "downloads": -1, "filename": "bottleship-0.1.0.win-amd64.exe", "has_sig": false, "md5_digest": "33d0cefafb83aca80e52eb66aa584c7a", "packagetype": "bdist_wininst", "python_version": "any", "requires_python": null, "size": 249106, "upload_time": "2016-01-18T01:38:51", "url": "https://files.pythonhosted.org/packages/43/5b/bd4f0b481267606c08b1f3c9f4dab7c912e232bf57759077bb55f3096df8/bottleship-0.1.0.win-amd64.exe" }, { "comment_text": "", "digests": { "md5": "4c70a00184c24a396044b1101947fd75", "sha256": "9dcfc3990ee151ab2d5f9d0238f3a9c9476739500690cedfe0b17219602d1c1c" }, "downloads": -1, "filename": "bottleship-0.1.0.zip", "has_sig": false, "md5_digest": "4c70a00184c24a396044b1101947fd75", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 44401, "upload_time": "2016-01-18T01:38:41", "url": "https://files.pythonhosted.org/packages/36/ca/9dca90d1a24d58da18141b4e6cc99a4be146298b53738ba7891cecffc91e/bottleship-0.1.0.zip" } ], "0.2.0": [ { "comment_text": "", "digests": { "md5": "c77d35999903bdd6e3c4fb1c19898e02", "sha256": "dc48de9090c0ae9b222451b9397c6637c1c3c769f057e51400051333145a7262" }, "downloads": -1, "filename": "bottleship-0.2.0.win-amd64.exe", "has_sig": false, "md5_digest": "c77d35999903bdd6e3c4fb1c19898e02", "packagetype": "bdist_wininst", "python_version": "any", "requires_python": null, "size": 248070, "upload_time": "2016-01-18T06:12:37", "url": "https://files.pythonhosted.org/packages/a7/87/47bba02325f7c269c6c09f2f2a4e198c567b4fbc515b180933b2bf22ccc8/bottleship-0.2.0.win-amd64.exe" }, { "comment_text": "", "digests": { "md5": "1cb41631a545a73d5f8c1871c262e1a8", "sha256": "bd9e0ce1d663e1cf4ec8272aa7e13c932972c0cf4635ad9122389c9b53884a29" }, "downloads": -1, "filename": "bottleship-0.2.0.zip", "has_sig": false, "md5_digest": "1cb41631a545a73d5f8c1871c262e1a8", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 45069, "upload_time": "2016-01-18T06:12:30", "url": "https://files.pythonhosted.org/packages/3c/e1/1a237301b32e84a29dff40eb440ec1c71df3cf0ed0fa082e0191d243cffd/bottleship-0.2.0.zip" } ], "0.2.2": [ { "comment_text": "", "digests": { "md5": "1a903d821f6ed708dab3799b258f4821", "sha256": "0224ad8ff3edddf008c375d2768c3dddbae2827d509c1b2df03582ac7b448d5a" }, "downloads": -1, "filename": "bottleship-0.2.2-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "1a903d821f6ed708dab3799b258f4821", "packagetype": "bdist_wheel", "python_version": "2.7", "requires_python": null, "size": 20761, "upload_time": "2016-02-15T06:28:44", "url": "https://files.pythonhosted.org/packages/e3/67/ac0765943a5344934e1e6ed3c29e0b12d8664a267002c5f2f86d374309ab/bottleship-0.2.2-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "63acbaa284d1653b9b23f2051b83afdb", "sha256": "e08980fedc73793f9b3c15add071bbac116b1afa3f2e06164831829540481957" }, "downloads": -1, "filename": "bottleship-0.2.2.tar.gz", "has_sig": false, "md5_digest": "63acbaa284d1653b9b23f2051b83afdb", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 33705, "upload_time": "2016-02-15T06:28:38", "url": "https://files.pythonhosted.org/packages/b2/8f/0645e450663b6279354d7d0d54cf0d97beb0afdccc0be2711a6c8af6dadd/bottleship-0.2.2.tar.gz" } ], "0.2.3": [ { "comment_text": "", "digests": { "md5": "e372b9d3b8d52d265aa9f450d51ded77", "sha256": "50811334e62f4bbd782b6b613fe31bcda1ecc43b7af929895aee6f6a1dff68cf" }, "downloads": -1, "filename": "bottleship-0.2.3-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "e372b9d3b8d52d265aa9f450d51ded77", "packagetype": "bdist_wheel", "python_version": "2.7", "requires_python": null, "size": 20901, "upload_time": "2016-02-16T05:39:27", "url": "https://files.pythonhosted.org/packages/ba/d1/3480c7b10af27c0c152c6342f58b714019c7d71fd2f6d99ea652b6dbb418/bottleship-0.2.3-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "154c56a446567980d2b8338f3e4c8869", "sha256": "0bbfad2ad643d04fdea4d945bf4c56992d177521e44030f7e387bd3fad54d9a9" }, "downloads": -1, "filename": "bottleship-0.2.3.tar.gz", "has_sig": false, "md5_digest": "154c56a446567980d2b8338f3e4c8869", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 33855, "upload_time": "2016-02-16T05:39:10", "url": "https://files.pythonhosted.org/packages/d2/93/38197b7b91d6b600342768c9455fc510ad1711b7a4dd0154f129d3b1c5ab/bottleship-0.2.3.tar.gz" } ], "0.2.4": [ { "comment_text": "", "digests": { "md5": "07457f2bb8ca12d20e3948cfd6a971b0", "sha256": "8f416419d3b3a48cbcbbffce42cba5d8a68acb80bdef43b2b9ac006fba1c0b1b" }, "downloads": -1, "filename": "bottleship-0.2.4-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "07457f2bb8ca12d20e3948cfd6a971b0", "packagetype": "bdist_wheel", "python_version": "2.7", "requires_python": null, "size": 20858, "upload_time": "2016-02-20T06:02:30", "url": "https://files.pythonhosted.org/packages/be/d1/d3b5c9c3cae19a2c4c8509588e1c81bf3cd48e28cfc3715cef2a4dc09af2/bottleship-0.2.4-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "4af406665a03833eb0579b3c3d3088d8", "sha256": "6e3a0497e874a2a0f8d6c3bf9be32650e764f9573e42c06c2ddd53c6f2f00883" }, "downloads": -1, "filename": "bottleship-0.2.4.tar.gz", "has_sig": false, "md5_digest": "4af406665a03833eb0579b3c3d3088d8", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 33871, "upload_time": "2016-02-20T06:02:23", "url": "https://files.pythonhosted.org/packages/c2/38/e93e70c189c1326f942cbb90975cf0d26ee9766b33e4909b6f813695d7a7/bottleship-0.2.4.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "07457f2bb8ca12d20e3948cfd6a971b0", "sha256": "8f416419d3b3a48cbcbbffce42cba5d8a68acb80bdef43b2b9ac006fba1c0b1b" }, "downloads": -1, "filename": "bottleship-0.2.4-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "07457f2bb8ca12d20e3948cfd6a971b0", "packagetype": "bdist_wheel", "python_version": "2.7", "requires_python": null, "size": 20858, "upload_time": "2016-02-20T06:02:30", "url": "https://files.pythonhosted.org/packages/be/d1/d3b5c9c3cae19a2c4c8509588e1c81bf3cd48e28cfc3715cef2a4dc09af2/bottleship-0.2.4-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "4af406665a03833eb0579b3c3d3088d8", "sha256": "6e3a0497e874a2a0f8d6c3bf9be32650e764f9573e42c06c2ddd53c6f2f00883" }, "downloads": -1, "filename": "bottleship-0.2.4.tar.gz", "has_sig": false, "md5_digest": "4af406665a03833eb0579b3c3d3088d8", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 33871, "upload_time": "2016-02-20T06:02:23", "url": "https://files.pythonhosted.org/packages/c2/38/e93e70c189c1326f942cbb90975cf0d26ee9766b33e4909b6f813695d7a7/bottleship-0.2.4.tar.gz" } ] }