Ejemplo n.º 1
0
def translate_compare(expression: Expression, session: Session, model, get_model):
    (left, right) = expression.args
    left_path = dot_path(left)
    if left_path:
        path, field_name = left_path[:-1], left_path[-1]
        return translate_dot(
            path,
            session,
            model,
            functools.partial(emit_compare, field_name, right, expression.operator),
        )
    else:
        assert left == Variable("_this")
        if not isinstance(right, model):
            return sql.false()

        if expression.operator not in ("Eq", "Unify"):
            raise UnsupportedError(
                f"Unsupported comparison: {expression}. Models can only be compared"
                " with `=` or `==`"
            )

        primary_keys = [pk.name for pk in inspect(model).primary_key]
        pk_filter = sql.true()
        for key in primary_keys:
            pk_filter &= getattr(model, key) == getattr(right, key)
        return pk_filter
Ejemplo n.º 2
0
def translate_isa(expression: Expression, session: Session, model, get_model):
    assert expression.operator == "Isa"
    left, right = expression.args
    if dot_path(left) == ():
        assert left == Variable("_this")
    else:
        for field_name in dot_path(left):
            _, model, __ = get_relationship(model, field_name)

    assert not right.fields, "Unexpected fields in isa expression"
    constraint_type = get_model(right.tag)
    return sql.true() if issubclass(model, constraint_type) else sql.false()
Ejemplo n.º 3
0
def translate_isa(expression: Expression, session: Session, model, get_model):
    assert expression.operator == "Isa"
    left, right = expression.args
    left_path = dot_path(left)
    assert left_path[0] == Variable("_this")
    left_path = left_path[1:]  # Drop _this.
    if left_path:
        for field_name in left_path:
            _, model, __ = get_relationship(model, field_name)

    assert not right.fields, "Unexpected fields in isa expression"
    constraint_type = get_model(right.tag)
    model_type = inspect(model, raiseerr=True).class_
    return sql.true() if issubclass(model_type,
                                    constraint_type) else sql.false()
Ejemplo n.º 4
0
def test_preprocess_nested_many_many():
    _this = Variable("_this")
    expression = Expression(
        "And",
        [
            Expression("Isa", [_this, Pattern("Post", {})]),
            Expression(
                "In",
                [Variable("_tag_16"),
                 Expression("Dot", [_this, "tags"])]),
            Expression(
                "In",
                [
                    Variable("_user_18"),
                    Expression("Dot", [Variable("_tag_16"), "users"]),
                ],
            ),
            Expression(
                "Unify",
                [
                    "admin",
                    Expression("Dot", [Variable("_user_18"), "username"])
                ],
            ),
        ],
    )

    vars = defaultdict(list)
    new_expression = preprocess_expression(expression, vars)

    assert new_expression == Expression(
        "And",
        [
            Expression("Isa", [_this, Pattern("Post", {})]),
            Expression(
                "In",
                [Variable("_tag_16"),
                 Expression("Dot", [_this, "tags"])]),
        ],
    )

    assert vars == {
        Variable("_tag_16"): [
            Expression(
                "In",
                [
                    Variable("_user_18"),
                    Expression("Dot", [Variable("_tag_16"), "users"]),
                ],
            )
        ],
        Variable("_user_18"): [
            Expression(
                "Unify",
                [
                    "admin",
                    Expression("Dot", [Variable("_user_18"), "username"])
                ],
            )
        ],
    }

    users_expr = Expression("And", [
        Expression("Unify",
                   ["admin", Expression("Dot", [_this, "username"])])
    ])
    tags_expr = Expression(
        "And",
        [Expression(
            "In", [users_expr, Expression("Dot", [_this, "users"])])])

    assert preprocess(expression) == Expression(
        "And",
        [
            Expression("Isa", [_this, Pattern("Post", {})]),
            Expression("In", [tags_expr,
                              Expression("Dot", [_this, "tags"])]),
        ],
    )
Ejemplo n.º 5
0
def dot_op_field(expr):
    """Get the field from dot op ``expr`` or return ``False``."""
    return (isinstance(expr, Expression) and expr.operator == "Dot"
            and isinstance(expr.args[0], Variable)
            and expr.args[0] == Variable("_this") and expr.args[1])
Ejemplo n.º 6
0
def sub_this(variable: Variable, expression: Expression) -> Expression:
    """Substitute _this for ``variable`` in ``expression``."""
    return sub_var(variable, Variable("_this"), expression)
Ejemplo n.º 7
0
def is_this(variable):
    """Return true if ``variable`` is ``_this``."""
    return variable == Variable("_this")
Ejemplo n.º 8
0
def authorize_model(request, model, *, actor=None, action=None) -> Q:
    """Authorize ``request`` for django model ``model``, ``actor``, and ``action``.

    .. warning::

        This feature is currently in preview.

    Partially evaluates the Polar rule ``allow(actor, action, Variable(model))``. If
    authorization fails, raises a :py:class:`django.core.exceptions.PermissionDenied`
    exception.

    Otherwise, returns a django ``Q`` object representing a filter that must be
    applied to ``model``. This object can be applied to filter query results to
    only contain authorized objects.

    For example::

        post_filter = authorize_model(request, Post)
        authorized_posts = Post.objects.filter(post_filter)

    See also:

    - :py:class:`django_oso.models.AuthorizedModel`

    :param actor: The actor making the request. Defaults to ``request.user``.
    :param action: The action to authorize the actor to perform. Defaults to
                    ``request.method``.
    :param model: The model to authorize access for.

    :raises django.core.exceptions.PermissionDenied: If the request is not authorized.
    :returns: A django ``Q`` object representing the authorization filter.
    """
    if actor is None:
        actor = request.user

    if action is None:
        action = request.method

    assert issubclass(model, Model), f"Expected a model; received: {model}"
    resource = Variable("resource")
    constraint = TypeConstraint(resource, polar_model_name(model))
    results = Oso.query_rule(
        "allow",
        actor,
        action,
        resource,
        bindings={resource: constraint},
        accept_expression=True,
    )

    filter = None
    for result in results:
        resource_partial = result["bindings"]["resource"]
        if filter is None:
            filter = Q()

        next_filter = partial_to_query_filter(resource_partial, model)
        if next_filter == TRUE_FILTER:
            return TRUE_FILTER

        filter |= next_filter

    if filter is None:
        raise PermissionDenied()

    return filter