Merge pull request #82 from minrk/secure_cookie

use secure cookies
This commit is contained in:
Min RK
2014-10-26 20:26:56 -07:00
6 changed files with 42 additions and 23 deletions

View File

@@ -12,9 +12,24 @@ from .base import APIHandler
class AuthorizationsAPIHandler(APIHandler):
class TokenAPIHandler(APIHandler):
@token_authenticated
def get(self, token):
orm_token = self.db.query(orm.APIToken).filter(orm.APIToken.token == token).first()
if orm_token is None:
raise web.HTTPError(404)
self.write(json.dumps({
'user' : orm_token.user.name,
}))
class CookieAPIHandler(APIHandler):
@token_authenticated
def get(self, cookie_name):
cookie_value = self.request.body
btoken = self.get_secure_cookie(cookie_name, cookie_value)
if not btoken:
raise web.HTTPError(404)
token = btoken.decode('utf8', 'replace')
orm_token = self.db.query(orm.CookieToken).filter(orm.CookieToken.token == token).first()
if orm_token is None:
raise web.HTTPError(404)
@@ -23,5 +38,6 @@ class AuthorizationsAPIHandler(APIHandler):
}))
default_handlers = [
(r"/api/authorizations/([^/]+)", AuthorizationsAPIHandler),
(r"/api/authorizations/cookie/([^/]+)", CookieAPIHandler),
(r"/api/authorizations/token/([^/]+)", TokenAPIHandler),
]

View File

@@ -83,12 +83,15 @@ class BaseHandler(RequestHandler):
def get_current_user_cookie(self):
"""get_current_user from a cookie token"""
token = self.get_cookie(self.hub.server.cookie_name, None)
if token:
btoken = self.get_secure_cookie(self.hub.server.cookie_name)
if btoken:
token = btoken.decode('utf8', 'replace')
cookie_token = orm.CookieToken.find(self.db, token)
if cookie_token:
return cookie_token.user
else:
# don't log the token itself
self.log.warn("Invalid cookie token")
# have cookie, but it's not valid. Clear it and start over.
self.clear_cookie(self.hub.server.cookie_name, path=self.hub.server.base_url)
@@ -128,7 +131,7 @@ class BaseHandler(RequestHandler):
cookie_token = user.new_cookie_token()
self.db.add(cookie_token)
self.db.commit()
self.set_cookie(
self.set_secure_cookie(
user.server.cookie_name,
cookie_token.token,
path=user.server.base_url,
@@ -139,7 +142,7 @@ class BaseHandler(RequestHandler):
cookie_token = user.new_cookie_token()
self.db.add(cookie_token)
self.db.commit()
self.set_cookie(
self.set_secure_cookie(
self.hub.server.cookie_name,
cookie_token.token,
path=self.hub.server.base_url)

View File

@@ -299,7 +299,6 @@ class User(Base):
hub = db.query(Hub).first()
self.server = Server(
cookie_name='%s-%s' % (hub.server.cookie_name, self.name),
cookie_secret=hub.server.cookie_secret,
base_url=url_path_join(base_url, 'user', self.name),
)
db.add(self.server)

View File

@@ -28,19 +28,20 @@ if V(IPython.__version__) < V('2.2'):
# which authenticate via the central auth server.
def verify_token(self, token):
def verify_token(self, cookie_name, encrypted_cookie):
"""monkeypatch method for token verification"""
token_cache = self.settings['token_cache']
if token in token_cache:
cookie_cache = self.settings['cookie_cache']
if encrypted_cookie in cookie_cache:
# we've seen this token before, don't ask upstream again
return token_cache[token]
return cookie_cache[encrypted_cookie]
hub_api_url = self.settings['hub_api_url']
hub_api_key = self.settings['hub_api_key']
r = requests.get(url_path_join(
hub_api_url, "authorizations", token,
hub_api_url, "authorizations/cookie", cookie_name,
),
headers = {'Authorization' : 'token %s' % hub_api_key}
headers = {'Authorization' : 'token %s' % hub_api_key},
data=encrypted_cookie,
)
if r.status_code == 404:
data = {'user' : ''}
@@ -49,16 +50,16 @@ def verify_token(self, token):
data = None
else:
data = r.json()
token_cache[token] = data
cookie_cache[encrypted_cookie] = data
return data
def get_current_user(self):
"""alternative get_current_user to query the central server"""
my_user = self.settings['user']
token = self.get_cookie(self.cookie_name, '')
if token:
auth_data = self.verify_token(token)
encrypted_cookie = self.get_cookie(self.cookie_name)
if encrypted_cookie:
auth_data = self.verify_token(self.cookie_name, encrypted_cookie)
if not auth_data:
# treat invalid token the same as no token
return None
@@ -123,10 +124,9 @@ class SingleUserNotebookApp(NotebookApp):
s = getattr(self, 'tornado_settings',
getattr(self, 'webapp_settings')
)
s['token_cache'] = {}
s['cookie_cache'] = {}
s['user'] = self.user
s['hub_api_key'] = env.pop('JPY_API_TOKEN')
s['cookie_secret'] = env.pop('JPY_COOKIE_SECRET')
s['cookie_name'] = self.cookie_name
s['login_url'] = url_path_join(self.hub_prefix, 'login')
s['hub_api_url'] = self.hub_api_url

View File

@@ -65,7 +65,6 @@ class Spawner(LoggingConfigurable):
for key in self.env_keep:
if key in os.environ:
env[key] = os.environ[key]
env['JPY_COOKIE_SECRET'] = self.user.server.cookie_secret
env['JPY_API_TOKEN'] = self.api_token
return env

View File

@@ -48,23 +48,25 @@ def test_auth_api(app):
# make a new cookie token
user = db.query(orm.User).first()
api_token = user.new_api_token()
db.add(api_token)
cookie_token = user.new_cookie_token()
db.add(cookie_token)
db.commit()
# check success:
r = api_request(app, 'authorizations', cookie_token.token)
r = api_request(app, 'authorizations/token', api_token.token)
assert r.status_code == 200
reply = r.json()
assert reply['user'] == user.name
# check fail
r = api_request(app, 'authorizations', cookie_token.token,
r = api_request(app, 'authorizations/token', api_token.token,
headers={'Authorization': 'no sir'},
)
assert r.status_code == 403
r = api_request(app, 'authorizations', cookie_token.token,
r = api_request(app, 'authorizations/token', api_token.token,
headers={'Authorization': 'token: %s' % cookie_token.token},
)
assert r.status_code == 403