diff --git a/jupyterhub/remoteappmanager_config.py b/jupyterhub/remoteappmanager_config.py index d71feecc9..9ca103265 100644 --- a/jupyterhub/remoteappmanager_config.py +++ b/jupyterhub/remoteappmanager_config.py @@ -63,7 +63,12 @@ # database_class = "remoteappmanager.db.orm.ORMDatabase" # database_kwargs = { # "url": "sqlite:///"+os.path.abspath('./remoteappmanager.db')} - +# +# # User accounting +# +# auto_user_creation = True +# demo_applications = ['my-demo-app'] +# # # ---------------- # # Google Analytics # # ---------------- diff --git a/remoteappmanager/base_application.py b/remoteappmanager/base_application.py index 76e44440e..a82ee931b 100644 --- a/remoteappmanager/base_application.py +++ b/remoteappmanager/base_application.py @@ -131,8 +131,20 @@ def _user_default(self): user_name = self.command_line_config.user login_service = self.command_line_config.login_service user = User(name=user_name, login_service=login_service) - user.account = self.db.get_user(user_name=user_name) + # Handle User accounting + if self.db.get_user(user_name=user.name) is None: + self.log.info( + "User account not found for {}:".format(user.name)) + if self.file_config.auto_user_creation: + self.log.info( + "Creating new User account for {}:".format(user.name)) + self.db.create_user(user.name) + else: + self.log.info("User account found for {}:".format(user.name)) + user.account = self.db.get_user(user_name=user.name) + + # Add any demo apps to registry self.log.info("Adding demo apps to User registry:") self._add_demo_apps(user) @@ -168,20 +180,26 @@ def start(self): # Private def _add_demo_apps(self, user): """Grant access to any demo applications provided for user""" + if user.account is None: + self.log.debug("No user account available") + return - if user.demo_applications: + if not self.file_config.demo_applications: + self.log.debug("No demo applications available") return # Add all demo applications already registered for application in self.db.list_applications(): - if application.image in user.demo_applications: - self.log.info(application.image) + if application.image in self.file_config.demo_applications: + self.log.debug(f"Available image: {application.image}") self.db.grant_access( application.image, user.name, + '', False, True, - None + None, + True ) def _webapi_resources(self): diff --git a/remoteappmanager/file_config.py b/remoteappmanager/file_config.py index 6f5c96aa9..29e4969b2 100644 --- a/remoteappmanager/file_config.py +++ b/remoteappmanager/file_config.py @@ -2,7 +2,7 @@ import tornado.options from docker import tls -from traitlets import HasTraits, Int, Unicode, Bool, Dict +from traitlets import HasTraits, List, Int, Unicode, Bool, Dict from remoteappmanager import paths from remoteappmanager.traitlets import set_traits_from_dict @@ -71,6 +71,13 @@ class FileConfig(HasTraits): help="The google analytics tracking id" ) + #: Provide names of any default applications granted to users + demo_applications = List() + + #: Whether or not to automatically create user accounts upon starting + #: up the application if they do not already exist in the database + auto_user_creation = Bool(False) + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Sets the default of the docker configuration options from the diff --git a/remoteappmanager/tests/test_application.py b/remoteappmanager/tests/test_application.py index 76ec5e6aa..25d19e7b0 100644 --- a/remoteappmanager/tests/test_application.py +++ b/remoteappmanager/tests/test_application.py @@ -50,7 +50,8 @@ def test_initialization_with_sqlite_db(self): self.assertIsNotNone(app.container_manager) self.assertIsNotNone(app.hub) self.assertEqual(app.user.name, "johndoe") - self.assertEqual(app.user.account, None) + self.assertEqual(app.user.login_service, "") + self.assertIsNone(app.user.account) def test_error_default_value_with_unimportable_accounting(self): self.file_config.database_class = "not.importable.Class" @@ -93,6 +94,34 @@ def test_initialization(self): self.assertEqual(app.user.name, "johndoe") self.assertIsInstance(app.user.account, test_csv_db.CSVUser) + def test_initialization_with_demo_applications(self): + # Initialise database + sqlite_file_path = os.path.join(self.tempdir, "sqlite.db") + utils.init_sqlite_db(sqlite_file_path) + + # Add demo app to database using remoteappmanager API + from remoteappmanager.db.orm import ORMDatabase + test_db = ORMDatabase("sqlite:///"+sqlite_file_path) + test_db.create_application('my-demo-app') + del test_db + + self.file_config.database_class = ( + "remoteappmanager.db.orm.ORMDatabase") + self.file_config.database_kwargs = { + "url": "sqlite:///"+sqlite_file_path} + self.file_config.demo_applications = ['my-demo-app'] + self.file_config.auto_user_creation = True + + app = Application(self.command_line_config, + self.file_config, + self.environment_config) + + self.assertEqual(app.user.name, "johndoe") + self.assertIsNotNone(app.user.account) + + user_apps = app.db.get_accounting_for_user(app.user.account) + self.assertEqual('my-demo-app', user_apps[0].application.image) + def test_start(self): with patch( "remoteappmanager.application.Application.listen" diff --git a/remoteappmanager/tests/test_user.py b/remoteappmanager/tests/test_user.py index 657b630f2..1c075c726 100644 --- a/remoteappmanager/tests/test_user.py +++ b/remoteappmanager/tests/test_user.py @@ -6,13 +6,11 @@ class TestUser(TestCase): def setUp(self): + self.user = User(name='test-user', + login_service='Basic', + demo_applications=['some-image']) - self.user = User() - - def test_demo_applications(self): - - self.assertListEqual([], self.user.demo_applications) - self.assertListEqual( - [], - self.user.demo_applications - ) + def test_init(self): + self.assertEqual('test-user', self.user.name) + self.assertIsNone(self.user.account) + self.assertEqual('Basic', self.user.login_service) diff --git a/remoteappmanager/user.py b/remoteappmanager/user.py index ae922c277..d75e70341 100644 --- a/remoteappmanager/user.py +++ b/remoteappmanager/user.py @@ -13,9 +13,3 @@ class User(HasTraits): #: Reference to the authenticator method used for user login login_service = Unicode() - - @property - def demo_applications(self): - """Can be implemented to provide any default applications - granted by the user""" - return []