1423 model filter bypass (#1451)

* Add `view=admin` GET param to `/api/v1/users`, `/api/v1/teams`, and `/api/v1/challenges` to bypass filtering for admins
* Closes #1423 #1445
* Related to #1165
This commit is contained in:
Kevin Chung
2020-05-29 11:06:04 -04:00
committed by GitHub
parent 970e1ca65e
commit 50f75be5eb
6 changed files with 76 additions and 23 deletions

View File

@@ -47,31 +47,36 @@ class ChallengeList(Resource):
# This can return None (unauth) if visibility is set to public # This can return None (unauth) if visibility is set to public
user = get_current_user() user = get_current_user()
challenges = ( # Admins can request to see everything
Challenges.query.filter( if is_admin() and request.args.get("view") == "admin":
and_(Challenges.state != "hidden", Challenges.state != "locked") challenges = Challenges.query.order_by(Challenges.value).all()
) solve_ids = set([challenge.id for challenge in challenges])
.order_by(Challenges.value) else:
.all() challenges = (
) Challenges.query.filter(
and_(Challenges.state != "hidden", Challenges.state != "locked")
if user: )
solve_ids = ( .order_by(Challenges.value)
Solves.query.with_entities(Solves.challenge_id)
.filter_by(account_id=user.account_id)
.order_by(Solves.challenge_id.asc())
.all() .all()
) )
solve_ids = set([value for value, in solve_ids])
# TODO: Convert this into a re-useable decorator if user:
if is_admin(): solve_ids = (
pass Solves.query.with_entities(Solves.challenge_id)
.filter_by(account_id=user.account_id)
.order_by(Solves.challenge_id.asc())
.all()
)
solve_ids = set([value for value, in solve_ids])
# TODO: Convert this into a re-useable decorator
if is_admin():
pass
else:
if config.is_teams_mode() and get_current_team() is None:
abort(403)
else: else:
if config.is_teams_mode() and get_current_team() is None: solve_ids = set()
abort(403)
else:
solve_ids = set()
response = [] response = []
tag_schema = TagSchema(view="user", many=True) tag_schema = TagSchema(view="user", many=True)

View File

@@ -22,7 +22,11 @@ teams_namespace = Namespace("teams", description="Endpoint to retrieve Teams")
class TeamList(Resource): class TeamList(Resource):
@check_account_visibility @check_account_visibility
def get(self): def get(self):
teams = Teams.query.filter_by(hidden=False, banned=False) if is_admin() and request.args.get("view") == "admin":
teams = Teams.query.filter_by()
else:
teams = Teams.query.filter_by(hidden=False, banned=False)
user_type = get_current_user_type(fallback="user") user_type = get_current_user_type(fallback="user")
view = copy.deepcopy(TeamSchema.views.get(user_type)) view = copy.deepcopy(TeamSchema.views.get(user_type))
view.remove("members") view.remove("members")

View File

@@ -31,7 +31,11 @@ users_namespace = Namespace("users", description="Endpoint to retrieve Users")
class UserList(Resource): class UserList(Resource):
@check_account_visibility @check_account_visibility
def get(self): def get(self):
users = Users.query.filter_by(banned=False, hidden=False) if is_admin() and request.args.get("view") == "admin":
users = Users.query.filter_by()
else:
users = Users.query.filter_by(banned=False, hidden=False)
response = UserSchema(view="user", many=True).dump(users) response = UserSchema(view="user", many=True).dump(users)
if response.errors: if response.errors:

View File

@@ -135,6 +135,25 @@ def test_api_challenges_get_admin():
destroy_ctfd(app) destroy_ctfd(app)
def test_api_challenges_get_hidden_admin():
"""Can an admin see hidden challenges in API list response"""
app = create_ctfd()
with app.app_context():
gen_challenge(app.db, state="hidden")
gen_challenge(app.db)
with login_as_user(app, "admin") as admin:
challenges_list = admin.get("/api/v1/challenges", json="").get_json()[
"data"
]
assert len(challenges_list) == 1
challenges_list = admin.get(
"/api/v1/challenges?view=admin", json=""
).get_json()["data"]
assert len(challenges_list) == 2
destroy_ctfd(app)
def test_api_challenges_post_admin(): def test_api_challenges_post_admin():
"""Can a user post /api/v1/challenges if admin""" """Can a user post /api/v1/challenges if admin"""
app = create_ctfd() app = create_ctfd()

View File

@@ -696,6 +696,9 @@ def test_api_accessing_hidden_banned_users():
app.db.session.commit() app.db.session.commit()
with login_as_user(app, name="visible_user") as client: with login_as_user(app, name="visible_user") as client:
list_teams = client.get("/api/v1/teams").get_json()["data"]
assert len(list_teams) == 0
assert client.get("/api/v1/teams/1").status_code == 404 assert client.get("/api/v1/teams/1").status_code == 404
assert client.get("/api/v1/teams/1/solves").status_code == 404 assert client.get("/api/v1/teams/1/solves").status_code == 404
assert client.get("/api/v1/teams/1/fails").status_code == 404 assert client.get("/api/v1/teams/1/fails").status_code == 404
@@ -707,6 +710,10 @@ def test_api_accessing_hidden_banned_users():
assert client.get("/api/v1/teams/2/awards").status_code == 404 assert client.get("/api/v1/teams/2/awards").status_code == 404
with login_as_user(app, name="admin") as client: with login_as_user(app, name="admin") as client:
# Admins see hidden teams in lists
list_users = client.get("/api/v1/teams?view=admin").get_json()["data"]
assert len(list_users) == 2
assert client.get("/api/v1/teams/1").status_code == 200 assert client.get("/api/v1/teams/1").status_code == 200
assert client.get("/api/v1/teams/1/solves").status_code == 200 assert client.get("/api/v1/teams/1/solves").status_code == 200
assert client.get("/api/v1/teams/1/fails").status_code == 200 assert client.get("/api/v1/teams/1/fails").status_code == 200

View File

@@ -764,12 +764,19 @@ def test_api_accessing_hidden_users():
app.db.session.commit() app.db.session.commit()
with login_as_user(app, name="visible_user") as client: with login_as_user(app, name="visible_user") as client:
list_users = client.get("/api/v1/users").get_json()["data"]
assert len(list_users) == 1
assert client.get("/api/v1/users/3").status_code == 404 assert client.get("/api/v1/users/3").status_code == 404
assert client.get("/api/v1/users/3/solves").status_code == 404 assert client.get("/api/v1/users/3/solves").status_code == 404
assert client.get("/api/v1/users/3/fails").status_code == 404 assert client.get("/api/v1/users/3/fails").status_code == 404
assert client.get("/api/v1/users/3/awards").status_code == 404 assert client.get("/api/v1/users/3/awards").status_code == 404
with login_as_user(app, name="admin") as client: with login_as_user(app, name="admin") as client:
# Admins see the user in lists
list_users = client.get("/api/v1/users?view=admin").get_json()["data"]
assert len(list_users) == 3
assert client.get("/api/v1/users/3").status_code == 200 assert client.get("/api/v1/users/3").status_code == 200
assert client.get("/api/v1/users/3/solves").status_code == 200 assert client.get("/api/v1/users/3/solves").status_code == 200
assert client.get("/api/v1/users/3/fails").status_code == 200 assert client.get("/api/v1/users/3/fails").status_code == 200
@@ -788,12 +795,19 @@ def test_api_accessing_banned_users():
app.db.session.commit() app.db.session.commit()
with login_as_user(app, name="visible_user") as client: with login_as_user(app, name="visible_user") as client:
list_users = client.get("/api/v1/users").get_json()["data"]
assert len(list_users) == 1
assert client.get("/api/v1/users/3").status_code == 404 assert client.get("/api/v1/users/3").status_code == 404
assert client.get("/api/v1/users/3/solves").status_code == 404 assert client.get("/api/v1/users/3/solves").status_code == 404
assert client.get("/api/v1/users/3/fails").status_code == 404 assert client.get("/api/v1/users/3/fails").status_code == 404
assert client.get("/api/v1/users/3/awards").status_code == 404 assert client.get("/api/v1/users/3/awards").status_code == 404
with login_as_user(app, name="admin") as client: with login_as_user(app, name="admin") as client:
# Admins see the user in lists
list_users = client.get("/api/v1/users?view=admin").get_json()["data"]
assert len(list_users) == 3
assert client.get("/api/v1/users/3").status_code == 200 assert client.get("/api/v1/users/3").status_code == 200
assert client.get("/api/v1/users/3/solves").status_code == 200 assert client.get("/api/v1/users/3/solves").status_code == 200
assert client.get("/api/v1/users/3/fails").status_code == 200 assert client.get("/api/v1/users/3/fails").status_code == 200