moved security to buckler
This commit is contained in:
parent
921163f454
commit
10aa341bff
107
buckler_flask.py
Normal file
107
buckler_flask.py
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Session interface middlewares to integrate the flask app with Buckler.
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import urllib.parse
|
||||||
|
import urllib.request
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from flask.sessions import SessionInterface, SessionMixin
|
||||||
|
from flask import session, redirect, request
|
||||||
|
|
||||||
|
import config
|
||||||
|
|
||||||
|
class BucklerSessionInterface(SessionInterface):
|
||||||
|
"""
|
||||||
|
Queries the Buckler server for session data to the current user and
|
||||||
|
application.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.url = config.buckler['url']
|
||||||
|
self.app_id = config.buckler['app_id']
|
||||||
|
self.app_key = config.buckler['app_key']
|
||||||
|
|
||||||
|
def open_session(self, app, request):
|
||||||
|
"""Called when a request is initiated."""
|
||||||
|
user_id = request.cookies.get('userid')
|
||||||
|
user_sid = request.cookies.get('session')
|
||||||
|
|
||||||
|
params = {
|
||||||
|
'app_id': self.app_id,
|
||||||
|
'app_key': self.app_key,
|
||||||
|
'userid': user_id,
|
||||||
|
'session': user_sid
|
||||||
|
}
|
||||||
|
params = urllib.parse.urlencode(params)
|
||||||
|
req = urllib.request.Request(self.url + f"/get_session?{params}")
|
||||||
|
res = urllib.request.urlopen(req)
|
||||||
|
data = json.loads(res.read())
|
||||||
|
if data.get('error'):
|
||||||
|
return None
|
||||||
|
session = BucklerSession()
|
||||||
|
session.update(data['session_data'])
|
||||||
|
session.meta = data['meta']
|
||||||
|
session.cookies = request.cookies
|
||||||
|
return session
|
||||||
|
|
||||||
|
def save_session(self, app, session, response):
|
||||||
|
"""Called at the end of a request."""
|
||||||
|
if not session.modified:
|
||||||
|
return
|
||||||
|
user_id = session.meta.get('user_id')
|
||||||
|
user_sid = session.meta.get('user_sid')
|
||||||
|
|
||||||
|
params = {
|
||||||
|
'app_id': self.app_id,
|
||||||
|
'app_key': self.app_key,
|
||||||
|
'userid': user_id,
|
||||||
|
'session': user_sid
|
||||||
|
}
|
||||||
|
params = urllib.parse.urlencode(params)
|
||||||
|
data = json.dumps(session)
|
||||||
|
req = urllib.request.Request(
|
||||||
|
self.url + f"/set_session?{params}",
|
||||||
|
data=data.encode('utf8'),
|
||||||
|
method='POST')
|
||||||
|
res = urllib.request.urlopen(req)
|
||||||
|
|
||||||
|
last_used = datetime.fromisoformat(session.meta['last_used'])
|
||||||
|
now = datetime.now(last_used.tzinfo)
|
||||||
|
delta = now - last_used
|
||||||
|
if delta.seconds > 600:
|
||||||
|
response.set_cookie(
|
||||||
|
'userid',
|
||||||
|
session.cookies['userid'],
|
||||||
|
max_age=30*24*60*60,
|
||||||
|
secure=True,
|
||||||
|
httponly=True)
|
||||||
|
response.set_cookie(
|
||||||
|
'session',
|
||||||
|
session.cookies['session'],
|
||||||
|
max_age=30*24*60*60,
|
||||||
|
secure=True,
|
||||||
|
httponly=True)
|
||||||
|
|
||||||
|
|
||||||
|
class BucklerSession(dict, SessionMixin):
|
||||||
|
"""A server side session class based on the Buckler security shield."""
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.modified = False
|
||||||
|
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
super().__setitem__(key, value)
|
||||||
|
self.modified = True
|
||||||
|
|
||||||
|
|
||||||
|
def require_auth():
|
||||||
|
"""
|
||||||
|
Requires the user to be properly authenticated with Buckler before
|
||||||
|
accessing any views on the application.
|
||||||
|
"""
|
||||||
|
if not hasattr(session, 'meta'):
|
||||||
|
resp = redirect(config.buckler['login_url'])
|
||||||
|
resp.set_cookie('redirect', request.url)
|
||||||
|
return resp
|
|
@ -4,6 +4,13 @@ Configuration settings for the Juice IOT hub server.
|
||||||
`url_prefix` is the root path you wish app to reside at
|
`url_prefix` is the root path you wish app to reside at
|
||||||
eg. https://example.com/juice.
|
eg. https://example.com/juice.
|
||||||
`registration_open` whether or not new accounts may be registered.
|
`registration_open` whether or not new accounts may be registered.
|
||||||
|
`buckler` specifies settings pertaining to the Buckler server.
|
||||||
"""
|
"""
|
||||||
url_prefix = '/juice'
|
url_prefix = '/juice'
|
||||||
registration_open = True
|
registration_open = True
|
||||||
|
buckler = {
|
||||||
|
'url': "http://127.0.0.1:5400/buckler",
|
||||||
|
'app_id': 2,
|
||||||
|
'app_key': """lol""",
|
||||||
|
'login_url': "/buckler/login",
|
||||||
|
}
|
||||||
|
|
13
juice.py
13
juice.py
|
@ -15,11 +15,11 @@ import config
|
||||||
import models
|
import models
|
||||||
from auth import auth_required
|
from auth import auth_required
|
||||||
from tools import make_error
|
from tools import make_error
|
||||||
|
import buckler_flask
|
||||||
|
|
||||||
app_views = Blueprint('app_views', __name__)
|
app_views = Blueprint('app_views', __name__)
|
||||||
|
|
||||||
@app_views.route('/')
|
@app_views.route('/')
|
||||||
@auth_required
|
|
||||||
def index():
|
def index():
|
||||||
"""
|
"""
|
||||||
The index page.
|
The index page.
|
||||||
|
@ -35,7 +35,6 @@ def index():
|
||||||
|
|
||||||
|
|
||||||
@app_views.route('/toggle')
|
@app_views.route('/toggle')
|
||||||
@auth_required
|
|
||||||
def toggle():
|
def toggle():
|
||||||
"""
|
"""
|
||||||
Toggles the state of a RelayDevice.
|
Toggles the state of a RelayDevice.
|
||||||
|
@ -56,7 +55,6 @@ def toggle():
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@app_views.route('/edit')
|
@app_views.route('/edit')
|
||||||
@auth_required
|
|
||||||
def edit():
|
def edit():
|
||||||
"""
|
"""
|
||||||
Edits the text of a particular field.
|
Edits the text of a particular field.
|
||||||
|
@ -102,7 +100,6 @@ def edit():
|
||||||
|
|
||||||
|
|
||||||
@app_views.route('/new_device')
|
@app_views.route('/new_device')
|
||||||
@auth_required
|
|
||||||
def new_device():
|
def new_device():
|
||||||
"""
|
"""
|
||||||
Allows adding a new device. Accepts device_type parameter, returns
|
Allows adding a new device. Accepts device_type parameter, returns
|
||||||
|
@ -112,7 +109,7 @@ def new_device():
|
||||||
device_type = request.args.get('device_type')
|
device_type = request.args.get('device_type')
|
||||||
|
|
||||||
if device_type == 'RelayDevice':
|
if device_type == 'RelayDevice':
|
||||||
device = RelayDevice()
|
device = models.RelayDevice()
|
||||||
else:
|
else:
|
||||||
return make_error(400, "Unknown device type")
|
return make_error(400, "Unknown device type")
|
||||||
|
|
||||||
|
@ -134,7 +131,6 @@ def new_device():
|
||||||
|
|
||||||
|
|
||||||
@app_views.route('/lock_device')
|
@app_views.route('/lock_device')
|
||||||
@auth_required
|
|
||||||
def lock_device():
|
def lock_device():
|
||||||
"""
|
"""
|
||||||
Locks or unlocks a device to prevent or allow editing it's fields.
|
Locks or unlocks a device to prevent or allow editing it's fields.
|
||||||
|
@ -159,7 +155,6 @@ def lock_device():
|
||||||
|
|
||||||
|
|
||||||
@app_views.route('/delete')
|
@app_views.route('/delete')
|
||||||
@auth_required
|
|
||||||
def delete():
|
def delete():
|
||||||
"""
|
"""
|
||||||
Deletes a device.
|
Deletes a device.
|
||||||
|
@ -180,8 +175,10 @@ def delete():
|
||||||
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
app.session_interface = buckler_flask.BucklerSessionInterface()
|
||||||
|
app.before_request(buckler_flask.require_auth)
|
||||||
app.register_blueprint(app_views, url_prefix=config.url_prefix)
|
app.register_blueprint(app_views, url_prefix=config.url_prefix)
|
||||||
app.register_blueprint(auth.auth_views, url_prefix=config.url_prefix)
|
#app.register_blueprint(auth.auth_views, url_prefix=config.url_prefix)
|
||||||
app.jinja_env.undefined = "StrictUndefined"
|
app.jinja_env.undefined = "StrictUndefined"
|
||||||
if os.path.isfile('secret_key'):
|
if os.path.isfile('secret_key'):
|
||||||
with open('secret_key', 'rb') as file:
|
with open('secret_key', 'rb') as file:
|
||||||
|
|
Loading…
Reference in New Issue
Block a user