Exemplo n.º 1
0
def do_login(form):
    email = form.get("email", "").lower()
    password = form.get("password")
    next_url = form.get("next", "")
    res = dict(username=email, email=email, next_url=next_url)

    if not email or not password:
        res["error"] = _("You must provide your email and password.")
        res["code"] = 401
        return res

    try:
        user = User.query.filter(
            sql.func.lower(User.email) == email, User.can_login == True
        ).one()
    except NoResultFound:
        auth_failed.send(unwrap(current_app), email=email)
        res["error"] = _(
            "Sorry, we couldn't find an account for " "email '{email}'."
        ).format(email=email)
        res["code"] = 401
        return res

    if user and not user.authenticate(password):
        auth_failed.send(unwrap(current_app), email=email)
        res["error"] = _("Sorry, wrong password.")
        res["code"] = 401
        return res

    # Login successful
    login_user(user)
    res["user"] = user
    res["email"] = user.email
    return res
Exemplo n.º 2
0
def do_login(form):
    email = form.get("email", "").lower()
    password = form.get("password")
    next_url = form.get("next", "")
    res = {"username": email, "email": email, "next_url": next_url}

    if not email or not password:
        res["error"] = _("You must provide your email and password.")
        res["code"] = 401
        return res

    try:
        user = User.query.filter(
            sql.func.lower(User.email) == email, User.can_login == True
        ).one()
    except NoResultFound:
        auth_failed.send(unwrap(current_app), email=email)
        res["error"] = _(
            "Sorry, we couldn't find an account for " "email '{email}'."
        ).format(email=email)
        res["code"] = 401
        return res

    if user and not user.authenticate(password):
        auth_failed.send(unwrap(current_app), email=email)
        res["error"] = _("Sorry, wrong password.")
        res["code"] = 401
        return res

    # Login successful
    login_user(user)
    res["user"] = user
    res["email"] = user.email
    return res
Exemplo n.º 3
0
 def filter_with_permission(self, user, permission, obj_list, inherit=False):
     user = unwrap(user)
     return [
         obj
         for obj in obj_list
         if self.has_permission(user, permission, obj, inherit)
     ]
Exemplo n.º 4
0
 def filter_with_permission(self, user, permission, obj_list, inherit=False):
     user = unwrap(user)
     return [
         obj
         for obj in obj_list
         if self.has_permission(user, permission, obj, inherit)
     ]
Exemplo n.º 5
0
def create_document(folder, fs):
    check_write_access(folder)

    if isinstance(fs.filename, str):
        name = fs.filename
    else:
        name = str(fs.filename, errors="ignore")

    if not name:
        flash(_("Document name can't be empty."), "error")
        return None

    original_name = name
    name = get_new_filename(folder, name)
    doc = folder.create_document(title=name)
    doc.set_content(fs.read(), fs.content_type)

    if original_name != name:
        # set message after document has been successfully created!
        flash(
            _('"{original}" already present in folder, ' 'renamed "{name}"').format(
                original=original_name, name=name
            ),
            "info",
        )

    # Some unwrapping before posting event
    app = unwrap(current_app)
    community = g.community._model
    activity.send(app, actor=current_user, verb="post", object=doc, target=community)

    return doc
Exemplo n.º 6
0
def create_document(folder, fs):
    check_write_access(folder)

    if isinstance(fs.filename, text_type):
        name = fs.filename
    else:
        name = text_type(fs.filename, errors="ignore")

    if not name:
        flash(_("Document name can't be empty."), "error")
        return None

    original_name = name
    name = get_new_filename(folder, name)
    doc = folder.create_document(title=name)
    doc.set_content(fs.read(), fs.content_type)

    if original_name != name:
        # set message after document has been successfully created!
        flash(
            _('"{original}" already present in folder, '
              'renamed "{name}"').format(original=original_name, name=name),
            "info",
        )

    # Some unwrapping before posting event
    app = unwrap(current_app)
    community = g.community._model
    activity.send(app,
                  actor=current_user,
                  verb="post",
                  object=doc,
                  target=community)

    return doc
Exemplo n.º 7
0
def wizard_saving():
    """Automatically add existing accounts to the current community.

    Create accounts for new emails, add them to the community and send
    them a password reset email.
    """
    community = g.community._model
    existing_accounts = request.form.get("existing_account")
    existing_accounts = json.loads(existing_accounts)
    new_accounts = request.form.get("new_accounts")
    new_accounts = json.loads(new_accounts)

    if not (existing_accounts or new_accounts):
        flash(_("No new members were found"), "warning")
        return redirect(url_for(".members", community_id=g.community.slug))

    if existing_accounts:
        for email, role in existing_accounts.items():
            user = User.query.filter(User.email == email).first()
            community.set_membership(user, role)

            app = unwrap(current_app)
            activity.send(app, actor=user, verb="join", object=community)

            db.session.commit()

    if new_accounts:
        for account in new_accounts:
            email = account["email"]
            first_name = account["first_name"]
            last_name = account["last_name"]
            role = account["role"]

            user = User(
                email=email, last_name=last_name, first_name=first_name, can_login=True
            )
            db.session.add(user)

            community.set_membership(user, role)
            app = unwrap(current_app)
            activity.send(app, actor=user, verb="join", object=community)
            db.session.commit()

            send_reset_password_instructions(user)

    flash(_("New members added successfully"), "success")
    return redirect(url_for(".members", community_id=community.slug))
Exemplo n.º 8
0
def wizard_saving():
    """Automatically add existing accounts to the current community.

    Create accounts for new emails, add them to the community and send
    them a password reset email.
    """
    community = g.community._model
    existing_accounts = request.form.get("existing_account")
    existing_accounts = json.loads(existing_accounts)
    new_accounts = request.form.get("new_accounts")
    new_accounts = json.loads(new_accounts)

    if not (existing_accounts or new_accounts):
        flash(_("No new members were found"), "warning")
        return redirect(url_for(".members", community_id=g.community.slug))

    if existing_accounts:
        for email, role in existing_accounts.items():
            user = User.query.filter(User.email == email).first()
            community.set_membership(user, role)

            app = unwrap(current_app)
            activity.send(app, actor=user, verb="join", object=community)

            db.session.commit()

    if new_accounts:
        for account in new_accounts:
            email = account["email"]
            first_name = account["first_name"]
            last_name = account["last_name"]
            role = account["role"]

            user = User(
                email=email, last_name=last_name, first_name=first_name, can_login=True
            )
            db.session.add(user)

            community.set_membership(user, role)
            app = unwrap(current_app)
            activity.send(app, actor=user, verb="join", object=community)
            db.session.commit()

            send_reset_password_instructions(user)

    flash(_("New members added successfully"), "success")
    return redirect(url_for(".members", community_id=community.slug))
