blob: a496616697e31736b67009bca7f8fbf40f31acc2 [file]
import pytest
from casbin.enforcer import Enforcer
from flask import request, jsonify
from casbin_sqlalchemy_adapter import Adapter
from casbin_sqlalchemy_adapter import Base
from casbin_sqlalchemy_adapter import CasbinRule
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from flask_authz import CasbinEnforcer
def enforcer_partial():
engine = create_engine("sqlite://")
adapter = Adapter(engine)
session = sessionmaker(bind=engine)
Base.metadata.create_all(engine)
s = session()
s.query(CasbinRule).delete()
s.add(CasbinRule(ptype="p", v0="alice", v1="/item", v2="GET"))
s.add(CasbinRule(ptype="p", v0="bob", v1="/item", v2="GET"))
s.add(CasbinRule(ptype="p", v0="data2_admin", v1="/item", v2="POST"))
s.add(CasbinRule(ptype="p", v0="data2_admin", v1="/item", v2="DELETE"))
s.add(CasbinRule(ptype="p", v0="data2_admin", v1="/item", v2="GET"))
s.add(CasbinRule(ptype="g", v0="alice", v1="data2_admin"))
s.add(CasbinRule(ptype="g", v0="users", v1="data2_admin"))
s.commit()
s.close()
return CasbinEnforcer(adapter=adapter)
@pytest.fixture
def enforcer(app_fixture):
e = enforcer_partial()
e.init_app(app_fixture)
yield e
@pytest.fixture
def watcher():
class SomeWatcher:
def should_reload(self):
return True
def update_callback(self):
pass
yield SomeWatcher
@pytest.mark.parametrize(
"header, user, method, status, user_name",
[
("X-User", "alice", "GET", 200, "X-User"),
("X-USER", "alice", "GET", 200, "x-user"),
("x-user", "alice", "GET", 200, "X-USER"),
("X-User", "alice", "GET", 200, "X-USER"),
("X-User", "alice", "GET", 200, "X-Not-A-Header"),
("X-User", "alice", "POST", 201, None),
("X-User", "alice", "DELETE", 202, None),
("X-User", "bob", "GET", 200, None),
("X-User", "bob", "POST", 401, None),
("X-User", "bob", "DELETE", 401, None),
("X-Idp-Groups", "admin", "GET", 401, "X-User"),
("X-Idp-Groups", "users", "GET", 200, None),
("X-Idp-Groups", "noexist,testnoexist,users", "GET", 200, None),
# ("X-Idp-Groups", "noexist testnoexist users", "GET", 200, None),
("X-Idp-Groups", "noexist, testnoexist, users", "GET", 200, None),
("Authorization", "Basic Ym9iOnBhc3N3b3Jk", "GET", 200, "Authorization"),
("Authorization", "Unsupported Ym9iOnBhc3N3b3Jk", "GET", 401, None),
],
)
def test_enforcer(app_fixture, enforcer, header, user, method, status, user_name):
# enable auditing with user name
if user_name:
enforcer.user_name_headers = {user_name}
@app_fixture.route("/")
@enforcer.enforcer
def index():
return jsonify({"message": "passed"}), 200
@app_fixture.route("/item", methods=["GET", "POST", "DELETE"])
@enforcer.enforcer
def item():
if request.method == "GET":
return jsonify({"message": "passed"}), 200
elif request.method == "POST":
return jsonify({"message": "passed"}), 201
elif request.method == "DELETE":
return jsonify({"message": "passed"}), 202
headers = {header: user}
c = app_fixture.test_client()
# c.post('/add', data=dict(title='2nd Item', text='The text'))
rv = c.get("/")
assert rv.status_code == 401
caller = getattr(c, method.lower())
rv = caller("/item", headers=headers)
assert rv.status_code == status
@pytest.mark.parametrize(
"header, user, method, status",
[
("X-User", "alice", "GET", 200),
("X-User", "alice", "POST", 201),
("X-User", "alice", "DELETE", 202),
("X-User", "bob", "GET", 200),
("X-User", "bob", "POST", 401),
("X-User", "bob", "DELETE", 401),
("X-Idp-Groups", "admin", "GET", 401),
("X-Idp-Groups", "users", "GET", 200),
("Authorization", "Basic Ym9iOnBhc3N3b3Jk", "GET", 200),
("Authorization", "Unsupported Ym9iOnBhc3N3b3Jk", "GET", 401),
],
)
def test_enforcer_with_watcher(
app_fixture, enforcer, header, user, method, status, watcher
):
enforcer.set_watcher(watcher())
@app_fixture.route("/")
@enforcer.enforcer
def index():
return jsonify({"message": "passed"}), 200
@app_fixture.route("/item", methods=["GET", "POST", "DELETE"])
@enforcer.enforcer
def item():
if request.method == "GET":
return jsonify({"message": "passed"}), 200
elif request.method == "POST":
return jsonify({"message": "passed"}), 201
elif request.method == "DELETE":
return jsonify({"message": "passed"}), 202
headers = {header: user}
c = app_fixture.test_client()
# c.post('/add', data=dict(title='2nd Item', text='The text'))
rv = c.get("/")
assert rv.status_code == 401
caller = getattr(c, method.lower())
rv = caller("/item", headers=headers)
assert rv.status_code == status
def test_manager(app_fixture, enforcer):
@app_fixture.route("/manager", methods=["POST"])
@enforcer.manager
def manager(manager):
assert isinstance(manager, Enforcer)
return jsonify({"message": "passed"}), 201
c = app_fixture.test_client()
c.post("/manager")
def test_enforcer_set_watcher(enforcer, watcher):
assert enforcer.e.watcher is None
enforcer.set_watcher(watcher())
assert isinstance(enforcer.e.watcher, watcher)
@pytest.mark.parametrize(
"owner, method, status",
[
(["alice"], "GET", 200),
(["alice"], "POST", 201),
(["alice"], "DELETE", 202),
(["bob"], "GET", 200),
(["bob"], "POST", 401),
(["bob"], "DELETE", 401),
(["admin"], "GET", 401),
(["users"], "GET", 200),
(["alice", "bob"], "POST", 201),
(["noexist", "testnoexist"], "POST", 401),
],
)
def test_enforcer_with_owner_loader(app_fixture, enforcer, owner, method, status):
@app_fixture.route("/")
@enforcer.enforcer
def index():
return jsonify({"message": "passed"}), 200
@app_fixture.route("/item", methods=["GET", "POST", "DELETE"])
@enforcer.enforcer
def item():
if request.method == "GET":
return jsonify({"message": "passed"}), 200
elif request.method == "POST":
return jsonify({"message": "passed"}), 201
elif request.method == "DELETE":
return jsonify({"message": "passed"}), 202
@enforcer.owner_loader
def owner_loader():
return owner
c = app_fixture.test_client()
# c.post('/add', data=dict(title='2nd Item', text='The text'))
rv = c.get("/")
assert rv.status_code == 401
caller = getattr(c, method.lower())
rv = caller("/item")
assert rv.status_code == status