-
-
Notifications
You must be signed in to change notification settings - Fork 115
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
1 parent
199d23f
commit 355ffef
Showing
5 changed files
with
186 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,58 @@ | ||
from microdot import Microdot, Response, redirect | ||
from microdot_session import set_session_secret_key, with_session, \ | ||
update_session, delete_session | ||
|
||
BASE_TEMPLATE = '''<!doctype html> | ||
<html> | ||
<head> | ||
<title>Microdot login example</title> | ||
</head> | ||
<body> | ||
<h1>Microdot login example</h1> | ||
{content} | ||
</body> | ||
</html>''' | ||
|
||
LOGGED_OUT = '''<p>You are not logged in.</p> | ||
<form method="POST"> | ||
<p> | ||
Username: | ||
<input type="text" name="username" autofocus /> | ||
</p> | ||
<input type="submit" value="Submit" /> | ||
</form>''' | ||
|
||
LOGGED_IN = '''<p>Hello <b>{username}</b>!</p> | ||
<form method="POST" action="/logout"> | ||
<input type="submit" value="Logout" /> | ||
</form>''' | ||
|
||
app = Microdot() | ||
set_session_secret_key('top-secret') | ||
Response.default_content_type = 'text/html' | ||
|
||
|
||
@app.get('/') | ||
@app.post('/') | ||
@with_session | ||
def index(req, session): | ||
username = session.get('username') | ||
if req.method == 'POST': | ||
username = req.form.get('username') | ||
update_session(req, {'username': username}) | ||
return redirect('/') | ||
if username is None: | ||
return BASE_TEMPLATE.format(content=LOGGED_OUT) | ||
else: | ||
return BASE_TEMPLATE.format(content=LOGGED_IN.format( | ||
username=username)) | ||
|
||
|
||
@app.post('/logout') | ||
def logout(req): | ||
delete_session(req) | ||
return redirect('/') | ||
|
||
|
||
if __name__ == '__main__': | ||
app.run() |
File renamed without changes.
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,55 @@ | ||
import jwt | ||
|
||
secret_key = None | ||
|
||
|
||
def set_session_secret_key(key): | ||
global secret_key | ||
secret_key = key | ||
|
||
|
||
def get_session(request): | ||
global secret_key | ||
if not secret_key: | ||
raise ValueError('The session secret key is not configured') | ||
session = request.cookies.get('session') | ||
if session is None: | ||
return {} | ||
try: | ||
session = jwt.decode(session, secret_key, algorithms=['HS256']) | ||
except jwt.exceptions.PyJWTError: # pragma: no cover | ||
raise | ||
return {} | ||
return session | ||
|
||
|
||
def update_session(request, session): | ||
if not secret_key: | ||
raise ValueError('The session secret key is not configured') | ||
|
||
encoded_session = jwt.encode(session, secret_key, algorithm='HS256') | ||
|
||
@request.after_request | ||
def _update_session(request, response): | ||
response.set_cookie('session', encoded_session, http_only=True) | ||
return response | ||
|
||
|
||
def delete_session(request): | ||
@request.after_request | ||
def _delete_session(request, response): | ||
response.set_cookie('session', '', http_only=True, | ||
expires='Thu, 01 Jan 1970 00:00:01 GMT') | ||
return response | ||
|
||
|
||
def with_session(f): | ||
def wrapper(request, *args, **kwargs): | ||
return f(request, get_session(request), *args, **kwargs) | ||
|
||
for attr in ['__name__', '__doc__', '__module__', '__qualname__']: | ||
try: | ||
setattr(wrapper, attr, getattr(f, attr)) | ||
except AttributeError: # pragma: no cover | ||
pass | ||
return wrapper |
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,72 @@ | ||
import unittest | ||
from microdot import Microdot | ||
from microdot_session import set_session_secret_key, get_session, \ | ||
update_session, delete_session, with_session | ||
from microdot_test_client import TestClient | ||
|
||
set_session_secret_key('top-secret!') | ||
|
||
|
||
class TestSession(unittest.TestCase): | ||
def setUp(self): | ||
self.app = Microdot() | ||
self.client = TestClient(self.app) | ||
|
||
def tearDown(self): | ||
pass | ||
|
||
def test_session(self): | ||
@self.app.get('/') | ||
def index(req): | ||
session = get_session(req) | ||
return str(session.get('name')) | ||
|
||
@self.app.get('/with') | ||
@with_session | ||
def session_context_manager(req, session): | ||
return str(session.get('name')) | ||
|
||
@self.app.post('/set') | ||
def set_session(req): | ||
update_session(req, {'name': 'joe'}) | ||
return 'OK' | ||
|
||
@self.app.post('/del') | ||
def del_session(req): | ||
delete_session(req) | ||
return 'OK' | ||
|
||
res = self.client.get('/') | ||
self.assertEqual(res.text, 'None') | ||
res = self.client.get('/with') | ||
self.assertEqual(res.text, 'None') | ||
|
||
res = self.client.post('/set') | ||
self.assertEqual(res.text, 'OK') | ||
|
||
res = self.client.get('/') | ||
self.assertEqual(res.text, 'joe') | ||
res = self.client.get('/with') | ||
self.assertEqual(res.text, 'joe') | ||
|
||
res = self.client.post('/del') | ||
self.assertEqual(res.text, 'OK') | ||
|
||
res = self.client.get('/') | ||
self.assertEqual(res.text, 'None') | ||
res = self.client.get('/with') | ||
self.assertEqual(res.text, 'None') | ||
|
||
def test_session_no_secret_key(self): | ||
set_session_secret_key(None) | ||
|
||
@self.app.get('/') | ||
def index(req): | ||
self.assertRaises(ValueError, get_session, req) | ||
self.assertRaises(ValueError, update_session, req, {}) | ||
return '' | ||
|
||
res = self.client.get('/') | ||
self.assertEqual(res.status_code, 200) | ||
|
||
set_session_secret_key('top-secret!') |
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 |
---|---|---|
|
@@ -20,6 +20,7 @@ deps= | |
pytest | ||
pytest-cov | ||
jinja2 | ||
pyjwt | ||
setenv= | ||
PYTHONPATH=libs/common | ||
|
||
|