Exemplo n.º 9
0
    def grant_role(self, principal, role, obj=None):
        """Grant `role` to `user` (either globally, if `obj` is None, or on the
        specific `obj`)."""
        assert principal
        principal = unwrap(principal)
        session = object_session(obj) if obj is not None else db.session
        manager = self._current_user_manager(session=session)
        args = {
            "role": role,
            "object": obj,
            "anonymous": False,
            "user": None,
            "group": None,
        }

        if principal is AnonymousRole or (
            hasattr(principal, "is_anonymous") and principal.is_anonymous
        ):
            args["anonymous"] = True
        elif isinstance(principal, User):
            args["user"] = principal
        else:
            args["group"] = principal

        query = session.query(RoleAssignment)
        if query.filter_by(**args).limit(1).count():
            # role already granted, nothing to do
            return

        # same as above but in current, not yet flushed objects in session. We
        # cannot call flush() in grant_role() since this method may be called a
        # great number of times in the same transaction, and sqlalchemy limits
        # to 100 flushes before triggering a warning
        for ra in (
            o
            for models in (session.new, session.dirty)
            for o in models
            if isinstance(o, RoleAssignment)
        ):
            if all(getattr(ra, attr) == val for attr, val in args.items()):
                return

        ra = RoleAssignment(**args)
        session.add(ra)
        audit = SecurityAudit(manager=manager, op=SecurityAudit.GRANT, **args)
        if obj is not None:
            audit.object_id = obj.id
            audit.object_type = obj.entity_type
            object_name = ""
            for attr_name in ("name", "path", "__path_before_delete"):
                if hasattr(obj, attr_name):
                    object_name = getattr(obj, attr_name)
            audit.object_name = object_name

        session.add(audit)
        self._needs_flush()

        if hasattr(principal, "__roles_cache__"):
            del principal.__roles_cache__
Exemplo n.º 10
0
    def grant_role(self, principal, role, obj=None):
        """Grant `role` to `user` (either globally, if `obj` is None, or on the
        specific `obj`)."""
        assert principal
        principal = unwrap(principal)
        session = object_session(obj) if obj is not None else db.session
        manager = self._current_user_manager(session=session)
        args = {
            "role": role,
            "object": obj,
            "anonymous": False,
            "user": None,
            "group": None,
        }

        if principal is AnonymousRole or (
            hasattr(principal, "is_anonymous") and principal.is_anonymous
        ):
            args["anonymous"] = True
        elif isinstance(principal, User):
            args["user"] = principal
        else:
            args["group"] = principal

        query = session.query(RoleAssignment)
        if query.filter_by(**args).limit(1).count():
            # role already granted, nothing to do
            return

        # same as above but in current, not yet flushed objects in session. We
        # cannot call flush() in grant_role() since this method may be called a
        # great number of times in the same transaction, and sqlalchemy limits
        # to 100 flushes before triggering a warning
        for ra in (
            o
            for models in (session.new, session.dirty)
            for o in models
            if isinstance(o, RoleAssignment)
        ):
            if all(getattr(ra, attr) == val for attr, val in args.items()):
                return

        ra = RoleAssignment(**args)
        session.add(ra)
        audit = SecurityAudit(manager=manager, op=SecurityAudit.GRANT, **args)
        if obj is not None:
            audit.object_id = obj.id
            audit.object_type = obj.entity_type
            object_name = ""
            for attr_name in ("name", "path", "__path_before_delete"):
                if hasattr(obj, attr_name):
                    object_name = getattr(obj, attr_name)
            audit.object_name = object_name

        session.add(audit)
        self._needs_flush()

        if hasattr(principal, "__roles_cache__"):
            del principal.__roles_cache__
Exemplo n.º 11
0
def finalize_validate():
    config_file = Path(current_app.instance_path) / "config.py"
    logging_file = Path(current_app.instance_path) / "logging.yml"

    assert not config_file.exists()
    config = cmd_config.DefaultConfig(logging_file="logging.yml")
    config.SQLALCHEMY_DATABASE_URI = session_get("db")["uri"]

    redis_uri = session_get("redis")["uri"]
    config.REDIS_URI = redis_uri
    config.BROKER_URL = redis_uri
    config.CELERY_RESULT_BACKEND = redis_uri

    d = session_get("site_info")
    config.SITE_NAME = d["sitename"]
    config.MAIL_SENDER = d["mailsender"]

    is_production = d["server_mode"] == "production"
    config.PRODUCTION = is_production
    config.DEBUG = not is_production
    config.DEBUG_TB_ENABLED = config.DEBUG
    config.CELERY_ALWAYS_EAGER = not is_production

    cmd_config.write_config(config_file, config)
    cmd_config.maybe_write_logging(logging_file)

    admin_account = session_get("admin_account")
    # create a new app that will be configured with new config,
    # to create database and admin_user
    setup_app = unwrap(current_app)
    app = setup_app.__class__(
        setup_app.import_name,
        static_url_path=setup_app.static_url_path,
        static_folder=setup_app.static_folder,
        template_folder=setup_app.template_folder,
        instance_path=setup_app.instance_path,
    )
    with app.test_request_context("/setup/finalize"):
        app.create_db()
        db_session = db.session()
        admin = User(
            email=admin_account["email"],
            password=admin_account["password"],
            last_name=admin_account["name"],
            first_name=admin_account["firstname"],
            can_login=True,
        )
        db_session.add(admin)
        security = get_service("security")
        security.grant_role(admin, Admin)
        db_session.commit()

    session_clear()

    return render_template("setupwizard/done.html",
                           config_file=config_file,
                           logging_file=logging_file)
