diff --git a/pillar/application/modules/projects.py b/pillar/application/modules/projects.py index 4561c44d..4291a1e3 100644 --- a/pillar/application/modules/projects.py +++ b/pillar/application/modules/projects.py @@ -8,6 +8,7 @@ from flask import g, Blueprint, request, abort, current_app from application.utils import remove_private_keys, authorization, PillarJSONEncoder from application.utils.gcs import GoogleCloudStorageBucket +from application.utils.authorization import user_has_role from manage_extra.node_types.asset import node_type_asset from manage_extra.node_types.blog import node_type_blog from manage_extra.node_types.comment import node_type_comment @@ -25,6 +26,11 @@ def before_inserting_projects(items): :param items: List of project docs that have been inserted (normally one) """ + + # Allow admin users to do whatever they want. + if user_has_role(u'admin'): + return + for item in items: item.pop('url', None) @@ -93,7 +99,10 @@ def after_inserting_project(project, db_user): node_type_asset, node_type_page, node_type_comment] - project['url'] = "p-{!s}".format(project_id) + + # Allow admin users to use whatever url they want. + if not user_has_role(u'admin') or not project.get('url'): + project['url'] = "p-{!s}".format(project_id) # Initialize storage page (defaults to GCS) if current_app.config.get('TESTING'): @@ -112,13 +121,8 @@ def after_inserting_project(project, db_user): abort_with_error(status) -@blueprint.route('/create', methods=['POST']) -@authorization.require_login(require_roles={'admin', 'subscriber'}) -def create_project(): - """Creates a new project.""" - - project_name = request.form['project_name'] - user_id = g.current_user['user_id'] +def _create_new_project(project_name, user_id, overrides): + """Creates a new project owned by the given user.""" log.info('Creating new project "%s" for user %s', project_name, user_id) @@ -136,6 +140,8 @@ def create_project(): 'summary': '', 'category': 'assets', # TODO: allow the user to choose this. } + if overrides is not None: + project.update(overrides) result, _, _, status = post_internal('projects', project) if status != 201: @@ -143,14 +149,26 @@ def create_project(): return abort_with_error(status) project.update(result) - project_id = result['_id'] - log.info('Created project %s for user %s', project_id, user_id) + log.info('Created project %s for user %s', project['_id'], user_id) + + return project + + +@blueprint.route('/create', methods=['POST']) +@authorization.require_login(require_roles={'admin', 'subscriber'}) +def create_project(overrides=None): + """Creates a new project.""" + + project_name = request.form['project_name'] + user_id = g.current_user['user_id'] + + project = _create_new_project(project_name, user_id, overrides) # Return the project in the response. resp = current_app.response_class(json.dumps(project, cls=PillarJSONEncoder), mimetype='application/json', status=201, - headers={'Location': '/projects/%s' % project_id}) + headers={'Location': '/projects/%s' % project['_id']}) return resp diff --git a/pillar/application/utils/authorization.py b/pillar/application/utils/authorization.py index a3b0b07a..8726ff77 100644 --- a/pillar/application/utils/authorization.py +++ b/pillar/application/utils/authorization.py @@ -109,3 +109,13 @@ def require_login(require_roles=set()): return func(*args, **kwargs) return wrapper return decorator + + +def user_has_role(role): + """Returns True iff the user is logged in and has the given role.""" + + current_user = g.get('current_user') + if current_user is None: + return False + + return role in current_user['roles'] diff --git a/pillar/manage.py b/pillar/manage.py index fd02621f..ee8b7568 100755 --- a/pillar/manage.py +++ b/pillar/manage.py @@ -67,13 +67,14 @@ def put_item(collection, item): @manager.command -def setup_db(): +def setup_db(admin_email): """Setup the database - Create admin, subscriber and demo Group collection - Create admin user (must use valid blender-id credentials) - Create one project """ - # groups_collection = app.data.driver.db['groups'] + + # Create default groups groups_list = [] for group in ['admin', 'subscriber', 'demo']: g = {'name': group} @@ -81,58 +82,31 @@ def setup_db(): groups_list.append(g[0]['_id']) print("Creating group {0}".format(group)) - while True: - admin_username = raw_input('Admin email:') - if len(admin_username) < 1: - print ("Username is too short") - else: - break + # Create admin user + user = {'username': admin_email, + 'groups': groups_list, + 'roles': ['admin', 'subscriber', 'demo'], + 'settings': {'email_communications': 1}, + 'auth': [], + 'full_name': admin_email, + 'email': admin_email} + result, _, _, status = post_internal('users', user) + if status != 201: + raise SystemExit('Error creating user {}: {}'.format(admin_email, result)) + user.update(result) + print("Created user {0}".format(user['_id'])) - user = dict( - username=admin_username, - groups=groups_list, - roles=['admin', 'subscriber', 'demo'], - settings=dict(email_communications=1), - auth=[], - full_name=admin_username, - email=admin_username, - ) - user = post_internal('users', user) - print("Created user {0}".format(user[0]['_id'])) + # Create a default project by faking a POST request. + with app.test_request_context(data={'project_name': u'Default Project'}): + from flask import g + from application.modules import projects - # TODO: Create a default project - default_permissions = _default_permissions() + g.current_user = {'user_id': user['_id'], + 'groups': user['groups'], + 'roles': set(user['roles'])} - node_type_blog['permissions'] = default_permissions - node_type_post['permissions'] = default_permissions - node_type_comment['permissions'] = default_permissions - - project = dict( - owners=dict(users=[], groups=[]), - description='Default Project', - name='Default Project', - node_types=[ - node_type_blog, - node_type_post, - node_type_comment - ], - status='published', - user=user[0]['_id'], - is_private=False, - permissions=default_permissions, - url='default-project', - summary='Default Project summary', - category='training' - ) - # Manually insert into db, since using post_internal would trigger hook - # TODO: fix this by bassing the context (and the user to g object) - projects_collection = app.data.driver.db['projects'] - project = projects_collection.insert_one(project) - print("Created default project {0}".format(project.inserted_id)) - gcs_storage = GoogleCloudStorageBucket(str(project.inserted_id)) - - if gcs_storage.bucket.exists(): - print("Created CGS instance") + projects.create_project(overrides={'url': 'default-project', + 'is_private': False}) def _default_permissions(): diff --git a/tests/common_test_class.py b/tests/common_test_class.py index a51e1e32..c85633ef 100644 --- a/tests/common_test_class.py +++ b/tests/common_test_class.py @@ -96,13 +96,13 @@ class AbstractPillarTest(TestMinimal): return found['_id'], found - def create_user(self, roles=('subscriber', )): + def create_user(self, user_id='cafef00dc379cf10c4aaceaf', roles=('subscriber', )): with self.app.test_request_context(): users = self.app.data.driver.db['users'] assert isinstance(users, pymongo.collection.Collection) result = users.insert_one({ - '_id': ObjectId('cafef00dc379cf10c4aaceaf'), + '_id': ObjectId(user_id), '_updated': datetime.datetime(2016, 4, 15, 13, 15, 11, tzinfo=tz_util.utc), '_created': datetime.datetime(2016, 4, 15, 13, 15, 11, tzinfo=tz_util.utc), 'username': 'tester',