Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -354,9 +354,6 @@ app.on_inserted_nodes += after_inserting_nodes
|
|||||||
app.on_fetched_item_projects += before_returning_item_permissions
|
app.on_fetched_item_projects += before_returning_item_permissions
|
||||||
app.on_fetched_item_projects += project_node_type_has_method
|
app.on_fetched_item_projects += project_node_type_has_method
|
||||||
app.on_fetched_resource_projects += before_returning_resource_permissions
|
app.on_fetched_resource_projects += before_returning_resource_permissions
|
||||||
# Projects hooks
|
|
||||||
app.on_insert_projects += before_inserting_projects
|
|
||||||
app.on_inserted_projects += after_inserting_projects
|
|
||||||
|
|
||||||
|
|
||||||
def post_GET_user(request, payload):
|
def post_GET_user(request, payload):
|
||||||
@@ -389,6 +386,8 @@ file_storage.setup_app(app, url_prefix='/storage')
|
|||||||
# The encoding module (receive notification and report progress)
|
# The encoding module (receive notification and report progress)
|
||||||
from modules.encoding import encoding
|
from modules.encoding import encoding
|
||||||
from modules.blender_id import blender_id
|
from modules.blender_id import blender_id
|
||||||
|
from modules import projects
|
||||||
|
|
||||||
app.register_blueprint(encoding, url_prefix='/encoding')
|
app.register_blueprint(encoding, url_prefix='/encoding')
|
||||||
app.register_blueprint(blender_id, url_prefix='/blender_id')
|
app.register_blueprint(blender_id, url_prefix='/blender_id')
|
||||||
|
projects.setup_app(app, url_prefix='/p')
|
||||||
|
@@ -1,17 +1,23 @@
|
|||||||
import logging
|
import logging
|
||||||
from flask import g
|
import json
|
||||||
from flask import abort
|
|
||||||
from eve.methods.put import put_internal
|
|
||||||
from eve.methods.post import post_internal
|
from eve.methods.post import post_internal
|
||||||
from application import app
|
from eve.methods.put import put_internal
|
||||||
from application.utils import remove_private_keys
|
from eve.methods.patch import patch_internal
|
||||||
|
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.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.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
|
||||||
from manage_extra.node_types.group import node_type_group
|
from manage_extra.node_types.group import node_type_group
|
||||||
from manage_extra.node_types.page import node_type_page
|
from manage_extra.node_types.page import node_type_page
|
||||||
from manage_extra.node_types.comment import node_type_comment
|
from manage_extra.node_types.post import node_type_post
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
blueprint = Blueprint('projects', __name__)
|
||||||
|
|
||||||
|
|
||||||
def before_inserting_projects(items):
|
def before_inserting_projects(items):
|
||||||
@@ -20,6 +26,11 @@ def before_inserting_projects(items):
|
|||||||
|
|
||||||
:param items: List of project docs that have been inserted (normally one)
|
: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:
|
for item in items:
|
||||||
item.pop('url', None)
|
item.pop('url', None)
|
||||||
|
|
||||||
@@ -34,52 +45,143 @@ def after_inserting_projects(items):
|
|||||||
:param items: List of project docs that have been inserted (normally one)
|
:param items: List of project docs that have been inserted (normally one)
|
||||||
"""
|
"""
|
||||||
current_user = g.current_user
|
current_user = g.current_user
|
||||||
users_collection = app.data.driver.db['users']
|
users_collection = current_app.data.driver.db['users']
|
||||||
user = users_collection.find_one({'_id': current_user['user_id']})
|
user = users_collection.find_one(current_user['user_id'])
|
||||||
|
|
||||||
for item in items:
|
for item in items:
|
||||||
# Create a project specific group (with name matching the project id)
|
after_inserting_project(item, user)
|
||||||
project_group = dict(name=str(item['_id']))
|
|
||||||
group = post_internal('groups', project_group)
|
|
||||||
# If Group creation failed, stop
|
def after_inserting_project(project, db_user):
|
||||||
# TODO: undo project creation
|
project_id = project['_id']
|
||||||
if group[3] != 201:
|
user_id = db_user['_id']
|
||||||
abort(group[3])
|
|
||||||
else:
|
# Create a project-specific admin group (with name matching the project id)
|
||||||
group = group[0]
|
result, _, _, status = post_internal('groups', {'name': str(project_id)})
|
||||||
|
if status != 201:
|
||||||
|
log.error('Unable to create admin group for new project %s: %s',
|
||||||
|
project_id, result)
|
||||||
|
return abort_with_error(status)
|
||||||
|
|
||||||
|
admin_group_id = result['_id']
|
||||||
|
log.info('Created admin group %s for project %s', admin_group_id, project_id)
|
||||||
|
|
||||||
# Assign the current user to the group
|
# Assign the current user to the group
|
||||||
if 'groups' in user:
|
db_user.setdefault('groups', []).append(admin_group_id)
|
||||||
user['groups'].append(group['_id'])
|
|
||||||
else:
|
result, _, _, status = patch_internal('users', {'groups': db_user['groups']}, _id=user_id)
|
||||||
user['groups'] = [group['_id']]
|
if status != 200:
|
||||||
put_internal('users', remove_private_keys(user), _id=user['_id'])
|
log.error('Unable to add user %s as member of admin group %s for new project %s: %s',
|
||||||
|
user_id, admin_group_id, project_id, result)
|
||||||
|
return abort_with_error(status)
|
||||||
|
log.debug('Made user %s member of group %s', user_id, admin_group_id)
|
||||||
|
|
||||||
# Assign the group to the project with admin rights
|
# Assign the group to the project with admin rights
|
||||||
permissions = dict(
|
permissions = {
|
||||||
world=['GET'],
|
'world': ['GET'],
|
||||||
users=[],
|
'users': [],
|
||||||
groups=[
|
'groups': [
|
||||||
dict(group=group['_id'],
|
{'group': admin_group_id,
|
||||||
methods=['GET', 'PUT', 'POST'])
|
'methods': ['GET', 'PUT', 'POST']},
|
||||||
]
|
]
|
||||||
)
|
}
|
||||||
|
|
||||||
# Assign permissions to the project itself, as well as to the node_types
|
# Assign permissions to the project itself, as well as to the node_types
|
||||||
item['permissions'] = permissions
|
project['permissions'] = permissions
|
||||||
node_type_asset['permissions'] = permissions
|
node_type_asset['permissions'] = permissions
|
||||||
node_type_group['permissions'] = permissions
|
node_type_group['permissions'] = permissions
|
||||||
node_type_page['permissions'] = permissions
|
node_type_page['permissions'] = permissions
|
||||||
node_type_comment['permissions'] = permissions
|
node_type_comment['permissions'] = permissions
|
||||||
|
|
||||||
# Assign the basic 'group', 'asset' and 'page' node_types
|
# Assign the basic 'group', 'asset' and 'page' node_types
|
||||||
item['node_types'] = [
|
project['node_types'] = [
|
||||||
node_type_group,
|
node_type_group,
|
||||||
node_type_asset,
|
node_type_asset,
|
||||||
node_type_page,
|
node_type_page,
|
||||||
node_type_comment]
|
node_type_comment]
|
||||||
# TODO: Depending on user role or status, assign the url attribute
|
|
||||||
|
# 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)
|
# Initialize storage page (defaults to GCS)
|
||||||
gcs_storage = GoogleCloudStorageBucket(str(item['_id']))
|
if current_app.config.get('TESTING'):
|
||||||
|
log.warning('Not creating Google Cloud Storage bucket while running unit tests!')
|
||||||
|
else:
|
||||||
|
gcs_storage = GoogleCloudStorageBucket(str(project_id))
|
||||||
if gcs_storage.bucket.exists():
|
if gcs_storage.bucket.exists():
|
||||||
log.debug("Created CGS bucket {0}".format(item['_id']))
|
log.info('Created CGS instance for project %s', project_id)
|
||||||
# Assign a url based on the project id
|
else:
|
||||||
item['url'] = "p-{}".format(item['_id'])
|
log.warning('Unable to create CGS instance for project %s', project_id)
|
||||||
|
|
||||||
# Commit the changes
|
# Commit the changes
|
||||||
put_internal('projects', remove_private_keys(item), _id=item['_id'])
|
result, _, _, status = put_internal('projects', remove_private_keys(project), _id=project_id)
|
||||||
|
if status != 200:
|
||||||
|
log.warning('Unable to update project %s: %s', project_id, result)
|
||||||
|
abort_with_error(status)
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
# Create the project itself, the rest will be done by the after-insert hook.
|
||||||
|
project = {'description': '',
|
||||||
|
'name': project_name,
|
||||||
|
'node_types': [node_type_blog,
|
||||||
|
node_type_post,
|
||||||
|
node_type_comment],
|
||||||
|
'status': 'published',
|
||||||
|
'user': user_id,
|
||||||
|
'is_private': True,
|
||||||
|
'permissions': {},
|
||||||
|
'url': '',
|
||||||
|
'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:
|
||||||
|
log.error('Unable to create project "%s": %s', project_name, result)
|
||||||
|
return abort_with_error(status)
|
||||||
|
project.update(result)
|
||||||
|
|
||||||
|
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']})
|
||||||
|
return resp
|
||||||
|
|
||||||
|
|
||||||
|
def abort_with_error(status):
|
||||||
|
"""Aborts with the given status, or 500 if the status doesn't indicate an error.
|
||||||
|
|
||||||
|
If the status is < 400, status 500 is used instead.
|
||||||
|
"""
|
||||||
|
|
||||||
|
abort(status if status // 100 >= 4 else 500)
|
||||||
|
|
||||||
|
|
||||||
|
def setup_app(app, url_prefix):
|
||||||
|
app.on_insert_projects += before_inserting_projects
|
||||||
|
app.on_inserted_projects += after_inserting_projects
|
||||||
|
app.register_blueprint(blueprint, url_prefix=url_prefix)
|
||||||
|
@@ -25,8 +25,6 @@ class PillarJSONEncoder(json.JSONEncoder):
|
|||||||
|
|
||||||
def default(self, obj):
|
def default(self, obj):
|
||||||
if isinstance(obj, datetime.datetime):
|
if isinstance(obj, datetime.datetime):
|
||||||
if obj.tzinfo is None:
|
|
||||||
raise ValueError('All datetime.datetime objects should be timezone-aware.')
|
|
||||||
return obj.strftime(RFC1123_DATE_FORMAT)
|
return obj.strftime(RFC1123_DATE_FORMAT)
|
||||||
|
|
||||||
if isinstance(obj, bson.ObjectId):
|
if isinstance(obj, bson.ObjectId):
|
||||||
|
@@ -63,7 +63,8 @@ def validate_token():
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
g.current_user = {'user_id': db_user['_id'],
|
g.current_user = {'user_id': db_user['_id'],
|
||||||
'groups': db_user['groups']}
|
'groups': db_user['groups'],
|
||||||
|
'roles': set(db_user.get('roles', []))}
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
|
import functools
|
||||||
from flask import g
|
from flask import g
|
||||||
from flask import abort
|
from flask import abort
|
||||||
from application import app
|
from application import app
|
||||||
@@ -80,3 +81,38 @@ def check_permissions(resource, method, append_allowed_methods=False):
|
|||||||
return
|
return
|
||||||
|
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
|
def require_login(require_roles=set()):
|
||||||
|
"""Decorator that enforces users to authenticate.
|
||||||
|
|
||||||
|
Optionally only allows access to users with a certain role./
|
||||||
|
"""
|
||||||
|
|
||||||
|
def decorator(func):
|
||||||
|
@functools.wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
current_user = g.get('current_user')
|
||||||
|
|
||||||
|
if current_user is None:
|
||||||
|
log.warning('Unauthenticated acces to %s attempted.', func)
|
||||||
|
abort(403)
|
||||||
|
|
||||||
|
if require_roles and not require_roles.intersection(set(current_user['roles'])):
|
||||||
|
log.warning('User %s is authenticated, but does not have any required role %s to '
|
||||||
|
'access %s', current_user['user_id'], require_roles, func)
|
||||||
|
abort(403)
|
||||||
|
|
||||||
|
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']
|
||||||
|
@@ -67,13 +67,14 @@ def put_item(collection, item):
|
|||||||
|
|
||||||
|
|
||||||
@manager.command
|
@manager.command
|
||||||
def setup_db():
|
def setup_db(admin_email):
|
||||||
"""Setup the database
|
"""Setup the database
|
||||||
- Create admin, subscriber and demo Group collection
|
- Create admin, subscriber and demo Group collection
|
||||||
- Create admin user (must use valid blender-id credentials)
|
- Create admin user (must use valid blender-id credentials)
|
||||||
- Create one project
|
- Create one project
|
||||||
"""
|
"""
|
||||||
# groups_collection = app.data.driver.db['groups']
|
|
||||||
|
# Create default groups
|
||||||
groups_list = []
|
groups_list = []
|
||||||
for group in ['admin', 'subscriber', 'demo']:
|
for group in ['admin', 'subscriber', 'demo']:
|
||||||
g = {'name': group}
|
g = {'name': group}
|
||||||
@@ -81,58 +82,31 @@ def setup_db():
|
|||||||
groups_list.append(g[0]['_id'])
|
groups_list.append(g[0]['_id'])
|
||||||
print("Creating group {0}".format(group))
|
print("Creating group {0}".format(group))
|
||||||
|
|
||||||
while True:
|
# Create admin user
|
||||||
admin_username = raw_input('Admin email:')
|
user = {'username': admin_email,
|
||||||
if len(admin_username) < 1:
|
'groups': groups_list,
|
||||||
print ("Username is too short")
|
'roles': ['admin', 'subscriber', 'demo'],
|
||||||
else:
|
'settings': {'email_communications': 1},
|
||||||
break
|
'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(
|
# Create a default project by faking a POST request.
|
||||||
username=admin_username,
|
with app.test_request_context(data={'project_name': u'Default Project'}):
|
||||||
groups=groups_list,
|
from flask import g
|
||||||
roles=['admin', 'subscriber', 'demo'],
|
from application.modules import projects
|
||||||
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']))
|
|
||||||
|
|
||||||
# TODO: Create a default project
|
g.current_user = {'user_id': user['_id'],
|
||||||
default_permissions = _default_permissions()
|
'groups': user['groups'],
|
||||||
|
'roles': set(user['roles'])}
|
||||||
|
|
||||||
node_type_blog['permissions'] = default_permissions
|
projects.create_project(overrides={'url': 'default-project',
|
||||||
node_type_post['permissions'] = default_permissions
|
'is_private': False})
|
||||||
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")
|
|
||||||
|
|
||||||
|
|
||||||
def _default_permissions():
|
def _default_permissions():
|
||||||
|
@@ -560,28 +560,6 @@ projects_schema = {
|
|||||||
'embeddable': True
|
'embeddable': True
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'owners': {
|
|
||||||
'type': 'dict',
|
|
||||||
'schema': {
|
|
||||||
'users': {
|
|
||||||
'type': 'list',
|
|
||||||
'schema': {
|
|
||||||
'type': 'objectid',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'groups': {
|
|
||||||
'type': 'list',
|
|
||||||
'schema': {
|
|
||||||
'type': 'objectid',
|
|
||||||
'data_relation': {
|
|
||||||
'resource': 'groups',
|
|
||||||
'field': '_id',
|
|
||||||
'embeddable': True
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'status': {
|
'status': {
|
||||||
'type': 'string',
|
'type': 'string',
|
||||||
'allowed': [
|
'allowed': [
|
||||||
|
@@ -46,6 +46,7 @@ class AbstractPillarTest(TestMinimal):
|
|||||||
|
|
||||||
from application import app
|
from application import app
|
||||||
|
|
||||||
|
logging.getLogger('').setLevel(logging.DEBUG)
|
||||||
logging.getLogger('application').setLevel(logging.DEBUG)
|
logging.getLogger('application').setLevel(logging.DEBUG)
|
||||||
logging.getLogger('werkzeug').setLevel(logging.DEBUG)
|
logging.getLogger('werkzeug').setLevel(logging.DEBUG)
|
||||||
logging.getLogger('eve').setLevel(logging.DEBUG)
|
logging.getLogger('eve').setLevel(logging.DEBUG)
|
||||||
@@ -95,18 +96,18 @@ class AbstractPillarTest(TestMinimal):
|
|||||||
|
|
||||||
return found['_id'], found
|
return found['_id'], found
|
||||||
|
|
||||||
def create_user(self):
|
def create_user(self, user_id='cafef00dc379cf10c4aaceaf', roles=('subscriber', )):
|
||||||
with self.app.test_request_context():
|
with self.app.test_request_context():
|
||||||
users = self.app.data.driver.db['users']
|
users = self.app.data.driver.db['users']
|
||||||
assert isinstance(users, pymongo.collection.Collection)
|
assert isinstance(users, pymongo.collection.Collection)
|
||||||
|
|
||||||
result = users.insert_one({
|
result = users.insert_one({
|
||||||
'_id': ObjectId('cafef00dc379cf10c4aaceaf'),
|
'_id': ObjectId(user_id),
|
||||||
'_updated': datetime.datetime(2016, 4, 15, 13, 15, 11, tzinfo=tz_util.utc),
|
'_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),
|
'_created': datetime.datetime(2016, 4, 15, 13, 15, 11, tzinfo=tz_util.utc),
|
||||||
'username': 'tester',
|
'username': 'tester',
|
||||||
'groups': [],
|
'groups': [],
|
||||||
'roles': ['subscriber'],
|
'roles': list(roles),
|
||||||
'settings': {'email_communications': 1},
|
'settings': {'email_communications': 1},
|
||||||
'auth': [{'token': '',
|
'auth': [{'token': '',
|
||||||
'user_id': unicode(BLENDER_ID_TEST_USERID),
|
'user_id': unicode(BLENDER_ID_TEST_USERID),
|
||||||
@@ -117,6 +118,17 @@ class AbstractPillarTest(TestMinimal):
|
|||||||
|
|
||||||
return result.inserted_id
|
return result.inserted_id
|
||||||
|
|
||||||
|
def create_valid_auth_token(self, user_id, token='token'):
|
||||||
|
now = datetime.datetime.now(tz_util.utc)
|
||||||
|
future = now + datetime.timedelta(days=1)
|
||||||
|
|
||||||
|
with self.app.test_request_context():
|
||||||
|
from application.utils import authentication as auth
|
||||||
|
|
||||||
|
token_data = auth.store_token(user_id, token, future, None)
|
||||||
|
|
||||||
|
return token_data
|
||||||
|
|
||||||
def mock_blenderid_validate_unhappy(self):
|
def mock_blenderid_validate_unhappy(self):
|
||||||
"""Sets up Responses to mock unhappy validation flow."""
|
"""Sets up Responses to mock unhappy validation flow."""
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import responses
|
import responses
|
||||||
|
import json
|
||||||
from bson import tz_util
|
from bson import tz_util
|
||||||
|
|
||||||
from common_test_class import AbstractPillarTest, TEST_EMAIL_USER, TEST_EMAIL_ADDRESS
|
from common_test_class import AbstractPillarTest, TEST_EMAIL_USER, TEST_EMAIL_ADDRESS
|
||||||
@@ -93,3 +94,67 @@ class AuthenticationTests(AbstractPillarTest):
|
|||||||
found_token = auth.find_token('expired-sub', subclient)
|
found_token = auth.find_token('expired-sub', subclient)
|
||||||
self.assertIsNotNone(found_token)
|
self.assertIsNotNone(found_token)
|
||||||
self.assertNotEqual(token3['_id'], found_token['_id'])
|
self.assertNotEqual(token3['_id'], found_token['_id'])
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_save_own_user(self):
|
||||||
|
"""Tests that a user can't change their own fields."""
|
||||||
|
|
||||||
|
from application.utils import authentication as auth
|
||||||
|
from application.utils import PillarJSONEncoder, remove_private_keys
|
||||||
|
|
||||||
|
user_id = self.create_user(roles=[u'subscriber'])
|
||||||
|
|
||||||
|
now = datetime.datetime.now(tz_util.utc)
|
||||||
|
future = now + datetime.timedelta(days=1)
|
||||||
|
|
||||||
|
with self.app.test_request_context():
|
||||||
|
auth.store_token(user_id, 'nonexpired-main', future, None)
|
||||||
|
|
||||||
|
with self.app.test_request_context(
|
||||||
|
headers={'Authorization': self.make_header('nonexpired-main')}):
|
||||||
|
self.assertTrue(auth.validate_token())
|
||||||
|
|
||||||
|
users = self.app.data.driver.db['users']
|
||||||
|
db_user = users.find_one(user_id)
|
||||||
|
|
||||||
|
updated_fields = remove_private_keys(db_user)
|
||||||
|
updated_fields['roles'] = ['admin', 'subscriber', 'demo'] # Try to elevate our roles.
|
||||||
|
|
||||||
|
# POSTing updated info to a specific user URL is not allowed by Eve.
|
||||||
|
resp = self.client.post('/users/%s' % user_id,
|
||||||
|
data=json.dumps(updated_fields, cls=PillarJSONEncoder),
|
||||||
|
headers={'Authorization': self.make_header('nonexpired-main'),
|
||||||
|
'Content-Type': 'application/json'})
|
||||||
|
self.assertEqual(405, resp.status_code)
|
||||||
|
|
||||||
|
# POSTing with our _id to update shouldn't work either, as POST always creates new users.
|
||||||
|
updated_fields_with_id = dict(_id=user_id, **updated_fields)
|
||||||
|
resp = self.client.post('/users',
|
||||||
|
data=json.dumps(updated_fields_with_id, cls=PillarJSONEncoder),
|
||||||
|
headers={'Authorization': self.make_header('nonexpired-main'),
|
||||||
|
'Content-Type': 'application/json'})
|
||||||
|
self.assertEqual(422, resp.status_code)
|
||||||
|
|
||||||
|
# PUT and PATCH should not be allowed.
|
||||||
|
resp = self.client.put('/users/%s' % user_id,
|
||||||
|
data=json.dumps(updated_fields, cls=PillarJSONEncoder),
|
||||||
|
headers={'Authorization': self.make_header('nonexpired-main'),
|
||||||
|
'Content-Type': 'application/json'})
|
||||||
|
self.assertEqual(403, resp.status_code)
|
||||||
|
|
||||||
|
updated_fields = {'roles': ['admin', 'subscriber', 'demo']}
|
||||||
|
resp = self.client.patch('/users/%s' % user_id,
|
||||||
|
data=json.dumps(updated_fields, cls=PillarJSONEncoder),
|
||||||
|
headers={'Authorization': self.make_header('nonexpired-main'),
|
||||||
|
'Content-Type': 'application/json'})
|
||||||
|
self.assertEqual(403, resp.status_code)
|
||||||
|
|
||||||
|
# After all of this, the roles should be the same.
|
||||||
|
with self.app.test_request_context(
|
||||||
|
headers={'Authorization': self.make_header('nonexpired-main')}):
|
||||||
|
self.assertTrue(auth.validate_token())
|
||||||
|
|
||||||
|
users = self.app.data.driver.db['users']
|
||||||
|
db_user = users.find_one(user_id)
|
||||||
|
|
||||||
|
self.assertEqual([u'subscriber'], db_user['roles'])
|
||||||
|
65
tests/test_project_management.py
Normal file
65
tests/test_project_management.py
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
|
"""Unit tests for creating and editing projects_blueprint."""
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
import responses
|
||||||
|
from bson import ObjectId
|
||||||
|
|
||||||
|
from common_test_class import AbstractPillarTest
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectCreationTest(AbstractPillarTest):
|
||||||
|
@responses.activate
|
||||||
|
def test_project_creation_wrong_role(self):
|
||||||
|
user_id = self.create_user(roles=[u'whatever'])
|
||||||
|
self.create_valid_auth_token(user_id, 'token')
|
||||||
|
|
||||||
|
resp = self.client.post('/p/create',
|
||||||
|
headers={'Authorization': self.make_header('token')},
|
||||||
|
data={'project_name': u'Prøject El Niño'})
|
||||||
|
|
||||||
|
self.assertEqual(403, resp.status_code)
|
||||||
|
|
||||||
|
# Test that the project wasn't created.
|
||||||
|
with self.app.test_request_context():
|
||||||
|
projects = self.app.data.driver.db['projects']
|
||||||
|
self.assertEqual(0, len(list(projects.find())))
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_project_creation_good_role(self):
|
||||||
|
user_id = self.create_user(roles=[u'subscriber'])
|
||||||
|
self.create_valid_auth_token(user_id, 'token')
|
||||||
|
|
||||||
|
resp = self.client.post('/p/create',
|
||||||
|
headers={'Authorization': self.make_header('token')},
|
||||||
|
data={'project_name': u'Prøject El Niñö'})
|
||||||
|
|
||||||
|
self.assertEqual(201, resp.status_code)
|
||||||
|
project = json.loads(resp.data.decode('utf-8'))
|
||||||
|
project_id = project['_id']
|
||||||
|
|
||||||
|
# Test that the Location header contains the location of the project document.
|
||||||
|
self.assertEqual('http://localhost/projects/%s' % project_id,
|
||||||
|
resp.headers['Location'])
|
||||||
|
|
||||||
|
# Check some of the more complex/interesting fields.
|
||||||
|
self.assertEqual(u'Prøject El Niñö', project['name'])
|
||||||
|
self.assertEqual(str(user_id), project['user'])
|
||||||
|
self.assertEqual('p-%s' % project_id, project['url'])
|
||||||
|
self.assertEqual(1, len(project['permissions']['groups']))
|
||||||
|
|
||||||
|
group_id = ObjectId(project['permissions']['groups'][0]['group'])
|
||||||
|
|
||||||
|
# Check that there is a group for the project, and that the user is member of it.
|
||||||
|
with self.app.test_request_context():
|
||||||
|
groups = self.app.data.driver.db['groups']
|
||||||
|
users = self.app.data.driver.db['users']
|
||||||
|
|
||||||
|
group = groups.find_one(group_id)
|
||||||
|
db_user = users.find_one(user_id)
|
||||||
|
|
||||||
|
self.assertEqual(str(project_id), group['name'])
|
||||||
|
self.assertIn(group_id, db_user['groups'])
|
||||||
|
|
Reference in New Issue
Block a user