Exemplo n.º 12
0
def finalize_validate():
    config_file = Path(current_app.instance_path) / "config.py"
    logging_file = Path(current_app.instance_path) / "logging.yml"

    assert not config_file.exists()
    config = cmd_config.DefaultConfig(logging_file="logging.yml")
    config.SQLALCHEMY_DATABASE_URI = session_get("db")["uri"]

    redis_uri = session_get("redis")["uri"]
    config.REDIS_URI = redis_uri
    config.BROKER_URL = redis_uri
    config.CELERY_RESULT_BACKEND = redis_uri

    d = session_get("site_info")
    config.SITE_NAME = d["sitename"]
    config.MAIL_SENDER = d["mailsender"]

    is_production = d["server_mode"] == "production"
    config.PRODUCTION = is_production
    config.DEBUG = not is_production
    config.DEBUG_TB_ENABLED = config.DEBUG
    config.CELERY_ALWAYS_EAGER = not is_production

    cmd_config.write_config(config_file, config)
    cmd_config.maybe_write_logging(logging_file)

    admin_account = session_get("admin_account")
    # create a new app that will be configured with new config,
    # to create database and admin_user
    setup_app = unwrap(current_app)
    app = setup_app.__class__(
        setup_app.import_name,
        static_url_path=setup_app.static_url_path,
        static_folder=setup_app.static_folder,
        template_folder=setup_app.template_folder,
        instance_path=setup_app.instance_path,
    )
    with app.test_request_context("/setup/finalize"):
        app.create_db()
        db_session = db.session()
        admin = User(
            email=admin_account["email"],
            password=admin_account["password"],
            last_name=admin_account["name"],
            first_name=admin_account["firstname"],
            can_login=True,
        )
        db_session.add(admin)
        security = get_service("security")
        security.grant_role(admin, Admin)
        db_session.commit()

    session_clear()

    return render_template(
        "setupwizard/done.html", config_file=config_file, logging_file=logging_file
    )
Exemplo n.º 13
0
    def flask_app(self) -> Flask:
        if has_app_context():
            return unwrap(flask_current_app)

        self.flask_app_factory = symbol_by_name(self.flask_app_factory)
        app = self.flask_app_factory()

        register_after_fork(app, self._setup_after_fork)
        return app
Exemplo n.º 14
0
 def filter_with_permission(
     self,
     user: User,
     permission: Union[Permission, str],
     obj_list: List[Model],
     inherit=False,
 ):
     user = unwrap(user)
     return [
         obj for obj in obj_list
         if self.has_permission(user, permission, obj, inherit)
     ]
Exemplo n.º 15
0
def members_post():
    community = g.community._model
    action = request.form.get("action")

    user_id = request.form.get("user")
    if not user_id:
        flash(_("You must provide a user."), "error")
        return redirect(url_for(".members", community_id=community.slug))
    user_id = int(user_id)
    user = User.query.get(user_id)

    if action in ("add-user-role", "set-user-role"):
        role = request.form.get("role").lower()

        community.set_membership(user, role)

        if action == "add-user-role":
            app = unwrap(current_app)
            activity.send(app, actor=user, verb="join", object=community)

        db.session.commit()
        return redirect(url_for(".members", community_id=community.slug))

    elif action == "delete":
        membership_id = int(request.form["membership"])
        membership = Membership.query.get(membership_id)
        if membership.user_id != user_id:
            raise InternalServerError()

        community.remove_membership(user)

        app = unwrap(current_app)
        activity.send(app, actor=user, verb="leave", object=community)

        db.session.commit()
        return redirect(url_for(".members", community_id=community.slug))

    else:
        raise BadRequest("Unknown action: {}".format(repr(action)))
Exemplo n.º 16
0
def document_upload(doc_id):
    doc = get_document(doc_id)
    check_write_access(doc)

    fd = request.files["file"]
    doc.set_content(fd.read(), fd.content_type)
    del doc.lock

    self = unwrap(current_app)
    activity.send(self, actor=current_user, verb="update", object=doc)
    db.session.commit()
    flash(_("New version successfully uploaded"), "success")
    return redirect(url_for(doc))
Exemplo n.º 17
0
def document_upload(doc_id):
    doc = get_document(doc_id)
    check_write_access(doc)

    fd = request.files["file"]
    doc.set_content(fd.read(), fd.content_type)
    del doc.lock

    self = unwrap(current_app)
    activity.send(self, actor=current_user, verb="update", object=doc)
    db.session.commit()
    flash(_("New version successfully uploaded"), "success")
    return redirect(url_for(doc))
Exemplo n.º 18
0
def members_post():
    community = g.community._model
    action = request.form.get("action")

    user_id = request.form.get("user")
    if not user_id:
        flash(_("You must provide a user."), "error")
        return redirect(url_for(".members", community_id=community.slug))
    user_id = int(user_id)
    user = User.query.get(user_id)

    if action in ("add-user-role", "set-user-role"):
        role = request.form.get("role").lower()

        community.set_membership(user, role)

        if action == "add-user-role":
            app = unwrap(current_app)
            activity.send(app, actor=user, verb="join", object=community)

        db.session.commit()
        return redirect(url_for(".members", community_id=community.slug))

    elif action == "delete":
        membership_id = int(request.form["membership"])
        membership = Membership.query.get(membership_id)
        if membership.user_id != user_id:
            raise InternalServerError()

        community.remove_membership(user)

        app = unwrap(current_app)
        activity.send(app, actor=user, verb="leave", object=community)

        db.session.commit()
        return redirect(url_for(".members", community_id=community.slug))

    else:
        raise BadRequest("Unknown action: {}".format(repr(action)))
Exemplo n.º 19
0
    def do_access_control(self) -> Union[None, Response]:
        """`before_request` handler to check if user should be redirected to
        login page."""
        from abilian.services import get_service

        if current_app.testing and current_app.config.get("NO_LOGIN"):
            # Special case for tests
            user = User.query.get(0)
            login_user(user, force=True)
            return None

        state = self.app_state
        user = unwrap(current_user)

        # Another special case for tests
        if current_app.testing and getattr(user, "is_admin", False):
            return None

        # pyre-fixme[9]: security has type `SecurityService`; used as `Service`.
        security: SecurityService = get_service("security")
        user_roles = frozenset(security.get_roles(user))
        endpoint = request.endpoint
        blueprint = request.blueprint

        access_controllers: List[Callable] = []
        access_controllers.extend(state.bp_access_controllers.get(None, []))

        if blueprint and blueprint in state.bp_access_controllers:
            access_controllers.extend(state.bp_access_controllers[blueprint])

        if endpoint and endpoint in state.endpoint_access_controllers:
            access_controllers.extend(
                state.endpoint_access_controllers[endpoint])

        for access_controller in reversed(access_controllers):
            verdict = access_controller(user=user, roles=user_roles)
            if verdict is None:
                continue
            elif verdict is True:
                return None
            else:
                if user.is_anonymous:
                    return self.redirect_to_login()
                raise Forbidden()

        # default policy
        if current_app.config.get("PRIVATE_SITE") and user.is_anonymous:
            return self.redirect_to_login()

        return None
