add a notion of admin users

and an @admin_only decorator for restricted methods
This commit is contained in:
MinRK
2014-09-13 22:38:59 -07:00
parent 6d753ec6ea
commit 53edc0b2f7
4 changed files with 51 additions and 21 deletions

View File

@@ -158,6 +158,12 @@ class JupyterHubApp(Application):
debug_db = Bool(False)
db = Any()
admin_users = Set(config=True,
help="""list of usernames of admin users
If unspecified, all users are admin.
"""
)
tornado_settings = Dict(config=True)
handlers = List()
@@ -300,6 +306,7 @@ class JupyterHubApp(Application):
log=self.log,
db=self.db,
hub=self.hub,
admin_users=self.admin_users,
authenticator=import_item(self.authenticator)(config=self.config),
spawner_class=import_item(self.spawner_class),
base_url=base_url,

View File

@@ -56,6 +56,10 @@ class BaseHandler(RequestHandler):
# Login and cookie-related
#---------------------------------------------------------------
@property
def admin_users(self):
return self.settings.setdefault('admin_users', set())
def get_current_user_token(self):
"""get_current_user from Authorization header token"""
auth_header = self.request.headers.get('Authorization', '')
@@ -90,10 +94,10 @@ class BaseHandler(RequestHandler):
def user_from_username(self, username):
"""Get ORM User for username"""
user = self.db.query(orm.User).filter(orm.User.name==username).first()
if user is None:
user = orm.User(name=username)
admin = (not self.admin_users) or username in self.admin_users
user = orm.User(name=username, admin=admin)
self.db.add(user)
self.db.commit()
return user

View File

@@ -8,7 +8,7 @@ import uuid
from sqlalchemy.types import TypeDecorator, VARCHAR
from sqlalchemy import (
Column, Integer, String, ForeignKey, Unicode, Binary,
Column, Integer, String, ForeignKey, Unicode, Binary, Boolean,
)
from sqlalchemy.ext.declarative import declarative_base, declared_attr
from sqlalchemy.orm import sessionmaker, relationship, backref
@@ -157,6 +157,7 @@ class User(Base):
# should we allow multiple servers per user?
_server_id = Column(Integer, ForeignKey('servers.id'))
server = relationship(Server, primaryjoin=_server_id == Server.id)
admin = Column(Boolean, default=False)
api_tokens = relationship("APIToken", backref="user")
cookie_tokens = relationship("CookieToken", backref="user")

View File

@@ -29,24 +29,42 @@ def wait_for_server(ip, port, timeout=10):
else:
break
def auth_decorator(check_auth):
"""Make an authentication decorator
def token_authenticated(method):
"""decorator for a method authenticated only by the Authorization token header"""
def check_token(self, *args, **kwargs):
I heard you like decorators, so I put a decorator
in your decorator, so you can decorate while you decorate.
"""
def decorator(method):
def decorated(self, *args, **kwargs):
check_auth(self)
return method(self, *args)
decorated.__name__ = method.__name__
decorated.__doc__ = method.__doc__
return decorated
decorator.__name__ = check_auth.__name__
decorator.__doc__ = check_auth.__doc__
return decorator
@auth_decorator
def token_authenticated(self):
"""decorator for a method authenticated only by the Authorization token header
(no cookies)
"""
if self.get_current_user_token() is None:
raise web.HTTPError(403)
return method(self, *args, **kwargs)
check_token.__name__ = method.__name__
check_token.__doc__ = method.__doc__
return check_token
def authenticated_403(method):
"""decorator like web.authenticated, but raise 403 instead of redirect to login"""
def check_user(self, *args, **kwargs):
@auth_decorator
def authenticated_403(self):
"""like web.authenticated, but raise 403 instead of redirect to login"""
if self.get_current_user() is None:
raise web.HTTPError(403)
return method(self, *args, **kwargs)
check_user.__name__ = method.__name__
check_user.__doc__ = method.__doc__
return check_user
@auth_decorator
def admin_only(self):
"""decorator for restricting access to admin users"""
user = self.get_current_user()
if user is None or not user.admin:
raise web.HTTPError(403)