示例#1
0
def test_nested_relationship_many_single(session, oso,
                                         tag_nested_test_fixture):
    """Test that nested relationships work.

    post - (many) -> tags - (single) -> User

    A user can read a post with a tag if the tag's creator is the user.
    """
    oso.load_str("""
        allow(user, "read", post: Post) if tag in post.tags and tag.created_by = user;
    """)

    posts = session.query(Post).filter(
        authorize_model(oso, tag_nested_test_fixture["user"], "read", session,
                        Post))
    assert tag_nested_test_fixture["user_eng_post"] in posts
    assert tag_nested_test_fixture["user_user_post"] in posts
    assert not tag_nested_test_fixture["random_post"] in posts
    assert not tag_nested_test_fixture["not_tagged_post"] in posts
    assert tag_nested_test_fixture["all_tagged_post"] in posts
    assert posts.count() == 3

    posts = session.query(Post).filter(
        authorize_model(oso, tag_nested_test_fixture["other_user"], "read",
                        session, Post))
    assert not tag_nested_test_fixture["user_eng_post"] in posts
    assert not tag_nested_test_fixture["user_user_post"] in posts
    assert tag_nested_test_fixture["random_post"] in posts
    assert not tag_nested_test_fixture["not_tagged_post"] in posts
    assert tag_nested_test_fixture["all_tagged_post"] in posts
    assert posts.count() == 2
示例#2
0
def test_authorize_model_basic(session, oso, fixture_data):
    """Test that a simple policy with checks on non-relationship attributes is correct."""
    oso.load_str('allow("user", "read", post: Post) if post.access_level = "public";')
    oso.load_str('allow("user", "write", post: Post) if post.access_level = "private";')
    oso.load_str('allow("admin", "read", post: Post);')
    oso.load_str(
        'allow("moderator", "read", post: Post) if '
        '(post.access_level = "private" or post.access_level = "public") and '
        "post.needs_moderation = true;"
    )

    posts = authorize_model(oso, "user", "read", session, Post)

    assert posts.count() == 5
    assert posts.all()[0].contents == "foo public post"
    assert posts.all()[0].id == 0

    posts = authorize_model(oso, "user", "write", session, Post)

    assert posts.count() == 4
    assert posts.all()[0].contents == "foo private post"
    assert posts.all()[1].contents == "foo private post 2"

    posts = authorize_model(oso, "admin", "read", session, Post)
    assert posts.count() == 9

    posts = authorize_model(oso, "moderator", "read", session, Post)
    print_query(posts)
    assert posts.all()[0].contents == "private for moderation"
    assert posts.all()[1].contents == "public for moderation"

    posts = authorize_model(oso, "guest", "read", session, Post)
    assert posts.count() == 0
示例#3
0
def test_partial_in_collection(session, oso,
                               tag_nested_many_many_test_fixture):
    oso.load_str("""
        allow(user, "read", post: Post) if post in user.posts;
    """)

    user = tag_nested_many_many_test_fixture["user"]
    posts = session.query(Post).filter(
        authorize_model(oso, user, "read", session, Post))
    print_query(posts)
    posts = posts.all()

    assert tag_nested_many_many_test_fixture["user_eng_post"] in posts
    assert tag_nested_many_many_test_fixture["user_user_post"] in posts
    assert tag_nested_many_many_test_fixture["random_post"] not in posts
    assert tag_nested_many_many_test_fixture["not_tagged_post"] in posts
    assert tag_nested_many_many_test_fixture["all_tagged_post"] in posts
    assert len(posts) == 4

    user = tag_nested_many_many_test_fixture["other_user"]
    posts = (session.query(Post).filter(
        authorize_model(oso, user, "read", session, Post)).all())
    assert tag_nested_many_many_test_fixture["user_eng_post"] not in posts
    assert tag_nested_many_many_test_fixture["user_user_post"] not in posts
    assert tag_nested_many_many_test_fixture["random_post"] in posts
    assert tag_nested_many_many_test_fixture["not_tagged_post"] not in posts
    assert tag_nested_many_many_test_fixture["all_tagged_post"] not in posts
    assert len(posts) == 1
示例#4
0
def test_nested_relationship_many_many(session, oso,
                                       tag_nested_many_many_test_fixture):
    """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.
    """
    # TODO This direction doesn't work, because tag in user.tags is a concrete object.
    # allow(user, "read", post: Post) if tag in post.tags and tag in user.tags;
    oso.load_str("""
    allow(user, "read", post: Post) if tag in post.tags and user in tag.users;
    """)

    posts = session.query(Post).filter(
        authorize_model(oso, tag_nested_many_many_test_fixture["user"], "read",
                        session, Post))
    assert tag_nested_many_many_test_fixture["user_eng_post"] in posts
    assert tag_nested_many_many_test_fixture["user_user_post"] in posts
    assert not tag_nested_many_many_test_fixture["random_post"] in posts
    assert not tag_nested_many_many_test_fixture["not_tagged_post"] in posts
    assert tag_nested_many_many_test_fixture["all_tagged_post"] in posts

    posts = session.query(Post).filter(
        authorize_model(oso, tag_nested_many_many_test_fixture["other_user"],
                        "read", session, Post))
    assert not tag_nested_many_many_test_fixture["user_eng_post"] in posts
    assert not tag_nested_many_many_test_fixture["user_user_post"] in posts
    assert tag_nested_many_many_test_fixture["random_post"] in posts
    assert not tag_nested_many_many_test_fixture["not_tagged_post"] in posts
    assert tag_nested_many_many_test_fixture["all_tagged_post"] in posts
示例#5
0
def _authorize_query(query: Query) -> Optional[Query]:
    """Authorize an existing query with an oso instance, user and action."""
    # Get the query session.
    session = query.session

    # Check whether this is an oso session.
    if not isinstance(session, AuthorizedSessionBase):
        # Not an authorized session.
        return None

    oso = session.oso_context["oso"]
    user = session.oso_context["user"]
    action = session.oso_context["action"]

    # TODO (dhatch): This is necessary to allow ``authorize_query`` to work
    # on queries that have already been made.  If a query has a LIMIT or OFFSET
    # applied, SQLAlchemy will by default throw an error if filters are applied.
    # This prevents these errors from occuring, but could result in some
    # incorrect queries. We should remove this if possible.
    query = query.enable_assertions(False)

    entities = {column["entity"] for column in query.column_descriptions}
    for entity in entities:
        # Only apply authorization to columns that represent a mapper entity.
        if entity is None:
            continue

        authorized_filter = authorize_model(oso, user, action, query.session, entity)
        if authorized_filter is not None:
            query = query.filter(authorized_filter)

    return query
示例#6
0
def test_authorize_scalar_attribute_eq(session, oso, fixture_data):
    """Test authorization rules on a relationship with one object equaling another."""
    # Object equals another object
    oso.load_str(
        'allow(actor: User, "read", post: Post) if post.created_by = actor and '
        'post.access_level = "private";'
    )
    oso.load_str(
        'allow(actor: User, "read", post: Post) if ' 'post.access_level = "public";'
    )
    oso.load_str(
        'allow(actor: User{is_moderator: true}, "read", post: Post) if '
        'post.access_level = "public";'
    )

    foo = session.query(User).filter(User.username == "foo").first()

    posts = authorize_model(oso, foo, "read", session, Post)
    print_query(posts)

    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)
示例#7
0
def test_authorize_scalar_attribute_condition(session, oso, fixture_data):
    """Scalar attribute condition checks."""
    # Object equals another object

    oso.load_str(
        'allow(actor: User, "read", post: Post) if post.created_by.is_banned = false and '
        'post.created_by.username = actor.username and post.access_level = "private";'
    )

    oso.load_str(
        'allow(actor: User, "read", post: Post) if post.created_by.is_banned = false and '
        'post.access_level = "public";'
    )

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

    foo = session.query(User).filter(User.username == "foo").first()

    posts = authorize_model(oso, foo, "read", session, Post)

    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 = session.query(User).filter(User.username == "admin_user").first()
    posts = authorize_model(oso, admin, "read", session, Post)

    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)
示例#8
0
def test_redundant_in_on_same_field(session, oso,
                                    tag_nested_many_many_test_fixture):
    oso.load_str("""
        allow(_, "read", post: Post) if
            tag in post.tags and tag2 in post.tags
            and tag.name = "random" and tag2.is_public = true;
        """)

    posts = session.query(Post).filter(
        authorize_model(oso, "user", "read", session, Post))

    posts = posts.all()
    assert len(posts) == 2
示例#9
0
def test_unify_ins(session, oso, tag_nested_many_many_test_fixture):
    oso.load_str("""
        allow(_, _, post) if
            tag1 in post.tags and
            tag2 in post.tags and
            tag1.name = tag2.name and
            tag1.name > "a" and
            tag2.name <= "z";
        """)

    posts = session.query(Post).filter(
        authorize_model(oso, "user", "read", session, Post))

    assert posts.count() == 1
示例#10
0
def test_empty_constraints_in(session, oso, tag_nested_many_many_test_fixture):
    oso.load_str("""allow(_, "read", post: Post) if _tag in post.tags;""")
    user = tag_nested_many_many_test_fixture["user"]
    posts = authorize_model(oso, user, "read", session, Post)
    assert str(posts) == (
        "SELECT posts.id AS posts_id, posts.contents AS posts_contents, posts.access_level AS posts_access_level,"
        + " posts.created_by_id AS posts_created_by_id, posts.needs_moderation AS posts_needs_moderation"
        + " \nFROM posts"
        + " \nWHERE (EXISTS (SELECT 1"
        + " \nFROM post_tags, tags"
        + " \nWHERE posts.id = post_tags.post_id AND tags.name = post_tags.tag_id))"
    )
    posts = posts.all()
    assert len(posts) == 4
    assert tag_nested_many_many_test_fixture["not_tagged_post"] not in posts
示例#11
0
def test_in_intersection(session, oso, tag_nested_many_many_test_fixture):
    oso.load_str("""
        allow(_, _, post: Post) if
            u in post.users and
            t in post.tags and
            u in t.users;
    """)

    posts = session.query(Post).filter(
        authorize_model(oso, "user", "read", session, Post))

    # TODO (dhatch): Add query in here when this works.
    assert_query_equals(posts, "")

    assert posts.count() == 4
示例#12
0
def test_partial_isa_with_path(session, oso,
                               tag_nested_many_many_test_fixture):
    oso.load_str("""
            allow(_, _, post: Post) if check_user(post.created_by);
            # User is not a tag.
            check_user(user: Tag) if user.username = "******";
            check_user(user: User) if user.username = "******";
        """)

    user = tag_nested_many_many_test_fixture["user"]
    posts = session.query(Post).filter(
        authorize_model(oso, user, "read", session, Post))
    # Should only get posts created by user.
    posts = posts.all()
    for post in posts:
        assert post.created_by.username == "user"

    assert len(posts) == 4
示例#13
0
def test_in_multiple_attribute_relationship(session, oso, tag_test_fixture):
    oso.load_str("""
        allow(user, "read", post: Post) if post.access_level = "public";
        allow(user, "read", post: Post) if post.access_level = "private" and post.created_by = user;
        allow(user, "read", post: Post) if
            tag in post.tags and
            0 < post.id and
            (tag.is_public = true or tag.name = "foo");
    """)

    posts = session.query(Post).filter(
        authorize_model(oso, tag_test_fixture["user"], "read", session, Post))

    assert tag_test_fixture["user_public_post"] in posts
    assert tag_test_fixture["user_private_post"] in posts
    assert tag_test_fixture["other_user_public_post"] in posts
    assert not tag_test_fixture["other_user_private_post"] in posts
    assert tag_test_fixture["other_user_random_post"] in posts
    assert tag_test_fixture["other_user_foo_post"] in posts
    assert posts.count() == 5
示例#14
0
def test_in_with_constraints_but_no_matching_objects(
        session, oso, tag_nested_many_many_test_fixture):
    oso.load_str("""
        allow(_, "read", post: Post) if
            tag in post.tags and
            tag.name = "bloop";
    """)
    user = tag_nested_many_many_test_fixture["user"]
    posts = session.query(Post).filter(
        authorize_model(oso, user, "read", session, Post))
    assert str(posts) == (
        "SELECT posts.id AS posts_id, posts.contents AS posts_contents, posts.access_level AS posts_access_level,"
        +
        " posts.created_by_id AS posts_created_by_id, posts.needs_moderation AS posts_needs_moderation"
        + " \nFROM posts" + " \nWHERE (EXISTS (SELECT 1" +
        " \nFROM post_tags, tags" +
        " \nWHERE posts.id = post_tags.post_id AND tags.name = post_tags.tag_id AND tags.name = ?))"
    )
    posts = posts.all()
    assert len(posts) == 0
示例#15
0
def test_nested_relationship_many_many_constrained(
        session, oso, tag_nested_many_many_test_fixture):
    """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(_, "read", post: Post) if tag in post.tags and user in tag.users and
        user.username = "******";
    """)

    posts = session.query(Post).filter(
        authorize_model(oso, tag_nested_many_many_test_fixture["user"], "read",
                        session, Post))
    assert tag_nested_many_many_test_fixture["user_eng_post"] in posts
    assert tag_nested_many_many_test_fixture["user_user_post"] in posts
    assert not tag_nested_many_many_test_fixture["random_post"] in posts
    assert not tag_nested_many_many_test_fixture["not_tagged_post"] in posts
    assert tag_nested_many_many_test_fixture["all_tagged_post"] in posts