Exemplo n.º 20
0
    def ungrant_role(
        self,
        principal: Principal,
        role: Union[Role, str],
        object: Optional[Model] = None,
    ) -> None:
        """Ungrant `role` to `user` (either globally, if `object` is None, or
        on the specific `object`)."""

        assert principal
        principal = unwrap(principal)
        session = object_session(object) if object is not None else db.session
        manager = self._current_user_manager(session=session)

        args = {
            "role": role,
            "object": object,
            "anonymous": False,
            "user": None,
            "group": None,
        }
        query = session.query(RoleAssignment)
        query = query.filter(
            RoleAssignment.role == role, RoleAssignment.object == object
        )

        if principal is AnonymousRole or (
            hasattr(principal, "is_anonymous") and principal.is_anonymous
        ):
            args["anonymous"] = True
            query.filter(
                RoleAssignment.anonymous == False,
                RoleAssignment.user == None,
                RoleAssignment.group == None,
            )

        elif isinstance(principal, User):
            args["user"] = principal
            query = query.filter(RoleAssignment.user == principal)
        else:
            args["group"] = principal
            query = query.filter(RoleAssignment.group == principal)

        ra = query.one()
        session.delete(ra)
        audit = SecurityAudit(manager=manager, op=SecurityAudit.REVOKE, **args)
        session.add(audit)
        self._needs_flush()
        self._clear_role_cache(principal)
Exemplo n.º 21
0
    def flask_app(self):
        if has_app_context():
            return unwrap(flask_current_app)

        self.flask_app_factory = symbol_by_name(self.flask_app_factory)
        app = self.flask_app_factory()

        if "sentry" in app.extensions:
            from raven.contrib.celery import register_signal, register_logger_signal

            client = app.extensions["sentry"].client
            client.tags["process_type"] = "celery task"
            register_signal(client)
            register_logger_signal(client)

        register_after_fork(app, self._setup_after_fork)
        return app
Exemplo n.º 22
0
def page_delete():
    title = request.form["title"].strip()
    try:
        page = get_page_by_title(title)
    except NoResultFound:
        flash(_("This page doesn't exist"), "error")
        return redirect(url_for(".index", community_id=g.community.slug))

    db.session.delete(page)

    app = unwrap(current_app)
    community = g.community._model
    activity.send(app, actor=current_user, verb="delete", object=page, target=community)

    db.session.commit()
    flash(_("Page %(title)s deleted.", title=title))
    return redirect(url_for(".index", community_id=g.community.slug))
Exemplo n.º 23
0
    def do_access_control(self):
        """`before_request` handler to check if user should be redirected to
        login page."""
        from abilian.services import get_service

        if current_app.testing and current_app.config.get("NO_LOGIN"):
            # Special case for tests
            user = User.query.get(0)
            login_user(user, force=True)
            return

        state = self.app_state
        user = unwrap(current_user)

        # Another special case for tests
        if current_app.testing and getattr(user, "is_admin", False):
            return

        security = get_service("security")
        user_roles = frozenset(security.get_roles(user))
        endpoint = request.endpoint
        blueprint = request.blueprint

        access_controllers = []
        access_controllers.extend(state.bp_access_controllers.get(None, []))

        if blueprint and blueprint in state.bp_access_controllers:
            access_controllers.extend(state.bp_access_controllers[blueprint])

        if endpoint and endpoint in state.endpoint_access_controllers:
            access_controllers.extend(state.endpoint_access_controllers[endpoint])

        for access_controller in reversed(access_controllers):
            verdict = access_controller(user=user, roles=user_roles)
            if verdict is None:
                continue
            elif verdict is True:
                return
            else:
                if user.is_anonymous:
                    return self.redirect_to_login()
                raise Forbidden()

        # default policy
        if current_app.config.get("PRIVATE_SITE") and user.is_anonymous:
            return self.redirect_to_login()
Exemplo n.º 24
0
    def flask_app(self):
        if has_app_context():
            return unwrap(flask_current_app)

        self.flask_app_factory = symbol_by_name(self.flask_app_factory)
        app = self.flask_app_factory()

        if "sentry" in app.extensions:
            from raven.contrib.celery import register_signal, register_logger_signal

            client = app.extensions["sentry"].client
            client.tags["process_type"] = "celery task"
            register_signal(client)
            register_logger_signal(client)

        register_after_fork(app, self._setup_after_fork)
        return app
Exemplo n.º 25
0
    def before_request(self):
        req = unwrap(request)
        failed = req.csrf_failed

        if not failed:
            return

        rule = req.url_rule
        view = current_app.view_functions[rule.endpoint]
        if getattr(view, "csrf_support_graceful_failure", False):
            # view can handle it nicely for the user
            return None

        if hasattr(view, "view_class") and getattr(
                view.view_class, "csrf_support_graceful_failure", False):
            return None

        raise BadRequest(failed)
Exemplo n.º 26
0
    def ungrant_role(self, principal, role, object=None):
        """Ungrant `role` to `user` (either globally, if `object` is None, or
        on the specific `object`)."""
        assert principal
        principal = unwrap(principal)
        session = object_session(object) if object is not None else db.session
        manager = self._current_user_manager(session=session)

        args = {
            "role": role,
            "object": object,
            "anonymous": False,
            "user": None,
            "group": None,
        }
        query = session.query(RoleAssignment)
        query = query.filter(
            RoleAssignment.role == role, RoleAssignment.object == object
        )

        if principal is AnonymousRole or (
            hasattr(principal, "is_anonymous") and principal.is_anonymous
        ):
            args["anonymous"] = True
            query.filter(
                RoleAssignment.anonymous == False,
                RoleAssignment.user == None,
                RoleAssignment.group == None,
            )

        elif isinstance(principal, User):
            args["user"] = principal
            query = query.filter(RoleAssignment.user == principal)
        else:
            args["group"] = principal
            query = query.filter(RoleAssignment.group == principal)

        ra = query.one()
        session.delete(ra)
        audit = SecurityAudit(manager=manager, op=SecurityAudit.REVOKE, **args)
        session.add(audit)
        self._needs_flush()
        self._clear_role_cache(principal)
