Commit 94dd59da authored by iv's avatar iv

Add possibility to revoke all cookies + fix origin.

parent 2be80aa2
...@@ -15,6 +15,12 @@ FS_PATH = '/tmp/couscous' ...@@ -15,6 +15,12 @@ FS_PATH = '/tmp/couscous'
ALLOWED_METHODS = ['GET', 'PUT', 'PROPFIND', 'PROPPATCH', 'MKCOL', 'DELETE', ALLOWED_METHODS = ['GET', 'PUT', 'PROPFIND', 'PROPPATCH', 'MKCOL', 'DELETE',
'COPY', 'MOVE', 'OPTIONS'] 'COPY', 'MOVE', 'OPTIONS']
def generate_key():
"""
set application's secret key used for HMAC signature
"""
app.secret_key = os.urandom(24)
def debug(content): def debug(content):
if app.debug: print(content) if app.debug: print(content)
...@@ -27,10 +33,11 @@ URI_BEGINNING_PATH = { ...@@ -27,10 +33,11 @@ URI_BEGINNING_PATH = {
'devices': '/webdav/devices/' 'devices': '/webdav/devices/'
} }
def make_cookie_content_to_be_signed(): def make_cookie_content_to_be_signed(origin=None):
""" cookie content is based on Origin header and User-Agent """ cookie content is based on Origin header and User-Agent
(later HMAC'ed) """ (later HMAC'ed) """
if not origin:
origin = request.headers.get('Origin') origin = request.headers.get('Origin')
useragent = request.headers.get('User-Agent') useragent = request.headers.get('User-Agent')
return str(origin) + str(useragent) return str(origin) + str(useragent)
...@@ -42,21 +49,26 @@ def verify_cookie(cookey): ...@@ -42,21 +49,26 @@ def verify_cookie(cookey):
is_correct = False is_correct = False
debug("verify_cookie: " + base64_decode(cookey)) debug("verify_cookie for origin: " + base64_decode(cookey))
cookie_value = request.cookies.get(str(cookey)) cookie_value = request.cookies.get(cookey)
if cookie_value: if cookie_value:
debug("cookie exists for this origin")
s = Signer(app.secret_key) s = Signer(app.secret_key)
debug("verify_cookie: " + cookie_value + ", " + \ expected_cookie_content = make_cookie_content_to_be_signed(base64_decode(cookey))
s.get_signature(make_cookie_content_to_be_signed())) expected_cookie_content = s.get_signature(expected_cookie_content)
if s.get_signature(make_cookie_content_to_be_signed()) == cookie_value: debug("verify_cookie: " + cookie_value + ", " + expected_cookie_content)
if expected_cookie_content == cookie_value:
debug('correct cookie')
is_correct = True is_correct = True
else:
debug('incorrect cookie')
return is_correct return is_correct
def is_authorized(cookies_list): def is_authorized(cookies_list):
debug('is authorized, looking into cookies:\n' + str(request.cookies))
origin = request.headers.get('Origin') origin = request.headers.get('Origin')
# TODO: accept requests from 127.0.0.1:port and localhost:port?
# accept any port of any local addresses: localhost and 127.0.0.0/8?
if origin is None: # request from same origin if origin is None: # request from same origin
return True return True
return verify_cookie(base64_encode(origin)) return verify_cookie(base64_encode(origin))
...@@ -81,7 +93,8 @@ def before_request(): ...@@ -81,7 +93,8 @@ def before_request():
'Authorization, Depth, If-Modified-Since, If-None-Match' 'Authorization, Depth, If-Modified-Since, If-None-Match'
headers['Access-Control-Expose-Headers'] = \ headers['Access-Control-Expose-Headers'] = \
'Content-Type, Last-Modified, WWW-Authenticate' 'Content-Type, Last-Modified, WWW-Authenticate'
headers['Access-Control-Allow-Origin'] = request.headers.get('Origin') origin = request.headers.get('Origin')
headers['Access-Control-Allow-Origin'] = origin
specific_header = request.headers.get('Access-Control-Request-Headers') specific_header = request.headers.get('Access-Control-Request-Headers')
...@@ -93,16 +106,18 @@ def before_request(): ...@@ -93,16 +106,18 @@ def before_request():
# tells the world we do CORS when authorized # tells the world we do CORS when authorized
debug('OPTIONS request special header: ' + specific_header) debug('OPTIONS request special header: ' + specific_header)
headers['Access-Control-Request-Headers'] = specific_header headers['Access-Control-Request-Headers'] = specific_header
headers['Access-Control-Allow-Origin'] = '*' headers['Access-Control-Allow-Origin'] = origin
headers['Access-Control-Allow-Methods'] = ', '.join(ALLOWED_METHODS) headers['Access-Control-Allow-Methods'] = ', '.join(ALLOWED_METHODS)
response = make_response(content, 200) response = make_response(content, 200)
response.headers = headers response.headers = headers
return response return response
else: else:
s = Signer(app.secret_key)
headers['WWW-Authenticate'] = 'Nayookie login_url=' + \ headers['WWW-Authenticate'] = 'Nayookie login_url=' + \
urlparse.urljoin(request.url_root, urlparse.urljoin(request.url_root,
URI_BEGINNING_PATH['authorization']) + '{?back_url}' URI_BEGINNING_PATH['authorization']) + '?sig=' + \
s.get_signature(origin) + '{&back_url,origin}'
response = make_response(content, 401) response = make_response(content, 401)
response.headers = headers response.headers = headers
# do not handle the request if not authorized # do not handle the request if not authorized
...@@ -303,18 +318,26 @@ def authorize(): ...@@ -303,18 +318,26 @@ def authorize():
filesystem via the webdav server filesystem via the webdav server
POST: set a cookie POST: set a cookie
""" """
origin = request.headers.get('Origin')
origin = request.args.get('origin')
if request.method == 'POST': if request.method == 'POST':
response = make_response() response = make_response()
if request.form.get('reset') == 'true':
debug('old key was: ' + app.secret_key)
generate_key()
debug('new key is: ' + app.secret_key)
s = Signer(app.secret_key) s = Signer(app.secret_key)
if s.get_signature(origin) == request.args.get('sig'):
key = base64_encode(str(origin)) key = base64_encode(str(origin))
back = request.args.get('back_url') back = request.args.get('back_url')
debug(back) sig = request.args.get('sig')
# TODO add checkbox to reset private key and invalidate all previous authorizations debug('Correct origin, setting cookie with info: ' + make_cookie_content_to_be_signed(origin=origin))
response.set_cookie(key, value=s.get_signature(make_cookie_content_to_be_signed()), max_age=None, response.set_cookie(key, value=s.get_signature(make_cookie_content_to_be_signed(origin=origin)),
expires=None, path='/', domain=None, secure=True, httponly=True) max_age=None, expires=None, path='/', domain=None, secure=True, httponly=True)
else:
return 'Something went wrong...'
if back: if back:
response.status = '301' # moved permanently response.status = '301' # moved permanently
...@@ -322,9 +345,11 @@ def authorize(): ...@@ -322,9 +345,11 @@ def authorize():
# what if not? use referer? send bad request error? just do nothing? # what if not? use referer? send bad request error? just do nothing?
else: else:
debug(request.args)
headers = request.headers headers = request.headers
response = make_response(render_template('authorization_page.html', response = make_response(render_template('authorization_page.html',
origin=origin, back_url=request.args.get('back_url'))) origin=request.args.get('origin'),
back_url=request.args.get('back_url')))
return response return response
@app.route(URI_BEGINNING_PATH['system']) @app.route(URI_BEGINNING_PATH['system'])
...@@ -362,5 +387,8 @@ if __name__ == '__main__': ...@@ -362,5 +387,8 @@ if __name__ == '__main__':
context.use_privatekey_file('ssl.key') context.use_privatekey_file('ssl.key')
context.use_certificate_file('ssl.cert') context.use_certificate_file('ssl.cert')
app.secret_key = os.urandom(24) if app.debug:
app.secret_key = 'maybe you should change me...'
else:
generate_key()
app.run(host="localhost", ssl_context=context) app.run(host="localhost", ssl_context=context)
<div>Headers: {{ headers }}</div>
<div>Allow origin: {{ origin }} ? </div> <div>Allow origin: {{ origin }} ? </div>
<div>You will then be redirected to {{ back_url }} .</div> <div>You will then be redirected to {{ back_url }} .</div>
<form action="" method="post"> <form action="" method="post">
<button name="mybutton" value="val">OK</button> <input type="checkbox" name="reset" value="true"> Invalidate all previous cookies <br>
<button name="accept" value="ok">OK</button>
</form> </form>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment