Example #1
0
def test_no_objects_collection_condition():
    public_tag = Tag(name="public", is_public=True)
    private_tag = Tag(name="private", is_public=False)

    post0 = Post(id=0, contents="public tag", tags=[public_tag])
    post1 = Post(id=1, contents="no tags", tags=[])
    post2 = Post(id=2, contents="both tags", tags=[public_tag, private_tag])
    post3 = Post(id=3, contents="public tag 2", tags=[public_tag])
    post4 = Post(id=4, contents="private tag", tags=[private_tag])

    public_tag.save()
    private_tag.save()
    post0.save()
    post1.save()
    post2.save()
    post3.save()
    post4.save()

    post0.tags.set([public_tag])
    post2.tags.set([public_tag, private_tag])
    post3.tags.set([public_tag])
    post4.tags.set([private_tag])

    Oso.load_str(
        """
        allow(_, _, post: test_app2::Post) if
            not (tag in post.tags and tag.is_public = true);
    """
    )

    posts = Post.objects.authorize(None, actor="u", action="r").all()
    assert len(posts) == 2
    assert post0 in posts
    assert post3 in posts
Example #2
0
def test_route_authorization(client, settings, simple_policy):
    """Test route authorization middleware"""
    settings.MIDDLEWARE.append(
        "django.contrib.sessions.middleware.SessionMiddleware")
    settings.MIDDLEWARE.append(
        "django.contrib.auth.middleware.AuthenticationMiddleware")
    settings.MIDDLEWARE.append("django_oso.middleware.RouteAuthorization")

    response = client.get("/a/")
    assert response.status_code == 403

    response = client.get("/b/")
    assert response.status_code == 403

    Oso.load_str('allow(_, "GET", _: HttpRequest{path: "/a/"});')
    response = client.get("/a/")
    assert response.status_code == 200

    response = client.post("/a/")
    assert response.status_code == 403

    # Django runs url resolving after middleware, so there is no way
    # for the route authorization middleware to determine if the response
    # would be a 404 and not apply authorization.
    response = client.get("/notfound/")
    assert response.status_code == 403
Example #3
0
def test_unify_ins(tag_nested_many_many_fixtures):
    Oso.load_str("""
        allow(_, _, post) if
            user1 in post.users and
            user2 in post.users and
            user1.id = user2.id and
            user1.id > 1 and
            user2.id <= 2;
        """)
    user = User.objects.get(username="******")
    authorize_filter = authorize_model(None, Post, actor=user, action="read")
    posts = Post.objects.filter(authorize_filter)
    expected = """
        SELECT "test_app2_post"."id", "test_app2_post"."contents", "test_app2_post"."title", "test_app2_post"."access_level",
               "test_app2_post"."created_by_id", "test_app2_post"."needs_moderation"
        FROM "test_app2_post"
        LEFT OUTER JOIN "test_app2_user_posts" ON ("test_app2_post"."id" = "test_app2_user_posts"."post_id")
        WHERE (EXISTS(SELECT U0."id"
                      FROM "test_app2_user" U0
                      INNER JOIN "test_app2_user" V0 ON (U0."id" = V0."id")
                      WHERE (U0."id" = "test_app2_user_posts"."user_id"
                             AND V0."id" = "test_app2_user_posts"."user_id"
                             AND U0."id" <= 2
                             AND V0."id" > 1)))
    """
    assert str(posts.query) == " ".join(expected.split())
    assert len(posts) == 1
Example #4
0
def test_empty_constraints_in(tag_nested_many_many_fixtures):
    """Test that ``unbound in partial.field`` without any further constraints
    on unbound translates into an existence check."""
    Oso.load_str("""
            allow(_: test_app2::User, "read", post: test_app2::Post) if
                _tag in post.tags;
        """)
    user = User.objects.get(username="******")
    authorize_filter = authorize_model(None, Post, actor=user, action="read")
    posts = Post.objects.filter(authorize_filter).distinct()
    expected = f"""
        SELECT DISTINCT "test_app2_post"."id", "test_app2_post"."contents", "test_app2_post"."title",
                        "test_app2_post"."access_level", "test_app2_post"."created_by_id",
                        "test_app2_post"."needs_moderation"
        FROM "test_app2_post"
        WHERE "test_app2_post"."id" IN
            (SELECT V0."id"
             FROM "test_app2_post" V0
             LEFT OUTER JOIN "test_app2_post_tags" V1 ON (V0."id" = V1."post_id")
             WHERE EXISTS(SELECT U0."id"
                          FROM "test_app2_tag" U0
                          WHERE U0."id" = {parenthesize('V1."tag_id"')}){is_true()})
    """
    assert str(posts.query) == " ".join(expected.split())
    assert len(posts) == 5
    assert tag_nested_many_many_fixtures["not_tagged_post"] not in posts
Example #5
0
def test_in_with_constraints_but_no_matching_objects(
        tag_nested_many_many_fixtures):
    Oso.load_str("""
            allow(_: test_app2::User, "read", post: test_app2::Post) if
                tag in post.tags and
                tag.name = "bloop";
        """)
    user = User.objects.get(username="******")
    posts = Post.objects.authorize(None, actor=user, action="read")
    expected = f"""
        SELECT "test_app2_post"."id", "test_app2_post"."contents", "test_app2_post"."title", "test_app2_post"."access_level",
               "test_app2_post"."created_by_id", "test_app2_post"."needs_moderation"
        FROM "test_app2_post"
        WHERE "test_app2_post"."id" IN (SELECT W0."id"
                                        FROM "test_app2_post" W0
                                        WHERE W0."id" IN
                                            (SELECT V0."id"
                                             FROM "test_app2_post" V0
                                             LEFT OUTER JOIN "test_app2_post_tags" V1 ON (V0."id" = V1."post_id")
                                             WHERE EXISTS(SELECT U0."id"
                                                          FROM "test_app2_tag" U0
                                                          WHERE (U0."id" = {parenthesize('V1."tag_id"')}
                                                          AND U0."name" = bloop)){is_true()}))
    """
    assert str(posts.query) == " ".join(expected.split())
    assert len(posts) == 0
Example #6
0
def test_partial_subfield_isa():
    from test_app.models import Post, User

    alice = User(name="alice")
    alice.save()
    not_alice = User(name="not alice")
    not_alice.save()

    Post(created_by=alice).save(),
    Post(created_by=not_alice).save(),
    Post(created_by=alice).save(),

    Oso.load_str("""
            allow(_, _, post: test_app::Post) if check(post.created_by);
            check(user: test_app::User) if user.name = "alice";
            check(post: test_app::Post) if post.is_private = false;
        """)

    authorize_filter = authorize_model(None, Post, actor="foo", action="bar")
    assert (
        str(authorize_filter) == "(OR:" +
        " (AND: (NOT (AND: ('pk__in', []))), (NOT (AND: ('pk__in', []))), ('created_by__name', 'alice')),"
        + " ('pk__in', []))")
    authorized_posts = Post.objects.filter(authorize_filter)
    assert (
        str(authorized_posts.query) ==
        'SELECT "test_app_post"."id", "test_app_post"."is_private", "test_app_post"."name",'
        +
        ' "test_app_post"."timestamp", "test_app_post"."option", "test_app_post"."created_by_id"'
        + ' FROM "test_app_post" LEFT OUTER JOIN "test_app_user"' +
        ' ON ("test_app_post"."created_by_id" = "test_app_user"."id") WHERE "test_app_user"."name" = alice'
    )
    assert authorized_posts.count() == 2
Example #7
0
def test_nested_relationship_many_many(tag_nested_many_many_fixtures):
    """Test that nested relationships work.
    post - (many) -> tags - (many) -> User
    A user can read a post with a tag if the tag's creator is the user.
    """
    Oso.load_str("""
            allow(user: test_app2::User, "read", post: test_app2::Post) if
                tag in post.tags and
                user in tag.users;
        """)

    user = User.objects.get(username="******")
    authorize_filter = authorize_model(None, Post, actor=user, action="read")
    posts = Post.objects.filter(authorize_filter)
    assert tag_nested_many_many_fixtures["user_eng_post"] in posts
    assert tag_nested_many_many_fixtures["user_user_post"] in posts
    assert tag_nested_many_many_fixtures["random_post"] not in posts
    assert tag_nested_many_many_fixtures["not_tagged_post"] not in posts
    assert tag_nested_many_many_fixtures["all_tagged_post"] in posts

    user = User.objects.get(username="******")
    authorize_filter = authorize_model(None, Post, actor=user, action="read")
    posts = Post.objects.filter(authorize_filter)
    assert tag_nested_many_many_fixtures["user_eng_post"] not in posts
    assert tag_nested_many_many_fixtures["user_user_post"] not in posts
    assert tag_nested_many_many_fixtures["random_post"] in posts
    assert tag_nested_many_many_fixtures["not_tagged_post"] not in posts
    assert tag_nested_many_many_fixtures["all_tagged_post"] in posts
Example #8
0
def test_ground_object_in_collection():
    tag = Tag(name="tag")
    post0 = Post(id=0, contents="tag post")
    post1 = Post(id=1, contents="no tag post")
    post2 = Post(id=2, contents="tag 2 post")

    tag.save()
    post0.save()
    post1.save()
    post2.save()

    post0.tags.set([tag])
    post2.tags.set([tag])

    Oso.register_constant(tag, "allowed_tag")
    Oso.load_str(
        """
        allow(_, _, post: test_app2::Post) if
            allowed_tag in post.tags;
    """
    )

    posts = Post.objects.authorize(None, actor="u", action="r").all()
    assert len(posts) == 2
    assert post0 in posts
    assert post2 in posts
Example #9
0
def test_many_many_with_other_condition(tag_nested_many_many_fixtures):
    """Test that using a many-to-many condition OR any other condition does not
    result in duplicate results."""
    Oso.load_str("""
            allow(_: test_app2::User, "read", post: test_app2::Post) if
                tag in post.tags and
                tag.name = "eng";
            allow(_: test_app2::User, "read", post: test_app2::Post) if
                post.access_level = "public";
        """)
    user = User.objects.get(username="******")
    posts = Post.objects.authorize(None, actor=user, action="read")
    expected = f"""
       SELECT "test_app2_post"."id", "test_app2_post"."contents", "test_app2_post"."access_level",
              "test_app2_post"."created_by_id", "test_app2_post"."needs_moderation"
       FROM "test_app2_post"
       WHERE "test_app2_post"."id" IN
           (SELECT W0."id"
            FROM "test_app2_post" W0
            WHERE
                (W0."id" IN
                    (SELECT V0."id"
                    FROM "test_app2_post" V0
                    LEFT OUTER JOIN "test_app2_post_tags" V1 ON (V0."id" = V1."post_id")
                    WHERE
                        EXISTS(SELECT U0."id"
                                FROM "test_app2_tag" U0
                                WHERE (U0."id" = {parenthesize('V1."tag_id"')} AND U0."name" = eng)){is_true()})
                        OR W0."access_level" = public))
    """
    assert str(posts.query) == " ".join(expected.split())
    # all should be returned with no duplicates
    assert list(posts) == list(tag_nested_many_many_fixtures.values())
Example #10
0
def test_redundant_in_on_same_field(tag_nested_many_many_fixtures):
    Oso.load_str("""
        allow(_, "read", post) if
            tag1 in post.tags and
            tag2 in post.tags and
            tag1.name = "random" and
            tag2.is_public = true;
        """)
    user = User.objects.get(username="******")
    authorize_filter = authorize_model(None, Post, actor=user, action="read")
    posts = Post.objects.filter(authorize_filter)
    expected = f"""

        SELECT "test_app2_post"."id", "test_app2_post"."contents", "test_app2_post"."title", "test_app2_post"."access_level",
               "test_app2_post"."created_by_id", "test_app2_post"."needs_moderation"
        FROM "test_app2_post"
        WHERE "test_app2_post"."id" IN (SELECT V0."id"
                                        FROM "test_app2_post" V0
                                        LEFT OUTER JOIN "test_app2_post_tags" V1 ON (V0."id" = V1."post_id")
                                        WHERE (EXISTS(SELECT U0."id"
                                                      FROM "test_app2_tag" U0
                                                      WHERE (U0."id" = {parenthesize('V1."tag_id"')}
                                                      AND U0."name" = random)){is_true()}
                                                      AND EXISTS(SELECT U0."id"
                                                                 FROM "test_app2_tag" U0
                                                                 WHERE (U0."id" = {parenthesize('V1."tag_id"')}
                                                                 AND U0."is_public"{is_true()})){is_true()}))
    """
    assert str(posts.query) == " ".join(expected.split())
    assert len(posts) == 2
Example #11
0
def test_in_intersection(tag_nested_many_many_fixtures):
    Oso.load_str("""
        allow(_, _, post) if
            u in post.users and
            t in post.tags and
            u in t.users;
        """)
    user = User.objects.get(username="******")
    authorize_filter = authorize_model(None, Post, actor=user, action="read")
    posts = Post.objects.filter(authorize_filter)
    expected = f"""
        SELECT "test_app2_post"."id", "test_app2_post"."contents", "test_app2_post"."title", "test_app2_post"."access_level",
               "test_app2_post"."created_by_id", "test_app2_post"."needs_moderation"
        FROM "test_app2_post"
        WHERE "test_app2_post"."id"
        IN (SELECT X0."id"
            FROM "test_app2_post" X0
            LEFT OUTER JOIN "test_app2_user_posts" X1 ON (X0."id" = X1."post_id")
            LEFT OUTER JOIN "test_app2_post_tags" X3 ON (X0."id" = X3."post_id")
            WHERE (EXISTS(SELECT U0."id"
                   FROM "test_app2_user" U0
                   WHERE U0."id" = {parenthesize('X1."user_id"')}){is_true()}
                   AND EXISTS(SELECT W0."id"
                       FROM "test_app2_tag" W0
                       WHERE (W0."id" = {parenthesize('X3."tag_id"')}
                       AND W0."id" IN
                            (SELECT V0."id"
                             FROM "test_app2_tag" V0
                             LEFT OUTER JOIN "test_app2_tag_users" V1 ON (V0."id" = V1."tag_id")
                             WHERE EXISTS(SELECT U0."id"
                                         FROM "test_app2_user" U0
                                         WHERE U0."id" = {parenthesize('V1."user_id"')}){is_true()}))){is_true()}))
    """
    assert str(posts.query) == " ".join(expected.split())
    assert len(posts) == 4
Example #12
0
def test_rewrite_parameters():
    from test_app.models import Post

    Oso.load_str("""allow(_, _, resource) if g(resource.created_by);
           g(resource) if resource matches test_app::User;
        """)
    authorize_filter = authorize_model(None, Post, actor="foo", action="bar")
    assert str(authorize_filter) == str(TRUE_FILTER)
Example #13
0
def test_authorize_model_basic(post_fixtures):
    """Test that a simple policy with checks on non-relationship attributes is correct."""
    Oso.load_str("""
        allow(u, "read", post: test_app2::Post) if u in ["admin", "user"] and post.access_level = "public";
        allow("user", "write", post: test_app2::Post) if post.access_level = "private";
        allow("admin", "read", _post: test_app2::Post);
        allow("moderator", "read", post: test_app2::Post) if
            (post.access_level = "private" or post.access_level = "public") and
            post.needs_moderation = true;
        """)

    authorize_filter = authorize_model(None, Post, actor="user", action="read")
    assert str(authorize_filter) == "(AND: ('access_level', 'public'))"
    posts = Post.objects.filter(authorize_filter)
    assert posts.count() == 5
    assert posts.all()[0].contents == "foo public post"

    authorize_filter = authorize_model(None,
                                       Post,
                                       actor="user",
                                       action="write")
    assert str(authorize_filter) == "(AND: ('access_level', 'private'))"
    posts = Post.objects.filter(authorize_filter)
    assert posts.count() == 4
    assert posts.all()[0].contents == "foo private post"
    assert posts.all()[1].contents == "foo private post 2"

    authorize_filter = authorize_model(None,
                                       Post,
                                       actor="admin",
                                       action="read")
    assert str(authorize_filter) == str(TRUE_FILTER)
    posts = Post.objects.filter(authorize_filter)
    assert posts.count() == 9

    authorize_filter = authorize_model(None,
                                       Post,
                                       actor="moderator",
                                       action="read")
    expected = """
         (OR:
            (AND:
                ('access_level', 'private'),
                ('needs_moderation', True)),
            (AND:
                ('access_level', 'public'),
                ('needs_moderation', True)))
    """
    assert str(authorize_filter) == " ".join(expected.split())
    posts = Post.objects.filter(authorize_filter)
    assert posts.count() == 4
    assert posts.all()[0].contents == "private for moderation"
    assert posts.all()[1].contents == "public for moderation"

    # Not authorized
    with pytest.raises(PermissionDenied):
        authorize_model(None, Post, actor="guest", action="read")
Example #14
0
 def handle(self, *args, **options):
     from django_oso.oso import Oso
     while True:
         # Run OSO (which runs in loop too), and handle unhandled exceptions (KB Interrupt is already handled within
         # repl as a normal return.
         try:
             Oso.repl()
             return
         except Exception as e:
             traceback.print_exc()
Example #15
0
def test_negated_matches_with_partial(rf):
    from test_app.models import Post

    Post(name="test", is_private=False, timestamp=1).save()
    Oso.load_str("""
        allow(1, _, post) if not post matches test_app::Post;
        allow(2, _, post) if not post matches test_app::User;
        allow(3, _, post) if not post.created_by matches test_app::User;
        allow(4, _, post) if not post.created_by matches test_app::Post;
        """)
    request = rf.get("/")

    request.user = 1
    authorize_filter = authorize_model(request, Post)
    assert str(authorize_filter) == (
        f"(AND: {str(TRUE_FILTER)}, (NOT (AND: {str(TRUE_FILTER)})))")
    authorized_posts = Post.objects.filter(authorize_filter)
    # For some reason, this only seems to be raised when stringifying.
    with pytest.raises(EmptyResultSet):
        str(authorized_posts.query)
    assert authorized_posts.count() == 0

    request.user = 2
    authorize_filter = authorize_model(request, Post)
    assert str(authorize_filter) == str(TRUE_FILTER)
    authorized_posts = Post.objects.filter(authorize_filter)
    expected = """
        SELECT "test_app_post"."id", "test_app_post"."is_private", "test_app_post"."name",
               "test_app_post"."timestamp", "test_app_post"."option", "test_app_post"."created_by_id"
        FROM "test_app_post"
    """
    assert str(authorized_posts.query) == " ".join(expected.split())
    assert authorized_posts.count() == 1

    request.user = 3
    authorize_filter = authorize_model(request, Post)
    assert str(authorize_filter) == (
        f"(AND: {str(TRUE_FILTER)}, (NOT (AND: {str(TRUE_FILTER)})))")
    authorized_posts = Post.objects.filter(authorize_filter)
    # For some reason, this only seems to be raised when stringifying.
    with pytest.raises(EmptyResultSet):
        str(authorized_posts.query)
    assert authorized_posts.count() == 0

    request.user = 4
    authorize_filter = authorize_model(request, Post)
    assert str(authorize_filter) == str(TRUE_FILTER)
    authorized_posts = Post.objects.filter(authorize_filter)
    expected = """
        SELECT "test_app_post"."id", "test_app_post"."is_private", "test_app_post"."name",
               "test_app_post"."timestamp", "test_app_post"."option", "test_app_post"."created_by_id"
        FROM "test_app_post"
    """
    assert str(authorized_posts.query) == " ".join(expected.split())
    assert authorized_posts.count() == 1
Example #16
0
def test_model_registration():
    """Test that models are automatically registered with the policy."""
    from test_app import models
    from oso import Variable

    assert (next(
        Oso.query_rule("models", models.TestRegistration(),
                       Variable("x")))["bindings"]["x"] == 1)
    assert (next(
        Oso.query_rule("models", models.TestRegistration2(),
                       Variable("x")))["bindings"]["x"] == 2)
Example #17
0
def test_this_in_var(tag_nested_many_many_fixtures):
    Oso.load_str("""
        # _this in var
        allow(_, _, post: test_app2::Post) if
            post in x and
            x in post.created_by.posts;
        """)
    user = User.objects.get(username="******")
    authorize_filter = authorize_model(None, Post, actor=user, action="read")
    posts = Post.objects.filter(authorize_filter)
    expected = """
    """
    assert str(posts.query) == " ".join(expected.split())
    assert len(posts) == 5050
Example #18
0
def test_many_many_with_other_condition(tag_nested_many_many_fixtures):
    """Test that using a many-to-many condition OR any other condition does not
    result in duplicate results."""
    Oso.load_str("""
            allow(_: test_app2::User, "read", post: test_app2::Post) if
                tag in post.tags and
                tag.name = "eng";
            allow(_: test_app2::User, "read", post: test_app2::Post) if
                post.access_level = "public";
        """)
    user = User.objects.get(username="******")
    posts = Post.objects.authorize(None, actor=user, action="read")
    # all should be returned with no duplicates
    assert list(posts) == list(tag_nested_many_many_fixtures.values())
Example #19
0
def test_partial_with_allow_all(rf):
    from test_app.models import Post

    Post(name="test", is_private=False, timestamp=1).save()
    Oso.load_str("allow(_, _, _);")
    request = rf.get("/")
    request.user = "******"

    authorize_filter = authorize_model(request, Post)
    assert str(authorize_filter) == str(TRUE_FILTER)
    authorized_posts = Post.objects.filter(authorize_filter)
    expected = """
        SELECT "test_app_post"."id", "test_app_post"."is_private", "test_app_post"."name",
               "test_app_post"."timestamp", "test_app_post"."option", "test_app_post"."created_by_id"
        FROM "test_app_post"
    """
    assert str(authorized_posts.query) == " ".join(expected.split())
    assert authorized_posts.count() == 1
Example #20
0
def test_null_with_partial(rf):
    from test_app.models import Post

    Post(name="test", is_private=False, timestamp=1).save()
    Oso.load_str("allow(_, _, post: test_app::Post) if post.option = nil;")
    request = rf.get("/")
    request.user = "******"

    authorize_filter = authorize_model(request, Post)
    assert (str(authorize_filter) ==
            "(AND: (NOT (AND: ('pk__in', []))), ('option', None))")
    authorized_posts = Post.objects.filter(authorize_filter)
    assert str(authorized_posts.query) == (
        'SELECT "test_app_post"."id", "test_app_post"."is_private", "test_app_post"."name", '
        +
        '"test_app_post"."timestamp", "test_app_post"."option", "test_app_post"."created_by_id"'
        + ' FROM "test_app_post"' + ' WHERE "test_app_post"."option" IS NULL')
    assert authorized_posts.count() == 1
Example #21
0
def test_deeply_nested_in(tag_nested_many_many_fixtures):
    Oso.load_str("""
        allow(_, _, post: test_app2::Post) if
            foo in post.created_by.posts and foo.id > 1 and
            bar in foo.created_by.posts and bar.id > 2 and
            baz in bar.created_by.posts and baz.id > 3 and
            post in baz.created_by.posts and post.id > 4;
        """)
    user = User.objects.get(username="******")
    authorize_filter = authorize_model(None, Post, actor=user, action="read")
    posts = Post.objects.filter(authorize_filter).distinct()
    expected = """
        SELECT DISTINCT "test_app2_post"."id", "test_app2_post"."contents",
                        "test_app2_post"."access_level", "test_app2_post"."created_by_id",
                        "test_app2_post"."needs_moderation"
        FROM "test_app2_post"
        INNER JOIN "test_app2_user" ON ("test_app2_post"."created_by_id" = "test_app2_user"."id")
        LEFT OUTER JOIN "test_app2_user_posts" ON ("test_app2_user"."id" = "test_app2_user_posts"."user_id")
        WHERE (EXISTS(SELECT W0."id"
                      FROM "test_app2_post" W0
                      INNER JOIN "test_app2_user" W1 ON (W0."created_by_id" = W1."id")
                      LEFT OUTER JOIN "test_app2_user_posts" W2 ON (W1."id" = W2."user_id")
                      WHERE (EXISTS(SELECT V0."id"
                                    FROM "test_app2_post" V0
                                    INNER JOIN "test_app2_user" V1 ON (V0."created_by_id" = V1."id")
                                    LEFT OUTER JOIN "test_app2_user_posts" V2 ON (V1."id" = V2."user_id")
                                    WHERE (EXISTS(SELECT U0."id"
                                                  FROM "test_app2_post" U0
                                                  INNER JOIN "test_app2_user" U1 ON (U0."created_by_id" = U1."id")
                                                  INNER JOIN "test_app2_user_posts" U2 ON (U1."id" = U2."user_id")
                                                  WHERE (U0."id" = V2."post_id"
                                                         AND U0."id" > 3

                                                         # This is not the sql that is generated.
                                                         # Instead U0."id" is the LHS of below.
                                                         AND "test_app2_post"."id" = U2."post_id"))
                                           AND V0."id" = W2."post_id"
                                           AND V0."id" > 2))
                             AND W0."id" = "test_app2_user_posts"."post_id"
                             AND W0."id" > 1))
               AND "test_app2_post"."id" > 4)
    """
    assert str(posts.query) == " ".join(expected.split())
    assert len(posts) == 1
Example #22
0
def test_null_with_partial(rf):
    from test_app.models import Post

    Post(name="test", is_private=False, timestamp=1).save()
    Oso.load_str("allow(_, _, post: test_app::Post) if post.option = nil;")
    request = rf.get("/")
    request.user = "******"

    authorize_filter = authorize_model(request, Post)
    assert str(authorize_filter) == "(AND: ('option', None))"
    authorized_posts = Post.objects.filter(authorize_filter)
    expected = """
        SELECT "test_app_post"."id", "test_app_post"."is_private", "test_app_post"."name",
               "test_app_post"."timestamp", "test_app_post"."option", "test_app_post"."created_by_id"
        FROM "test_app_post"
        WHERE "test_app_post"."option" IS NULL
    """
    assert str(authorized_posts.query) == " ".join(expected.split())
    assert authorized_posts.count() == 1
Example #23
0
def test_field_comparison():
    post0 = Post(id=0, contents="private post", title="not private post")
    post1 = Post(id=1, contents="private post", title="private post")
    post2 = Post(id=2, contents="post", title="post")

    post0.save()
    post1.save()
    post2.save()

    Oso.load_str(
        """
        allow(_, _, post: test_app2::Post) if
            post.title = post.contents;
    """
    )

    posts = Post.objects.authorize(None, actor="u", action="r").all()
    assert len(posts) == 2
    assert post1 in posts
    assert post2 in posts
Example #24
0
def test_scalar_in_list():
    post0 = Post(id=0, contents="private post", title="not private post")
    post1 = Post(id=1, contents="allowed posts", title="private post")
    post2 = Post(id=2, contents="post", title="post")

    post0.save()
    post1.save()
    post2.save()

    Oso.load_str(
        """
        allow(_, _, post: test_app2::Post) if
            post.contents in ["post", "allowed posts"];
    """
    )

    posts = Post.objects.authorize(None, actor="u", action="r").all()
    assert len(posts) == 2
    assert post1 in posts
    assert post2 in posts
Example #25
0
def test_partial_errors(rf, settings):
    from test_app.models import Post

    Post(name="test", is_private=False, timestamp=1).save()
    Post(name="test_past", is_private=False, timestamp=-1).save()
    Post(name="test_public", is_private=False, timestamp=1).save()
    Post(name="test_private", is_private=True, timestamp=1).save()
    Post(name="test_private_2", is_private=True, timestamp=1).save()

    request = rf.get("/")
    request.user = "******"

    Oso.load_str('allow(_, "fail", post: test_app::Post) if post matches {x: 1};')

    with pytest.raises(UnsupportedError):
        q = Post.objects.authorize(request, action="fail")

    # No rules for this.
    q = Post.objects.authorize(request, action="get")
    assert q.count() == 0
Example #26
0
def test_authorize_scalar_attribute_condition(post_fixtures):
    """Scalar attribute condition checks."""
    Oso.load_str("""
        # Object equals another object
        allow(actor: test_app2::User, "read", post: test_app2::Post) if
            post.created_by.is_banned = false and
            post.created_by = actor and
            post.access_level = "private";

        allow(_actor: test_app2::User, "read", post: test_app2::Post) if
            post.created_by.is_banned = false and
            post.access_level = "public";

        # moderator can see posts made by banned users.
        allow(actor: test_app2::User, "read", post: test_app2::Post) if
            actor.is_moderator = true and
            post.created_by.is_banned = true;
        """)

    foo = User.objects.get(username="******")
    authorize_filter = authorize_model(None, Post, actor=foo, action="read")
    posts = Post.objects.filter(authorize_filter)

    def allowed(post, user):
        return (post.access_level == "public"
                and not post.created_by.is_banned) or (
                    post.access_level == "private" and post.created_by == user)

    assert posts.count() == 7
    assert all(allowed(post, foo) for post in posts)

    admin = User.objects.get(username="******")
    authorize_filter = authorize_model(None, Post, actor=admin, action="read")
    posts = Post.objects.filter(authorize_filter)

    def allowed_admin(post):
        return post.created_by.is_banned

    assert posts.count() == 6
    for post in posts:
        assert allowed(post, admin) or allowed_admin(post)
Example #27
0
def test_reverse_many_relationship(tag_nested_many_many_fixtures):
    """Test an authorization rule over a reverse relationship"""
    Oso.load_str("""
        allow(actor, _, post: test_app2::Post) if
            post.users matches test_app2::User and
            actor in post.users;
        """)

    user = User.objects.get(username="******")
    authorize_filter = authorize_model(None, Post, actor=user, action="read")
    assert str(authorize_filter) == "(AND: ('users', <User: User object (1)>))"
    posts = Post.objects.filter(authorize_filter)
    expected = """
        SELECT "test_app2_post"."id", "test_app2_post"."contents", "test_app2_post"."title", "test_app2_post"."access_level",
               "test_app2_post"."created_by_id", "test_app2_post"."needs_moderation"
        FROM "test_app2_post"
        INNER JOIN "test_app2_user_posts" ON ("test_app2_post"."id" = "test_app2_user_posts"."post_id")
        WHERE "test_app2_user_posts"."user_id" = 1
    """
    assert str(posts.query) == " ".join(expected.split())
    assert len(posts) == 5
Example #28
0
def test_authorize_scalar_attribute_eq(post_fixtures):
    """Test authorization rules on a relationship with one object equaling another."""
    # Object equals another object
    Oso.load_str("""
        allow(actor: test_app2::User, "read", _: test_app2::Post{created_by: actor, access_level: "private"});
        allow(_: test_app2::User, "read", post) if
            post matches test_app2::Post{access_level: "public"};
        allow(_: test_app2::User{is_moderator: true}, "read", post: test_app2::Post) if
            post matches {access_level: "public"};
        """)

    foo = User.objects.get(username="******")
    authorize_filter = authorize_model(None, Post, actor=foo, action="read")
    posts = Post.objects.filter(authorize_filter)

    def allowed(post):
        return (post.access_level == "public"
                or post.access_level == "private" and post.created_by == foo)

    assert posts.count() == 8
    assert all(allowed(post) for post in posts)
Example #29
0
def test_in_with_constraints_but_no_matching_objects(
        tag_nested_many_many_fixtures):
    Oso.load_str("""
            allow(_: test_app2::User, "read", post: test_app2::Post) if
                tag in post.tags and
                tag.name = "bloop";
        """)
    user = User.objects.get(username="******")
    authorize_filter = authorize_model(None, Post, actor=user, action="read")
    assert (str(authorize_filter) ==
            "(AND: (NOT (AND: ('pk__in', []))), ('tags__name', 'bloop'))")
    posts = Post.objects.filter(authorize_filter)
    assert (
        str(posts.query) ==
        'SELECT "test_app2_post"."id", "test_app2_post"."contents", "test_app2_post"."access_level",'
        +
        ' "test_app2_post"."created_by_id", "test_app2_post"."needs_moderation"'
        + ' FROM "test_app2_post"' + ' INNER JOIN "test_app2_post_tags"' +
        ' ON ("test_app2_post"."id" = "test_app2_post_tags"."post_id")' +
        ' INNER JOIN "test_app2_tag"' +
        ' ON ("test_app2_post_tags"."tag_id" = "test_app2_tag"."id")' +
        ' WHERE "test_app2_tag"."name" = bloop')
    assert len(posts) == 0
Example #30
0
def test_partial_in_collection(tag_nested_many_many_fixtures):
    Oso.load_str("""
            allow(user: test_app2::User, "read", post: test_app2::Post) if
                post in user.posts.all();
        """)

    user = User.objects.get(username="******")
    authorize_filter = authorize_model(None, Post, actor=user, action="read")
    posts = Post.objects.filter(authorize_filter)
    assert tag_nested_many_many_fixtures["user_eng_post"] in posts
    assert tag_nested_many_many_fixtures["user_user_post"] in posts
    assert tag_nested_many_many_fixtures["random_post"] not in posts
    assert tag_nested_many_many_fixtures["not_tagged_post"] in posts
    assert tag_nested_many_many_fixtures["all_tagged_post"] in posts

    user = User.objects.get(username="******")
    authorize_filter = authorize_model(None, Post, actor=user, action="read")
    posts = Post.objects.filter(authorize_filter)
    assert tag_nested_many_many_fixtures["user_eng_post"] not in posts
    assert tag_nested_many_many_fixtures["user_user_post"] not in posts
    assert tag_nested_many_many_fixtures["random_post"] in posts
    assert tag_nested_many_many_fixtures["not_tagged_post"] not in posts
    assert tag_nested_many_many_fixtures["all_tagged_post"] not in posts