Exemplo n.º 27
0
    def flask_app(self) -> Flask:
        if has_app_context():
            return unwrap(flask_current_app)

        self.flask_app_factory = symbol_by_name(self.flask_app_factory)
        app = self.flask_app_factory()

        if "sentry" in app.extensions:
            # pyre-fixme[21]: Could not find `raven`.
            from raven.contrib.celery import register_signal, register_logger_signal

            client = app.extensions["sentry"].client
            client.tags["process_type"] = "celery task"
            register_signal(client)
            register_logger_signal(client)

        # pyre-fixme[16]: Module `multiprocessing` has no attribute `util`.
        register_after_fork(app, self._setup_after_fork)
        return app
Exemplo n.º 28
0
    def before_request(self):
        req = unwrap(request)
        failed = req.csrf_failed

        if not failed:
            return

        rule = req.url_rule
        view = current_app.view_functions[rule.endpoint]
        if getattr(view, "csrf_support_graceful_failure", False):
            # view can handle it nicely for the user
            return None

        if hasattr(view, "view_class") and getattr(
            view.view_class, "csrf_support_graceful_failure", False
        ):
            return None

        raise BadRequest(failed)
Exemplo n.º 29
0
def delete_multiple(folder):
    check_write_access(folder)

    folders, docs = get_selected_objects(folder)

    for obj in docs + folders:
        app = unwrap(current_app)
        community = g.community._model
        activity.send(app,
                      actor=current_user,
                      verb="delete",
                      object=obj,
                      target=community)
        repository.delete_object(obj)

    if docs + folders:
        db.session.commit()
        if docs and folders:
            msg = _(
                "%(file_num)d files and %(folder_num)d folders sucessfully "
                "deleted.",
                file_num=len(docs),
                folder_num=len(folders),
            )
        elif docs and not folders:
            msg = _n(
                "1 file sucessfully deleted.",
                "%(num)d files sucessfully deleted.",
                num=len(docs),
            )
        else:
            msg = _n(
                "1 folder sucessfully deleted.",
                "%(num)d folders sucessfully deleted.",
                num=len(folders),
            )

        flash(msg, "success")
    else:
        flash(_("No object deleted"), "error")

    return redirect(url_for(folder))
Exemplo n.º 30
0
def page_delete():
    title = request.form["title"].strip()
    try:
        page = get_page_by_title(title)
    except NoResultFound:
        flash(_("This page doesn't exist"), "error")
        return redirect(url_for(".index", community_id=g.community.slug))

    db.session.delete(page)

    app = unwrap(current_app)
    community = g.community._model
    activity.send(app,
                  actor=current_user,
                  verb="delete",
                  object=page,
                  target=community)

    db.session.commit()
    flash(_("Page %(title)s deleted.", title=title))
    return redirect(url_for(".index", community_id=g.community.slug))
Exemplo n.º 31
0
    def load_user(user_id: str) -> Optional[User]:
        try:
            user = User.query.get(user_id)

            if not user or not user.can_login:
                # if a user is edited and should not have access any more, this
                # will ensure they cannot continue if he had an active session
                return None
        except Exception:
            logger.warning("Error during login.", exc_info=True)
            session = db.session()
            if not session.is_active:
                # session is not usable, rollback should restore a usable
                # session
                session.rollback()

            return None

        app = unwrap(current_app)
        app.services[AuthService.name].user_logged_in(app, user)
        user_loaded.send(app, user=user)
        return user
Exemplo n.º 32
0
def delete_multiple(folder):
    check_write_access(folder)

    folders, docs = get_selected_objects(folder)

    for obj in docs + folders:
        app = unwrap(current_app)
        community = g.community._model
        activity.send(
            app, actor=current_user, verb="delete", object=obj, target=community
        )
        repository.delete_object(obj)

    if docs + folders:
        db.session.commit()
        if docs and folders:
            msg = _(
                "%(file_num)d files and %(folder_num)d folders sucessfully " "deleted.",
                file_num=len(docs),
                folder_num=len(folders),
            )
        elif docs and not folders:
            msg = _n(
                "1 file sucessfully deleted.",
                "%(num)d files sucessfully deleted.",
                num=len(docs),
            )
        else:
            msg = _n(
                "1 folder sucessfully deleted.",
                "%(num)d folders sucessfully deleted.",
                num=len(folders),
            )

        flash(msg, "success")
    else:
        flash(_("No object deleted"), "error")

    return redirect(url_for(folder))
Exemplo n.º 33
0
    def load_user(user_id):
        try:
            user = User.query.get(user_id)

            if not user or not user.can_login:
                # if a user is edited and should not have access any more, this
                # will ensure they cannot continue if he had an active session
                return None
        except Exception:
            logger.warning("Error during login.", exc_info=True)
            session = db.session()
            if not session.is_active:
                # session is not usable, rollback should restore a usable
                # session
                session.rollback()

            return None

        app = unwrap(current_app)
        app.services[AuthService.name].user_logged_in(app, user)
        user_loaded.send(app, user=user)
        return user
Exemplo n.º 34
0
def indexable_role(principal):
    """Return a string suitable for query against `allowed_roles_and_users`
    field.

    :param principal: It can be :data:`Anonymous`, :data:`Authenticated`,
      or an instance of :class:`User` or :class:`Group`.
    """
    principal = unwrap(principal)

    if hasattr(principal, "is_anonymous") and principal.is_anonymous:
        # transform anonymous user to anonymous role
        principal = Anonymous

    if isinstance(principal, Role):
        return "role:{}".format(principal.name)
    elif isinstance(principal, User):
        fmt = "user:{:d}"
    elif isinstance(principal, Group):
        fmt = "group:{:d}"
    else:
        raise ValueError(repr(principal))

    return fmt.format(principal.id)
