Merge pull request #2375 from alphagov/user-folder-permissions

Add user folder permissions
This commit is contained in:
Katie Smith
2019-02-27 15:46:53 +00:00
committed by GitHub
10 changed files with 332 additions and 62 deletions

View File

@@ -0,0 +1,13 @@
from app import db
from app.dao.dao_utils import transactional
from app.models import ServiceUser
def dao_get_service_user(user_id, service_id):
return ServiceUser.query.filter_by(user_id=user_id, service_id=service_id).one()
@transactional
def dao_update_service_user(service_user):
db.session.add(service_user)

View File

@@ -12,6 +12,7 @@ from app.dao.dao_utils import (
version_class
)
from app.dao.service_sms_sender_dao import insert_service_sms_sender
from app.dao.service_user_dao import dao_get_service_user
from app.models import (
AnnualBilling,
ApiKey,
@@ -201,8 +202,13 @@ def dao_remove_user_from_service(service, user):
try:
from app.dao.permissions_dao import permission_dao
permission_dao.remove_user_service_permissions(user, service)
service_user = dao_get_service_user(user.id, service.id)
service_user.folders = []
service.users.remove(user)
db.session.add(service)
db.session.add_all([service, service_user])
except Exception as e:
db.session.rollback()
raise e
@@ -361,7 +367,7 @@ def dao_resume_service(service_id):
def dao_fetch_active_users_for_service(service_id):
query = User.query.filter(
User.user_to_service.any(id=service_id),
User.services.any(id=service_id),
User.state == 'active'
)

View File

@@ -119,7 +119,7 @@ class User(db.Model):
services = db.relationship(
'Service',
secondary='user_to_service',
backref='user_to_service')
backref='users')
organisations = db.relationship(
'Organisation',
secondary='user_to_organisation',
@@ -169,13 +169,16 @@ class User(db.Model):
}
user_to_service = db.Table(
'user_to_service',
db.Model.metadata,
db.Column('user_id', UUID(as_uuid=True), db.ForeignKey('users.id')),
db.Column('service_id', UUID(as_uuid=True), db.ForeignKey('services.id')),
UniqueConstraint('user_id', 'service_id', name='uix_user_to_service')
)
class ServiceUser(db.Model):
__tablename__ = 'user_to_service'
user_id = db.Column(UUID(as_uuid=True), db.ForeignKey('users.id'), primary_key=True)
service_id = db.Column(UUID(as_uuid=True), db.ForeignKey('services.id'), primary_key=True)
__table_args__ = (
UniqueConstraint('user_id', 'service_id', name='uix_user_to_service'),
)
user = db.relationship('User')
user_to_organisation = db.Table(
@@ -187,6 +190,17 @@ user_to_organisation = db.Table(
)
user_folder_permissions = db.Table(
'user_folder_permissions',
db.Model.metadata,
db.Column('user_id', UUID(as_uuid=True), primary_key=True),
db.Column('template_folder_id', UUID(as_uuid=True), db.ForeignKey('template_folder.id'), primary_key=True),
db.Column('service_id', UUID(as_uuid=True), primary_key=True),
db.ForeignKeyConstraint(['user_id', 'service_id'], ['user_to_service.user_id', 'user_to_service.service_id']),
db.ForeignKeyConstraint(['template_folder_id', 'service_id'], ['template_folder.id', 'template_folder.service_id'])
)
BRANDING_GOVUK = 'govuk' # Deprecated outside migrations
BRANDING_ORG = 'org'
BRANDING_BOTH = 'both'
@@ -344,10 +358,6 @@ class Service(db.Model, Versioned):
onupdate=datetime.datetime.utcnow)
active = db.Column(db.Boolean, index=False, unique=False, nullable=False, default=True)
message_limit = db.Column(db.BigInteger, index=False, unique=False, nullable=False)
users = db.relationship(
'User',
secondary=user_to_service,
backref=db.backref('user_to_service', lazy='dynamic'))
restricted = db.Column(db.Boolean, index=False, unique=False, nullable=False)
research_mode = db.Column(db.Boolean, index=False, unique=False, nullable=False, default=False)
email_from = db.Column(db.Text, index=False, unique=True, nullable=False)
@@ -727,13 +737,25 @@ class TemplateFolder(db.Model):
service = db.relationship('Service', backref='all_template_folders')
parent = db.relationship('TemplateFolder', remote_side=[id], backref='subfolders')
users = db.relationship(
'ServiceUser',
uselist=True,
backref=db.backref('folders', foreign_keys='user_folder_permissions.c.template_folder_id'),
secondary='user_folder_permissions',
primaryjoin='TemplateFolder.id == user_folder_permissions.c.template_folder_id'
)
__table_args__ = (
UniqueConstraint('id', 'service_id', name='ix_id_service_id'), {}
)
def serialize(self):
return {
'id': self.id,
'name': self.name,
'parent_id': self.parent_id,
'service_id': self.service_id
'service_id': self.service_id,
'users_with_permission': self.get_users_with_permission()
}
def is_parent_of(self, other):
@@ -743,6 +765,12 @@ class TemplateFolder(db.Model):
other = other.parent
return False
def get_users_with_permission(self):
service_users = self.users
users_with_permission = [str(service_user.user_id) for service_user in service_users]
return users_with_permission
template_folder_map = db.Table(
'template_folder_map',

View File

@@ -24,9 +24,11 @@ from app.dao.users_dao import (
get_user_and_accounts
)
from app.dao.permissions_dao import permission_dao
from app.dao.service_user_dao import dao_get_service_user, dao_update_service_user
from app.dao.services_dao import dao_fetch_service_by_id
from app.dao.templates_dao import dao_get_template_by_id
from app.models import KEY_TYPE_NORMAL, Service, SMS_TYPE, EMAIL_TYPE
from app.dao.template_folder_dao import dao_get_template_folder_by_id_and_service_id
from app.models import KEY_TYPE_NORMAL, Permission, Service, SMS_TYPE, EMAIL_TYPE
from app.notifications.process_notifications import (
persist_notification,
send_notification_to_queue
@@ -35,7 +37,6 @@ from app.schemas import (
email_data_request_schema,
partial_email_data_request_schema,
create_user_schema,
permission_schema,
user_update_schema_load_json,
user_update_password_schema_load_json
)
@@ -48,6 +49,7 @@ from app.user.users_schema import (
post_verify_code_schema,
post_send_user_sms_code_schema,
post_send_user_email_code_schema,
post_set_permissions_schema,
)
from app.schema_validation import validate
@@ -371,21 +373,29 @@ def get_user(user_id=None):
def set_permissions(user_id, service_id):
# TODO fix security hole, how do we verify that the user
# who is making this request has permission to make the request.
user = get_user_by_id(user_id=user_id)
service_user = dao_get_service_user(user_id, service_id)
user = service_user.user
service = dao_fetch_service_by_id(service_id=service_id)
data = request.get_json()
if 'permissions' in data:
user_permissions = data['permissions']
else:
user_permissions = data
validate(data, post_set_permissions_schema)
permissions, errors = permission_schema.load(user_permissions, many=True)
permission_list = [
Permission(service_id=service_id, user_id=user_id, permission=p['permission'])
for p in data['permissions']
]
permission_dao.set_user_service_permission(user, service, permission_list, _commit=True, replace=True)
if 'folder_permissions' in data:
folders = [
dao_get_template_folder_by_id_and_service_id(folder_id, service_id)
for folder_id in data['folder_permissions']
]
service_user.folders = folders
dao_update_service_user(service_user)
for p in permissions:
p.user = user
p.service = service
permission_dao.set_user_service_permission(user, service, permissions, _commit=True, replace=True)
return jsonify({}), 204

View File

@@ -40,3 +40,16 @@ post_send_user_sms_code_schema = {
'required': [],
'additionalProperties': False
}
post_set_permissions_schema = {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "POST schema for setting user permissions",
"type": "object",
"properties": {
"permissions": {"type": "array", "items": {"type": "object"}},
"folder_permissions": {"type": "array", "items": {"type": "string"}}
},
"required": ["permissions"],
"additionalProperties": False
}