示例#16
0
def test_deeply_nested_in(session, oso, tag_nested_many_many_test_fixture):
    oso.load_str("""
        allow(_, _, post: 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;
    """)

    posts = session.query(Post).filter(
        authorize_model(oso, "user", "read", session, Post))

    query_str = """
        SELECT posts.id AS posts_id, posts.contents AS posts_contents, posts.access_level AS
        posts_access_level, posts.created_by_id AS posts_created_by_id, posts.needs_moderation AS
        posts_needs_moderation
        FROM posts
        WHERE (EXISTS (SELECT 1
        FROM users
        WHERE users.id = posts.created_by_id AND (EXISTS (SELECT 1
        FROM posts
        WHERE users.id = posts.created_by_id AND posts.id > ? AND (EXISTS (SELECT 1
        FROM users
        WHERE users.id = posts.created_by_id AND (EXISTS (SELECT 1
        FROM posts
        WHERE users.id = posts.created_by_id AND posts.id > ? AND (EXISTS (SELECT 1
        FROM users
        WHERE users.id = posts.created_by_id AND (EXISTS (SELECT 1
        FROM posts
        WHERE users.id = posts.created_by_id AND posts.id > ?)))))))))))) AND (EXISTS (SELECT 1
        FROM users
        WHERE users.id = posts.created_by_id AND (EXISTS (SELECT 1
        FROM posts
        WHERE users.id = posts.created_by_id)))) AND posts.id > ? AND posts.id =
    """

    assert_query_equals(posts, query_str)
    assert posts.count() == 1
示例#17
0
def test_nested_relationship_many_many_many_constrained(session, engine, oso):
    """Test that nested relationships work.

    post - (many) -> tags - (many) -> category - (many) -> User
    """
    foo = User(username="******")
    bar = User(username="******")

    foo_category = Category(name="foo_category", users=[foo])
    bar_category = Category(name="bar_category", users=[bar])
    both_category = Category(name="both_category", users=[foo, bar])
    public_category = Category(name="public", users=[foo, bar])

    foo_tag = Tag(name="foo", categories=[foo_category])
    bar_tag = Tag(name="bar", categories=[bar_category])
    both_tag = Tag(
        name="both",
        categories=[foo_category, bar_category, public_category],
        is_public=True,
    )

    foo_post = Post(contents="foo_post", tags=[foo_tag])
    bar_post = Post(contents="bar_post", tags=[bar_tag])
    both_post = Post(contents="both_post", tags=[both_tag])
    none_post = Post(contents="none_post", tags=[])
    foo_post_2 = Post(contents="foo_post_2", tags=[foo_tag])
    public_post = Post(contents="public_post",
                       tags=[both_tag],
                       access_level="public")

    session.add_all([
        foo,
        bar,
        foo_category,
        bar_category,
        both_category,
        foo_tag,
        bar_tag,
        both_tag,
        foo_post,
        bar_post,
        both_post,
        none_post,
        foo_post_2,
        public_category,
        public_post,
    ])
    session.commit()

    # A user can read a post that they are the moderator of the category of.
    oso.load_str("""
        allow(user, "read", post: Post) if
            tag in post.tags and
            category in tag.categories and
            moderator in category.users
            and moderator = user;
    """)

    posts = session.query(Post).filter(
        authorize_model(oso, foo, "read", session, Post))
    posts = posts.all()

    assert foo_post in posts
    assert both_post in posts
    assert public_post in posts
    assert foo_post_2 in posts
    assert bar_post not in posts
    assert len(posts) == 4

    posts = session.query(Post).filter(
        authorize_model(oso, bar, "read", session, Post))
    posts = posts.all()

    assert bar_post in posts
    assert both_post in posts
    assert public_post in posts
    assert foo_post not in posts
    assert foo_post_2 not in posts
    assert len(posts) == 3

    # A user can read a post that they are the moderator of the category of if the
    # tag is public.
    oso.load_str("""
        allow(user, "read_2", post: Post) if
            tag in post.tags and
            tag.is_public = true and
            category in tag.categories and
            moderator in category.users
            and moderator = user;
    """)

    posts = session.query(Post).filter(
        authorize_model(oso, bar, "read_2", session, Post))

    posts = posts.all()

    # Only the both tag is public.
    assert both_post in posts
    assert public_post in posts
    assert bar_post not in posts
    assert foo_post not in posts
    assert foo_post_2 not in posts
    assert len(posts) == 2

    # A user can read a post that they are the moderator of the category of if the
    # tag is public and the category name is public.
    oso.load_str("""
        allow(user, "read_3", post: Post) if
            post.access_level = "public" and
            tag in post.tags and
            tag.is_public = true and
            category in tag.categories and
            category.name = "public" and
            moderator in category.users and
            moderator = user;
    """)

    posts = session.query(Post).filter(
        authorize_model(oso, bar, "read_3", session, Post))
    print_query(posts)
    posts = posts.all()

    # Only the both tag is public but the category name is not correct.
    assert len(posts) == 1