Exemplo n.º 35
0
def indexable_role(principal):
    """Return a string suitable for query against `allowed_roles_and_users`
    field.

    :param principal: It can be :data:`Anonymous`, :data:`Authenticated`,
      or an instance of :class:`User` or :class:`Group`.
    """
    principal = unwrap(principal)

    if hasattr(principal, "is_anonymous") and principal.is_anonymous:
        # transform anonymous user to anonymous role
        principal = Anonymous

    if isinstance(principal, Role):
        return f"role:{principal.name}"
    elif isinstance(principal, User):
        fmt = "user:{:d}"
    elif isinstance(principal, Group):
        fmt = "group:{:d}"
    else:
        raise ValueError(repr(principal))

    return fmt.format(principal.id)
Exemplo n.º 36
0
 def check_security() -> None:
     user = unwrap(current_user)
     if not security.has_role(user, "admin"):
         raise Forbidden()
Exemplo n.º 37
0
 def check_security():
     user = unwrap(current_user)
     if not security.has_role(user, "admin"):
         raise Forbidden()
Exemplo n.º 38
0
    def query_entity_with_permission(self, permission, user=None, Model=Entity):
        """Filter a query on an :class:`Entity` or on of its subclasses.

        Usage::

            read_q = security.query_entity_with_permission(READ, Model=MyModel)
            MyModel.query.filter(read_q)

        It should always be placed before any `.join()` happens in the query; else
        sqlalchemy might join to the "wrong" entity table when joining to other
        :class:`Entity`.

        :param user: user to filter for. Default: `current_user`.

        :param permission: required :class:`Permission`

        :param Model: An :class:`Entity` based class. Useful when there is more than
        one Entity based object in query, or if an alias should be used.

        :returns: a `sqlalchemy.sql.exists()` expression.
        """
        assert isinstance(permission, Permission)
        assert issubclass(Model, Entity)
        RA = sa.orm.aliased(RoleAssignment)
        PA = sa.orm.aliased(PermissionAssignment)
        # id column from entity table. Model.id would refer to 'model' table.
        # this allows the DB to use indexes / foreign key constraints.
        id_column = sa.inspect(Model).primary_key[0]
        creator = Model.creator
        owner = Model.owner

        if not self.running:
            return sa.sql.exists([1])

        if user is None:
            user = unwrap(current_user)

        # build role CTE
        principal_filter = RA.anonymous == True

        if not user.is_anonymous:
            principal_filter |= RA.user == user

        if user.groups:
            principal_filter |= RA.group_id.in_([g.id for g in user.groups])

        RA = sa.sql.select([RA], principal_filter).cte()
        permission_exists = sa.sql.exists([1]).where(
            sa.sql.and_(
                PA.permission == permission,
                PA.object_id == id_column,
                (RA.c.role == PA.role) | (PA.role == AnonymousRole),
                (RA.c.object_id == PA.object_id) | (RA.c.object_id == None),
            )
        )

        # is_admin: self-explanatory. It search for local or global admin
        # role, but PermissionAssignment is not involved, thus it can match on
        # entities that don't have *any permission assignment*, whereas previous
        # expressions cannot.
        is_admin = sa.sql.exists([1]).where(
            sa.sql.and_(
                RA.c.role == Admin,
                (RA.c.object_id == id_column) | (RA.c.object_id == None),
                principal_filter,
            )
        )

        filter_expr = permission_exists | is_admin

        if user and not user.is_anonymous:
            is_owner_or_creator = sa.sql.exists([1]).where(
                sa.sql.and_(
                    PA.permission == permission,
                    PA.object_id == id_column,
                    sa.sql.or_(
                        (PA.role == Owner) & (owner == user),
                        (PA.role == Creator) & (creator == user),
                    ),
                )
            )
            filter_expr |= is_owner_or_creator

        return filter_expr
Exemplo n.º 39
0
    def has_permission(self, user, permission, obj=None, inherit=False, roles=None):
        """
        :param obj: target object to check permissions.
        :param inherit: check with permission inheritance. By default, check only
                        local roles.
        :param roles: additional valid role or iterable of roles having
                      `permission`.
        """
        if not isinstance(permission, Permission):
            assert permission in PERMISSIONS
            permission = Permission(permission)
        user = unwrap(user)

        if not self.running:
            return True

        session = None
        if obj is not None:
            session = object_session(obj)

        if session is None:
            session = db.session()

        # root always have any permission
        if isinstance(user, User) and user.id == 0:
            return True

        # valid roles
        # 1: from database
        pa_filter = PermissionAssignment.object == None
        if obj is not None and obj.id is not None:
            pa_filter |= PermissionAssignment.object == obj

        pa_filter &= PermissionAssignment.permission == permission
        valid_roles = session.query(PermissionAssignment.role).filter(pa_filter)
        valid_roles = {res[0] for res in valid_roles.yield_per(1000)}

        # complete with defaults
        valid_roles |= {Admin}  # always have all permissions
        valid_roles |= DEFAULT_PERMISSION_ROLE.get(permission, set())

        # FIXME: obj.__class__ could define default permisssion matrix too

        if roles is not None:
            if isinstance(roles, (Role,) + (str,)):
                roles = (roles,)

            for r in roles:
                valid_roles.add(Role(r))

        # FIXME: query permission_role: global and on object

        if AnonymousRole in valid_roles:
            return True

        if Authenticated in valid_roles and not user.is_anonymous:
            return True

        # first test global roles, then object local roles
        checked_objs = [None, obj]
        if inherit and obj is not None:
            while obj.inherit_security and obj.parent is not None:
                obj = obj.parent
                checked_objs.append(obj)

        principals = [user] + list(user.groups)
        self._fill_role_cache_batch(principals)

        return any(
            self.has_role(principal, valid_roles, item)
            for principal in principals
            for item in checked_objs
        )
Exemplo n.º 40
0
 def prepare_args(self, args, kwargs):
     args, kwargs = super().prepare_args(args, kwargs)
     self.uploads = current_app.extensions["uploads"]
     self.user = unwrap(current_user)
     return args, kwargs
Exemplo n.º 41
0
def process_email(message):
    # type: (email.message.Message) -> bool
    """Email.Message object from command line script Run message (parsed
    email).

    Processing chain extract community thread post member from reply_to
    persist post in db.
    """
    app = unwrap(current_app)
    # Extract post destination from To: field, (community/forum/thread/member)
    to_address = message["To"]

    assert isinstance(to_address, text_type)

    if not (has_subtag(to_address)):
        logger.info("Email %r has no subtag, skipping...", to_address)
        return False

    try:
        infos = extract_email_destination(to_address)
        locale = infos[0]
        thread_id = infos[1]
        user_id = infos[2]
    except BaseException:
        logger.error(
            "Recipient %r cannot be converted to locale/thread_id/user.id",
            to_address,
            exc_info=True,
        )
        return False

    # Translate marker with locale from email address
    rq_headers = [("Accept-Language", locale)]
    with app.test_request_context("/process_email", headers=rq_headers):
        marker = text_type(MAIL_REPLY_MARKER)

    # Extract text and attachments from message
    try:
        newpost, attachments = process(message, marker)
    except BaseException:
        logger.error("Could not Process message", exc_info=True)
        return False

    # Persist post
    with current_app.test_request_context("/process_email",
                                          headers=rq_headers):
        g.user = User.query.get(user_id)
        thread = Thread.query.get(thread_id)
        community = thread.community
        # FIXME: check membership, send back an informative email in case of an
        # error
        post = thread.create_post(body_html=newpost)
        obj_meta = post.meta.setdefault("abilian.sbe.forum", {})
        obj_meta["origin"] = "email"
        obj_meta["send_by_email"] = True
        activity.send(app,
                      actor=g.user,
                      verb="post",
                      object=post,
                      target=community)

        for desc in attachments:
            attachment = PostAttachment(name=desc["filename"])
            attachment.post = post
            attachment.set_content(desc["data"], desc["content_type"])
            db.session.add(attachment)
        db.session.commit()

    # Notify all parties involved
    send_post_by_email.delay(post.id)
    return True
Exemplo n.º 42
0
    def has_role(self, principal, role, object=None):
        """True if `principal` has `role` (either globally, if `object` is
        None, or on the specific `object`).

        :param:role: can be a list or tuple of strings or a :class:`Role`
            instance

        `object` can be an :class:`Entity`, a string, or `None`.

        Note: we're using a cache for efficiency here. TODO: check that we're not
        over-caching.

        Note2: caching could also be moved upfront to when the user is loaded.
        """
        if not principal:
            return False

        principal = unwrap(principal)
        if not self.running:
            return True

        if isinstance(role, (Role, (str,))):
            role = (role,)

        # admin & manager always have role
        valid_roles = frozenset((Admin, Manager) + tuple(role))

        if AnonymousRole in valid_roles:
            # everybody has the role 'Anonymous'
            return True

        if (
            Authenticated in valid_roles
            and isinstance(principal, User)
            and not principal.is_anonymous
        ):
            return True

        if principal is AnonymousRole or (
            hasattr(principal, "is_anonymous") and principal.is_anonymous
        ):
            # anonymous user, and anonymous role isn't in valid_roles
            return False

        # root always have any role
        if isinstance(principal, User) and principal.id == 0:
            return True

        if object:
            assert isinstance(object, Entity)
            object_key = "{}:{}".format(object.object_type, str(object.id))
            if Creator in role:
                if object.creator == principal:
                    return True
            if Owner in role:
                if object.owner == principal:
                    return True

        else:
            object_key = None

        all_roles = (
            self._fill_role_cache(principal)
            if self.app_state.use_cache
            else self._all_roles(principal)
        )
        roles = set()
        roles |= all_roles.get(None, set())
        roles |= all_roles.get(object_key, set())
        return len(valid_roles & roles) > 0
Exemplo n.º 43
0
 def prepare_args(self, args, kwargs):
     args, kwargs = super().prepare_args(args, kwargs)
     self.uploads = current_app.extensions["uploads"]
     self.user = unwrap(current_user)
     return args, kwargs
Exemplo n.º 44
0
def process_email(message):
    # type: (email.message.Message) -> bool
    """Email.Message object from command line script Run message (parsed
    email).

    Processing chain extract community thread post member from reply_to
    persist post in db.
    """
    app = unwrap(current_app)
    # Extract post destination from To: field, (community/forum/thread/member)
    to_address = message["To"]

    assert isinstance(to_address, str)

    if not (has_subtag(to_address)):
        logger.info("Email %r has no subtag, skipping...", to_address)
        return False

    try:
        infos = extract_email_destination(to_address)
        locale = infos[0]
        thread_id = infos[1]
        user_id = infos[2]
    except BaseException:
        logger.error(
            "Recipient %r cannot be converted to locale/thread_id/user.id",
            to_address,
            exc_info=True,
        )
        return False

    # Translate marker with locale from email address
    rq_headers = [("Accept-Language", locale)]
    with app.test_request_context("/process_email", headers=rq_headers):
        marker = str(MAIL_REPLY_MARKER)

    # Extract text and attachments from message
    try:
        newpost, attachments = process(message, marker)
    except BaseException:
        logger.error("Could not Process message", exc_info=True)
        return False

    # Persist post
    with current_app.test_request_context("/process_email", headers=rq_headers):
        g.user = User.query.get(user_id)
        thread = Thread.query.get(thread_id)
        community = thread.community
        # FIXME: check membership, send back an informative email in case of an
        # error
        post = thread.create_post(body_html=newpost)
        obj_meta = post.meta.setdefault("abilian.sbe.forum", {})
        obj_meta["origin"] = "email"
        obj_meta["send_by_email"] = True
        activity.send(app, actor=g.user, verb="post", object=post, target=community)

        for desc in attachments:
            attachment = PostAttachment(name=desc["filename"])
            attachment.post = post
            attachment.set_content(desc["data"], desc["content_type"])
            db.session.add(attachment)
        db.session.commit()

    # Notify all parties involved
    send_post_by_email.delay(post.id)
    return True
Exemplo n.º 45
0
    def has_role(self, principal, role, object=None):
        """True if `principal` has `role` (either globally, if `object` is
        None, or on the specific `object`).

        :param:role: can be a list or tuple of strings or a :class:`Role`
            instance

        `object` can be an :class:`Entity`, a string, or `None`.

        Note: we're using a cache for efficiency here. TODO: check that we're not
        over-caching.

        Note2: caching could also be moved upfront to when the user is loaded.
        """
        if not principal:
            return False

        principal = unwrap(principal)
        if not self.running:
            return True

        if isinstance(role, (Role, string_types)):
            role = (role,)

        # admin & manager always have role
        valid_roles = frozenset((Admin, Manager) + tuple(role))

        if AnonymousRole in valid_roles:
            # everybody has the role 'Anonymous'
            return True

        if (
            Authenticated in valid_roles
            and isinstance(principal, User)
            and not principal.is_anonymous
        ):
            return True

        if principal is AnonymousRole or (
            hasattr(principal, "is_anonymous") and principal.is_anonymous
        ):
            # anonymous user, and anonymous role isn't in valid_roles
            return False

        # root always have any role
        if isinstance(principal, User) and principal.id == 0:
            return True

        if object:
            assert isinstance(object, Entity)
            object_key = "{}:{}".format(object.object_type, text_type(object.id))
            if Creator in role:
                if object.creator == principal:
                    return True
            if Owner in role:
                if object.owner == principal:
                    return True

        else:
            object_key = None

        all_roles = (
            self._fill_role_cache(principal)
            if self.app_state.use_cache
            else self._all_roles(principal)
        )
        roles = set()
        roles |= all_roles.get(None, set())
        roles |= all_roles.get(object_key, set())
        return len(valid_roles & roles) > 0
Exemplo n.º 46
0
    def has_permission(self, user, permission, obj=None, inherit=False, roles=None):
        """
        :param obj: target object to check permissions.
        :param inherit: check with permission inheritance. By default, check only
                        local roles.
        :param roles: additional valid role or iterable of roles having
                      `permission`.
        """
        if not isinstance(permission, Permission):
            assert permission in PERMISSIONS
            permission = Permission(permission)
        user = unwrap(user)

        if not self.running:
            return True

        session = None
        if obj is not None:
            session = object_session(obj)

        if session is None:
            session = db.session()

        # root always have any permission
        if isinstance(user, User) and user.id == 0:
            return True

        # valid roles
        # 1: from database
        pa_filter = PermissionAssignment.object == None
        if obj is not None and obj.id is not None:
            pa_filter |= PermissionAssignment.object == obj

        pa_filter &= PermissionAssignment.permission == permission
        valid_roles = session.query(PermissionAssignment.role).filter(pa_filter)
        valid_roles = {res[0] for res in valid_roles.yield_per(1000)}

        # complete with defaults
        valid_roles |= {Admin}  # always have all permissions
        valid_roles |= DEFAULT_PERMISSION_ROLE.get(permission, set())

        # FIXME: obj.__class__ could define default permisssion matrix too

        if roles is not None:
            if isinstance(roles, (Role,) + string_types):
                roles = (roles,)

            for r in roles:
                valid_roles.add(Role(r))

        # FIXME: query permission_role: global and on object

        if AnonymousRole in valid_roles:
            return True

        if Authenticated in valid_roles and not user.is_anonymous:
            return True

        # first test global roles, then object local roles
        checked_objs = [None, obj]
        if inherit and obj is not None:
            while obj.inherit_security and obj.parent is not None:
                obj = obj.parent
                checked_objs.append(obj)

        principals = [user] + list(user.groups)
        self._fill_role_cache_batch(principals)

        return any(
            (
                self.has_role(principal, valid_roles, item)
                for principal in principals
                for item in checked_objs
            )
        )
Exemplo n.º 47
0
    def query_entity_with_permission(self, permission, user=None, Model=Entity):
        """Filter a query on an :class:`Entity` or on of its subclasses.

        Usage::

            read_q = security.query_entity_with_permission(READ, Model=MyModel)
            MyModel.query.filter(read_q)

        It should always be placed before any `.join()` happens in the query; else
        sqlalchemy might join to the "wrong" entity table when joining to other
        :class:`Entity`.

        :param user: user to filter for. Default: `current_user`.

        :param permission: required :class:`Permission`

        :param Model: An :class:`Entity` based class. Useful when there is more than
        one Entity based object in query, or if an alias should be used.

        :returns: a `sqlalchemy.sql.exists()` expression.
        """
        assert isinstance(permission, Permission)
        assert issubclass(Model, Entity)
        RA = sa.orm.aliased(RoleAssignment)
        PA = sa.orm.aliased(PermissionAssignment)
        # id column from entity table. Model.id would refer to 'model' table.
        # this allows the DB to use indexes / foreign key constraints.
        id_column = sa.inspect(Model).primary_key[0]
        creator = Model.creator
        owner = Model.owner

        if not self.running:
            return sa.sql.exists([1])

        if user is None:
            user = unwrap(current_user)

        # build role CTE
        principal_filter = RA.anonymous == True

        if not user.is_anonymous:
            principal_filter |= RA.user == user

        if user.groups:
            principal_filter |= RA.group_id.in_([g.id for g in user.groups])

        RA = sa.sql.select([RA], principal_filter).cte()
        permission_exists = sa.sql.exists([1]).where(
            sa.sql.and_(
                PA.permission == permission,
                PA.object_id == id_column,
                (RA.c.role == PA.role) | (PA.role == AnonymousRole),
                (RA.c.object_id == PA.object_id) | (RA.c.object_id == None),
            )
        )

        # is_admin: self-explanatory. It search for local or global admin
        # role, but PermissionAssignment is not involved, thus it can match on
        # entities that don't have *any permission assignment*, whereas previous
        # expressions cannot.
        is_admin = sa.sql.exists([1]).where(
            sa.sql.and_(
                RA.c.role == Admin,
                (RA.c.object_id == id_column) | (RA.c.object_id == None),
                principal_filter,
            )
        )

        filter_expr = permission_exists | is_admin

        if user and not user.is_anonymous:
            is_owner_or_creator = sa.sql.exists([1]).where(
                sa.sql.and_(
                    PA.permission == permission,
                    PA.object_id == id_column,
                    sa.sql.or_(
                        (PA.role == Owner) & (owner == user),
                        (PA.role == Creator) & (creator == user),
                    ),
                )
            )
            filter_expr |= is_owner_or_creator

        return filter_expr