diff --git a/oshipka/persistance/__init__.py b/oshipka/persistance/__init__.py index 63b5191..09aac49 100644 --- a/oshipka/persistance/__init__.py +++ b/oshipka/persistance/__init__.py @@ -34,6 +34,17 @@ db = SQLAlchemy() migrate = Migrate() +SHARING_TYPE_TYPES_TYPE_PUBLIC = "PUBLIC" +SHARING_TYPE_TYPES_TYPE_AUTHZ = "AUTHZ" +SHARING_TYPE_TYPES_TYPE_AUTHN = "AUTHN" + +SHARING_TYPE_TYPES = [ + (SHARING_TYPE_TYPES_TYPE_PUBLIC, u'public'), + (SHARING_TYPE_TYPES_TYPE_AUTHZ, u'all logged in'), + (SHARING_TYPE_TYPES_TYPE_AUTHN, u'some authenticated users'), +] + + class Ownable(object): @declared_attr def user_id(self): @@ -236,8 +247,28 @@ def register_filters(app): def bool_filter(v): return bool(v) + def check_instance_perm(model_view, verb, instance): + model_acl = model_view.model_acl + # Anonymous user -> check public ACL + if current_user.is_anonymous: + instance_acl = model_acl.query.filter_by(user=current_user, instance=instance, + acl_type=SHARING_TYPE_TYPES_TYPE_PUBLIC).first() + else: + # Logged in user -> find (user, instance) pair + instance_acl = model_acl.query.filter_by(user=current_user, instance=instance, + acl_type=SHARING_TYPE_TYPES_TYPE_AUTHZ).first() + if not instance_acl: + # If not (user, instance) pair -> check authN ACL + instance_acl = model_acl.query.filter_by(user=current_user, instance=instance, + acl_type=SHARING_TYPE_TYPES_TYPE_AUTHN).first() + column = verb.replace('.', '__') + return getattr(instance_acl, column) + def has_permission(model, verb, instance=None): - acl = MODEL_VIEWS.get(model, {}).model.acls.get(verb) + model_view = MODEL_VIEWS.get(model, {}) + if '.' in verb: + return check_instance_perm(model_view, verb, instance) + acl = model_view.model.model_acls.get(verb) # Anonymous user -> do we require AuthN? if current_user.is_anonymous: return not acl.get('authn') @@ -248,8 +279,6 @@ def register_filters(app): return True # One role is enough to grant permission for role in roles: - if role in ['owner']: - return instance.user == current_user if current_user.has_role(role): return True return False diff --git a/oshipka/webapp/views.py b/oshipka/webapp/views.py index a76da8b..f1dee06 100644 --- a/oshipka/webapp/views.py +++ b/oshipka/webapp/views.py @@ -7,10 +7,12 @@ from functools import wraps import inflect from flask import flash, render_template, redirect, request, url_for, jsonify +from flask_login import current_user from flask_security import login_required, roles_required from sqlalchemy_filters import apply_filters -from oshipka.persistance import db, filter_m_n, update_m_ns +from oshipka.persistance import db, filter_m_n, update_m_ns, SHARING_TYPE_TYPES_TYPE_PUBLIC, \ + SHARING_TYPE_TYPES_TYPE_AUTHZ, SHARING_TYPE_TYPES_TYPE_AUTHN from oshipka.util.strings import camel_case_to_snake_case from config import MEDIA_DIR @@ -128,6 +130,13 @@ def default_create_func(vc): vc.instances = [instance] default_update_func(vc) + instance_public_acl = vc.model_view.model_acl(user=current_user, instance=instance, acl_type=SHARING_TYPE_TYPES_TYPE_PUBLIC) + instance_authn_acl = vc.model_view.model_acl(user=current_user, instance=instance, acl_type=SHARING_TYPE_TYPES_TYPE_AUTHN) + instance_authz_acl = vc.model_view.model_acl(user=current_user, instance=instance, acl_type=SHARING_TYPE_TYPES_TYPE_AUTHZ) + db.session.add(instance_public_acl) + db.session.add(instance_authn_acl) + db.session.add(instance_authz_acl) + def default_delete_func(vc): instance = vc.instances[0] @@ -248,9 +257,10 @@ def create_view(model_view, view_context_kwargs, is_login_required=False, the_ro class ModelView(object): - def __init__(self, app, model): + def __init__(self, app, model, model_acl): self.app = app self.model = model + self.model_acl = model_acl p = inflect.engine() if hasattr(model, "__name__"): diff --git a/vm_gen/templates/_model_py b/vm_gen/templates/_model_py index 963e006..34040dd 100644 --- a/vm_gen/templates/_model_py +++ b/vm_gen/templates/_model_py @@ -7,9 +7,10 @@ class [[ name ]](db.Model, ModelController[% for inherit in interits %], [[ inhe [%- include "_model_searchable_header_py" %] _file_columns = [ [%- for column in columns %][%- if column.is_file %]"[[ column.name ]]", [%- endif %] [%- endfor %] ] - acls = [[ acls ]] - [%- for column in columns %] + model_acls = [[ acls ]] + + [% for column in columns %] [%- if column._type == 'relationship' %] [%- include "_relationship_py" %] [%- else %] diff --git a/vm_gen/templates/html/_get.html b/vm_gen/templates/html/_get.html index 1758046..8971962 100644 --- a/vm_gen/templates/html/_get.html +++ b/vm_gen/templates/html/_get.html @@ -1,4 +1,5 @@ [%- for column in columns %] +{% if has_permission("[[ name|camel_to_snake ]]", "[[ column.name ]].read", instance) %} {% if "[[ column.name ]]" not in skip_list %}