-
-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit b1134cd
Showing
12 changed files
with
471 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
# Byte-compiled / optimized / DLL files | ||
__pycache__/ | ||
*.py[cod] | ||
*$py.class | ||
|
||
# C extensions | ||
*.so | ||
|
||
# Distribution / packaging | ||
.Python | ||
env/ | ||
build/ | ||
develop-eggs/ | ||
dist/ | ||
downloads/ | ||
eggs/ | ||
.eggs/ | ||
lib/ | ||
lib64/ | ||
parts/ | ||
sdist/ | ||
var/ | ||
wheels/ | ||
*.egg-info/ | ||
.installed.cfg | ||
*.egg | ||
|
||
# PyInstaller | ||
# Usually these files are written by a python script from a template | ||
# before PyInstaller builds the exe, so as to inject date/other infos into it. | ||
*.manifest | ||
*.spec | ||
|
||
# Installer logs | ||
pip-log.txt | ||
pip-delete-this-directory.txt | ||
|
||
# Unit test / coverage reports | ||
htmlcov/ | ||
.tox/ | ||
.coverage | ||
.coverage.* | ||
.cache | ||
nosetests.xml | ||
coverage.xml | ||
*.cover | ||
.hypothesis/ | ||
|
||
# Translations | ||
*.mo | ||
*.pot | ||
|
||
# Django stuff: | ||
*.log | ||
local_settings.py | ||
|
||
# Flask stuff: | ||
instance/ | ||
.webassets-cache | ||
|
||
# Scrapy stuff: | ||
.scrapy | ||
|
||
# Sphinx documentation | ||
docs/_build/ | ||
|
||
# PyBuilder | ||
target/ | ||
|
||
# Jupyter Notebook | ||
.ipynb_checkpoints | ||
|
||
# pyenv | ||
.python-version | ||
|
||
# celery beat schedule file | ||
celerybeat-schedule | ||
|
||
# SageMath parsed files | ||
*.sage.py | ||
|
||
# dotenv | ||
.env | ||
|
||
# virtualenv | ||
.venv | ||
venv/ | ||
ENV/ | ||
|
||
# Spyder project settings | ||
.spyderproject | ||
.spyproject | ||
|
||
# Rope project settings | ||
.ropeproject | ||
|
||
# mkdocs documentation | ||
/site | ||
|
||
# mypy | ||
.mypy_cache/ | ||
|
||
|
||
# AMH Additional | ||
NOTES |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
from sanic import Sanic | ||
from sanic.response import json | ||
from aoiklivereload import LiveReloader | ||
from sanic_jwt.decorators import protected | ||
# from sanic_jwt.auth_bp import bp as sanic_jwt_auth_bp | ||
from sanic_jwt import initialize, exceptions | ||
|
||
|
||
reloader = LiveReloader() | ||
reloader.start_watcher_thread() | ||
|
||
|
||
def authenticate(request, *args, **kwargs): | ||
username = request.json.get('username', None) | ||
password = request.json.get('password', None) | ||
|
||
if not username or not password: | ||
raise exceptions.AuthenticationFailed("Missing username or password.") | ||
|
||
user = username_table.get(username, None) | ||
if user is None: | ||
raise exceptions.AuthenticationFailed("User not found.") | ||
|
||
if password != user.password: | ||
raise exceptions.AuthenticationFailed("Password is incorrect.") | ||
|
||
return user | ||
|
||
|
||
app = Sanic() | ||
initialize(app, authenticate) | ||
|
||
|
||
class User(object): | ||
def __init__(self, id, username, password): | ||
setattr(self, app.config.SANIC_JWT_USER_ID, id) | ||
self.username = username | ||
self.password = password | ||
|
||
def __str__(self): | ||
return "User(id='%s')" % self.id | ||
|
||
|
||
users = [ | ||
User(1, 'user1', 'abcxyz'), | ||
User(2, 'user2', 'abcxyz'), | ||
] | ||
|
||
username_table = {u.username: u for u in users} | ||
userid_table = {getattr(u, app.config.SANIC_JWT_USER_ID): u for u in users} | ||
|
||
|
||
@app.route("/") | ||
async def test(request): | ||
return json({"hello": "world"}) | ||
|
||
|
||
@app.route("/protected") | ||
@protected() | ||
async def test(request): | ||
return json({"protected": True}) | ||
|
||
if __name__ == "__main__": | ||
app.run(host="127.0.0.1", port=8000) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
aiofiles==0.3.1 | ||
AoikLiveReload==0.1.0 | ||
argh==0.26.2 | ||
httptools==0.0.9 | ||
pathtools==0.1.2 | ||
PyJWT==1.5.2 | ||
PyYAML==3.12 | ||
sanic==0.6.0 | ||
sanic-jwt==0.1.0 | ||
ujson==1.35 | ||
uvloop==0.8.0 | ||
watchdog==0.8.3 | ||
websockets==3.3 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
from sanic_jwt.blueprint import bp as sanic_jwt_auth_bp | ||
from sanic_jwt.authentication import SanicJWTAuthentication | ||
from sanic_jwt import settings | ||
|
||
|
||
def initialize(app, authenticate): | ||
app.blueprint(sanic_jwt_auth_bp, url_prefix='/auth') | ||
app.auth = SanicJWTAuthentication(app, authenticate) | ||
app.config.from_object(settings) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import importlib | ||
import jwt | ||
|
||
from sanic_jwt import exceptions | ||
|
||
|
||
class BaseAuthentication(object): | ||
def __init__(self, app, authenticate): | ||
self.app = app | ||
self.authenticate = authenticate | ||
|
||
|
||
class SanicJWTAuthentication(BaseAuthentication): | ||
def _decode(self, token): | ||
secret = self._get_secret() | ||
algorithm = self._get_algorithm() | ||
return jwt.decode(token, secret, algorithms=[algorithm]) | ||
|
||
def _get_algorithm(self): | ||
return self.app.config.SANIC_JWT_ALGORITHM | ||
|
||
def _get_payload(self, user): | ||
parts = self.app.config.SANIC_JWT_PAYLOAD_HANDLER.split('.') | ||
fn = parts.pop() | ||
module = importlib.import_module('.'.join(parts)) | ||
method = getattr(module, fn) | ||
payload = method(self, user) | ||
|
||
return payload | ||
|
||
def _get_secret(self): | ||
return self.app.config.SANIC_JWT_SECRET | ||
|
||
def _get_token(self, request): | ||
header = request.headers.get(self.app.config.SANIC_JWT_AUTHORIZATION_HEADER, None) | ||
if header: | ||
try: | ||
prefix, token = header.split(' ') | ||
if prefix != self.app.config.SANIC_JWT_AUTHORIZATION_HEADER_PREFIX: | ||
raise Exception | ||
except Exception: | ||
raise exceptions.InvalidAuthorizationHeader() | ||
|
||
return token | ||
|
||
raise exceptions.MissingAuthorizationHeader() | ||
|
||
def get_access_token(self, user): | ||
payload = self._get_payload(user) | ||
secret = self._get_secret() | ||
algorithm = self._get_algorithm() | ||
|
||
return jwt.encode(payload, secret, algorithm=algorithm) | ||
|
||
def is_authenticated(self, request, *args, **kwargs): | ||
try: | ||
is_valid, _, __ = self.verify(request, *args, **kwargs) | ||
except Exception: | ||
raise exceptions.Unauthorized() | ||
|
||
return is_valid | ||
|
||
def verify(self, request, *args, **kwargs): | ||
token = self._get_token(request) | ||
is_valid = True | ||
reason = None | ||
|
||
try: | ||
self._decode(token) | ||
except jwt.exceptions.ExpiredSignatureError: | ||
is_valid = False | ||
reason = 'Signature has expired' | ||
|
||
status = 200 if is_valid else 400 | ||
|
||
return is_valid, status, reason |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
from sanic.response import json | ||
from sanic import Blueprint | ||
|
||
|
||
bp = Blueprint('auth_bp') | ||
|
||
|
||
@bp.post('/') | ||
async def authenticate(request, *args, **kwargs): | ||
try: | ||
user = request.app.auth.authenticate(request, *args, **kwargs) | ||
except Exception as e: | ||
raise e | ||
|
||
|
||
return json({ | ||
'access_token': request.app.auth.get_access_token(user) | ||
}) | ||
|
||
@bp.get('/verify') | ||
async def verify(request, *args, **kwargs): | ||
is_valid, status, reason = request.app.auth.verify(request, *args, **kwargs) | ||
|
||
response = { | ||
'valid': is_valid | ||
} | ||
|
||
if reason: | ||
response.update({'reason': reason}) | ||
|
||
return json(response, status=status) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
from functools import wraps | ||
from sanic.response import json | ||
|
||
|
||
def protected(): | ||
def decorator(f): | ||
@wraps(f) | ||
async def decorated_function(request, *args, **kwargs): | ||
is_authorized = request.app.auth.is_authenticated(request, *args, **kwargs) | ||
|
||
if is_authorized: | ||
# the user is authorized. | ||
# run the handler method and return the response | ||
response = await f(request, *args, **kwargs) | ||
return response | ||
else: | ||
# the user is not authorized. | ||
return json({ | ||
'status': 'not_authorized', | ||
}, 403) | ||
return decorated_function | ||
return decorator |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
from sanic.exceptions import SanicException, Unauthorized as SanicUnauthorized, add_status_code | ||
|
||
|
||
@add_status_code(401) | ||
class AuthenticationFailed(SanicException): | ||
def __init__(self, message="Authentication failed."): | ||
super().__init__(message) | ||
|
||
|
||
@add_status_code(400) | ||
class MissingAuthorizationHeader(SanicException): | ||
def __init__(self, message="Authorization header not present."): | ||
super().__init__(message) | ||
|
||
|
||
@add_status_code(400) | ||
class InvalidAuthorizationHeader(SanicException): | ||
def __init__(self, message="Authorization header is invalid."): | ||
super().__init__(message) | ||
|
||
|
||
class Unauthorized(SanicUnauthorized): | ||
def __init__(self): | ||
super().__init__("Auth required.", "Bearer") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
from datetime import datetime, timedelta | ||
|
||
|
||
def build_payload(authenticator, user): | ||
user_id = getattr(user, authenticator.app.config.SANIC_JWT_USER_ID) | ||
delta = timedelta(seconds=authenticator.app.config.SANIC_JWT_EXPIRATION_DELTA) | ||
exp = datetime.utcnow() + delta | ||
|
||
return { | ||
'user_id': user_id, | ||
'exp': exp, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
SANIC_JWT_ALGORITHM = 'HS256' | ||
SANIC_JWT_AUTHORIZATION_HEADER = 'authorization' | ||
SANIC_JWT_AUTHORIZATION_HEADER_PREFIX = 'Bearer' | ||
SANIC_JWT_EXPIRATION_DELTA = 60 * 5 * 6 | ||
SANIC_JWT_PAYLOAD_HANDLER = 'sanic_jwt.handlers.build_payload' | ||
SANIC_JWT_SECRET = 'This is a big secret. Shhhhh' | ||
SANIC_JWT_USER_ID = 'user_id' |
Oops, something went wrong.