[](https://pypi.org/project/gadeu/) [](https://gadeu.readthedocs.io)
**gadeu** (가드) is a decorative auth library for [Tornado](https://www.tornadoweb.org).
This README is only a high-level introduction to **gadeu**. For more detailed documentation, please view the official docs at [https://gadeu.readthedocs.io](https://gadeu.readthedocs.io).
## Installation
You can install ``gadeu`` from [PyPI](https://pypi.org/project/gadeu/) through usual means, such as ``pip``:
```bash
pip install gadeu
```
## Usage
To use ``gadeu`` two things must be done; first you must register at least one authorization handler, and second you must apply one of the authorization decorators to a request handler method. Consider the following example:
```python
import tornado
from gadeu import *
from .api.FakeApi import FakeApi
# you configure an authorization handler
AuthorizationManager.setAuthorizationHandler(
AuthorizationMethod.APIKEY,
handlers.ApiKeyAuthorizationHandler(key=apiKeySecret)
)
# you create a tornado app
app = tornado.web.Application()
# you add some handlers for your app
app.add_handlers('.*', [
(r'/api/v2/fakes', FakeApi),
(r'/api/v2/fakes/(?P<id>\d+)', FakeApi),
(r'/api/v2/fakes/(?P<name>[\dA-Za-z]+)', FakeApi),
(r'/api/v2/fakes/(?P<id>\d+)/(?P<name>[^/][\dA-Za-z]+)', FakeApi)
])
```
Elsewhere in your project, you defined `FakeApi` and decorated at least one handler:
```python
import tornado
from gadeu import authorization
class FakeApi(tornado.web.RequestHandler):
def initialize(self) -> None:
pass
@authorization.apiKey
async def put(self, id:str, name:str) -> None:
_d[id] = name
self.set_status(204)
```
In the above example, ``FakeApi.put`` has been decorated with ``@authorization.apiKey`` which will force a check for a valid API Key. The expectations of that check are implemented via the ``ApiKeyAuthorizationHandler`` configured in the first few lines of the example. There are more options than are shown here, but this basic setup is enough for a server to check for a valid API Key.
If you need to generate an encryption key there is a ``TokenUtil`` class that exposes a ``createSecretKey(...)`` method which you can use for this purpose, example:
```python
from gadeu import *
# never share this key! it should get stored to a keyvault and
# managed securely as part of your app settings.
secretKey = TokenUtil.createSecretKey(AuthorizationMethod.APIKEY)
```
You can also use ``TokenUtil`` to generate API Keys using your secret key.
```python
# share this token securely with your business partners, developers,
# testers, etc that need to authorize requests with a server.
apiKey = TokenUtil.createToken(secretKey, {'app':'bob123'}, AuthorizationMethod.APIKEY)
```
In the above example you can see a dictionary ``{'app':'bob123'}``, this is a "claims object" that gets encoded into the resulting token (``apiKey``). See the section below **Checking Claims** for more information on how they can be accessed.
Currently, only ``apiKey`` and ``bearerToken`` security schemes are supported, with a plan to add others as they are requested, PR'd, or required for our own projects. Both ``apiKey`` and ``bearerToken`` tokens are encrypted, and unless you leak your secret keys the wider public should not be able to peek at the token contents (ie. the "claims" you've stored.) That said, it is NOT a good practice to store anything sensitive in a claim (such as keys, passwords, etc.)
### Custom/Proprietary Authorization Methods
You can subclass ``AuthorizationHandler`` to implement custom behavior. You are encouraged to submit a PR if you find yourself implementing any well known security schemes such as:
* mutualTLS
* OAuth2
* openIdConnect
Since we do not currently use these schemes there are not yet handlers for them, despite their popularity.
### Checking Claims
In the future there will be decorators to facilitate claims assertions.
In the current implementation you can check claims "globally" from a custom ``validator`` function, or "locally" within your handler methods. Example:
```python
# you configure an authorization handler
AuthorizationManager.setAuthorizationHandler(
AuthorizationMethod.APIKEY,
handlers.ApiKeyAuthorizationHandler(
key=apiKeySecret,
validator=lambda token,claims: claims.get('has_api_access', False) == True
)
)
# elsewhere, you decorate your services, and check claims
class FakeApi(tornado.web.RequestHandler):
@authorization.apiKey
async def put(self, id:str, name:str) -> None:
claims = self.request.arguments.get('claims', None)
if claims.get('can_edit', False) != True:
raise tornado.web.HTTPError(403)
# do stuff
```
If ``claims`` is an argument name you already use (and therefore would be clobbered by ``gadeu``) then you can configure a custom argument name in your ``AuthorizationHandler``. Example:
```python
AuthorizationManager.setAuthorizationHandler(
AuthorizationMethod.APIKEY,
handlers.ApiKeyAuthorizationHandler(
key=secretKey,
claimsArgumentName='my_epic_arg_name')
)
```
Lastly, ``TokenUtil`` can be used directly against a token to check claims. This may be useful for non-standard scenarios (token passing over a websocket connection for example), or if you are building user-tools for managing and verifying tokens. Example:
```python
secretKey = TokenUtil.createSecretKey(AuthorizationMethod.APIKEY)
token = TokenUtil.createToken(
secretKey,
{ 'id':123, 'ts':datetime.now().isoformat() },
AuthorizationMethod.APIKEY)
claims = TokenUtil.getTokenClaims(
secretKey,
token,
AuthorizationMethod.APIKEY)
print(claims)
# outputs:
#
# {'id': 123, 'ts': '2025-05-10T17:58:41.048820'}
#
```
## Contact
You can reach me on [Discord](https://discordapp.com/users/307684202080501761) or [open an Issue on Github](https://github.com/wilson0x4d/gadeu/issues/new/choose).
Raw data
{
"_id": null,
"home_page": null,
"name": "gadeu",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.12",
"maintainer_email": null,
"keywords": "tornado, authentication, authorization, apiKey, bearerToken, JWT",
"author": null,
"author_email": "Shaun Wilson <mrshaunwilson@msn.com>",
"download_url": "https://files.pythonhosted.org/packages/94/1a/1e25ba2324e589ce793a0306524f14facefd1260f12fcdb61efebee801b3/gadeu-0.0.9.tar.gz",
"platform": null,
"description": "\n[](https://pypi.org/project/gadeu/) [](https://gadeu.readthedocs.io)\n\n**gadeu** (\uac00\ub4dc) is a decorative auth library for [Tornado](https://www.tornadoweb.org).\n\nThis README is only a high-level introduction to **gadeu**. For more detailed documentation, please view the official docs at [https://gadeu.readthedocs.io](https://gadeu.readthedocs.io).\n\n## Installation\n\nYou can install ``gadeu`` from [PyPI](https://pypi.org/project/gadeu/) through usual means, such as ``pip``:\n\n```bash\n\n pip install gadeu\n```\n\n## Usage\n\nTo use ``gadeu`` two things must be done; first you must register at least one authorization handler, and second you must apply one of the authorization decorators to a request handler method. Consider the following example:\n\n```python\n\n import tornado\n from gadeu import *\n from .api.FakeApi import FakeApi\n\n # you configure an authorization handler\n AuthorizationManager.setAuthorizationHandler(\n AuthorizationMethod.APIKEY,\n handlers.ApiKeyAuthorizationHandler(key=apiKeySecret)\n )\n\n # you create a tornado app\n app = tornado.web.Application()\n # you add some handlers for your app\n app.add_handlers('.*', [\n (r'/api/v2/fakes', FakeApi),\n (r'/api/v2/fakes/(?P<id>\\d+)', FakeApi),\n (r'/api/v2/fakes/(?P<name>[\\dA-Za-z]+)', FakeApi),\n (r'/api/v2/fakes/(?P<id>\\d+)/(?P<name>[^/][\\dA-Za-z]+)', FakeApi)\n ])\n```\n\nElsewhere in your project, you defined `FakeApi` and decorated at least one handler:\n\n```python\n\n import tornado\n from gadeu import authorization\n\n class FakeApi(tornado.web.RequestHandler): \n\n def initialize(self) -> None:\n pass\n\n @authorization.apiKey\n async def put(self, id:str, name:str) -> None:\n _d[id] = name\n self.set_status(204)\n```\n\nIn the above example, ``FakeApi.put`` has been decorated with ``@authorization.apiKey`` which will force a check for a valid API Key. The expectations of that check are implemented via the ``ApiKeyAuthorizationHandler`` configured in the first few lines of the example. There are more options than are shown here, but this basic setup is enough for a server to check for a valid API Key.\n\nIf you need to generate an encryption key there is a ``TokenUtil`` class that exposes a ``createSecretKey(...)`` method which you can use for this purpose, example:\n\n```python\n\n from gadeu import *\n\n # never share this key! it should get stored to a keyvault and\n # managed securely as part of your app settings.\n secretKey = TokenUtil.createSecretKey(AuthorizationMethod.APIKEY)\n```\n\nYou can also use ``TokenUtil`` to generate API Keys using your secret key.\n\n```python\n\n # share this token securely with your business partners, developers,\n # testers, etc that need to authorize requests with a server.\n apiKey = TokenUtil.createToken(secretKey, {'app':'bob123'}, AuthorizationMethod.APIKEY)\n```\n\nIn the above example you can see a dictionary ``{'app':'bob123'}``, this is a \"claims object\" that gets encoded into the resulting token (``apiKey``). See the section below **Checking Claims** for more information on how they can be accessed.\n\nCurrently, only ``apiKey`` and ``bearerToken`` security schemes are supported, with a plan to add others as they are requested, PR'd, or required for our own projects. Both ``apiKey`` and ``bearerToken`` tokens are encrypted, and unless you leak your secret keys the wider public should not be able to peek at the token contents (ie. the \"claims\" you've stored.) That said, it is NOT a good practice to store anything sensitive in a claim (such as keys, passwords, etc.)\n\n### Custom/Proprietary Authorization Methods\n\nYou can subclass ``AuthorizationHandler`` to implement custom behavior. You are encouraged to submit a PR if you find yourself implementing any well known security schemes such as:\n\n* mutualTLS\n* OAuth2\n* openIdConnect\n\nSince we do not currently use these schemes there are not yet handlers for them, despite their popularity.\n\n### Checking Claims\n\nIn the future there will be decorators to facilitate claims assertions.\n\nIn the current implementation you can check claims \"globally\" from a custom ``validator`` function, or \"locally\" within your handler methods. Example:\n\n```python\n\n # you configure an authorization handler\n AuthorizationManager.setAuthorizationHandler(\n AuthorizationMethod.APIKEY,\n handlers.ApiKeyAuthorizationHandler(\n key=apiKeySecret,\n validator=lambda token,claims: claims.get('has_api_access', False) == True\n )\n )\n\n # elsewhere, you decorate your services, and check claims\n class FakeApi(tornado.web.RequestHandler): \n\n @authorization.apiKey\n async def put(self, id:str, name:str) -> None:\n claims = self.request.arguments.get('claims', None)\n if claims.get('can_edit', False) != True:\n raise tornado.web.HTTPError(403)\n # do stuff\n\n```\n\nIf ``claims`` is an argument name you already use (and therefore would be clobbered by ``gadeu``) then you can configure a custom argument name in your ``AuthorizationHandler``. Example:\n\n```python\n\n AuthorizationManager.setAuthorizationHandler(\n AuthorizationMethod.APIKEY,\n handlers.ApiKeyAuthorizationHandler(\n key=secretKey,\n claimsArgumentName='my_epic_arg_name')\n )\n```\n\nLastly, ``TokenUtil`` can be used directly against a token to check claims. This may be useful for non-standard scenarios (token passing over a websocket connection for example), or if you are building user-tools for managing and verifying tokens. Example:\n\n```python\n\n secretKey = TokenUtil.createSecretKey(AuthorizationMethod.APIKEY)\n token = TokenUtil.createToken(\n secretKey, \n { 'id':123, 'ts':datetime.now().isoformat() },\n AuthorizationMethod.APIKEY)\n claims = TokenUtil.getTokenClaims(\n secretKey,\n token,\n AuthorizationMethod.APIKEY)\n print(claims)\n\n # outputs:\n #\n # {'id': 123, 'ts': '2025-05-10T17:58:41.048820'}\n #\n\n```\n\n## Contact\n\nYou can reach me on [Discord](https://discordapp.com/users/307684202080501761) or [open an Issue on Github](https://github.com/wilson0x4d/gadeu/issues/new/choose).\n",
"bugtrack_url": null,
"license": null,
"summary": "..a decorative auth library for Tornado.",
"version": "0.0.9",
"project_urls": {
"Documentation": "https://gadeu.readthedocs.io/",
"Homepage": "https://github.com/wilson0x4d/gadeu",
"Repository": "https://github.com/wilson0x4d/gadeu.git"
},
"split_keywords": [
"tornado",
" authentication",
" authorization",
" apikey",
" bearertoken",
" jwt"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "108beae58ecbc0e9ca30e26c439d9648bd9e191e6723cf83351ad4645c7edd4f",
"md5": "c641297f31188757858cb2d477f04fc8",
"sha256": "a9bd83c1b2b9c7abf1089f78af14b9968d7b8783a851fa297c13d44b60d8c6ae"
},
"downloads": -1,
"filename": "gadeu-0.0.9-py3-none-any.whl",
"has_sig": false,
"md5_digest": "c641297f31188757858cb2d477f04fc8",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.12",
"size": 12822,
"upload_time": "2025-08-12T09:28:20",
"upload_time_iso_8601": "2025-08-12T09:28:20.392245Z",
"url": "https://files.pythonhosted.org/packages/10/8b/eae58ecbc0e9ca30e26c439d9648bd9e191e6723cf83351ad4645c7edd4f/gadeu-0.0.9-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "941a1e25ba2324e589ce793a0306524f14facefd1260f12fcdb61efebee801b3",
"md5": "a554651f5c8e225e96984571f553c38a",
"sha256": "9dbe3332fadbd39736e1f8a74355ae884c67925bc1e2fc3f9699d17322fb0db2"
},
"downloads": -1,
"filename": "gadeu-0.0.9.tar.gz",
"has_sig": false,
"md5_digest": "a554651f5c8e225e96984571f553c38a",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.12",
"size": 11735,
"upload_time": "2025-08-12T09:28:21",
"upload_time_iso_8601": "2025-08-12T09:28:21.662849Z",
"url": "https://files.pythonhosted.org/packages/94/1a/1e25ba2324e589ce793a0306524f14facefd1260f12fcdb61efebee801b3/gadeu-0.0.9.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-12 09:28:21",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "wilson0x4d",
"github_project": "gadeu",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "gadeu"
}