diff --git a/CTFd/api/v1/challenges.py b/CTFd/api/v1/challenges.py
index 521f83fd..d9eb6f2b 100644
--- a/CTFd/api/v1/challenges.py
+++ b/CTFd/api/v1/challenges.py
@@ -1,4 +1,4 @@
-from flask import session, request, abort
+from flask import session, request, abort, url_for
from flask_restplus import Namespace, Resource
from CTFd.models import (
db,
@@ -29,11 +29,11 @@ from CTFd.cache import cache, clear_standings
from CTFd.utils.scores import get_standings
from CTFd.utils.config.visibility import scores_visible, accounts_visible, challenges_visible
from CTFd.utils.user import get_current_user, is_admin, authed
-from CTFd.utils.modes import get_model
+from CTFd.utils.modes import get_model, USERS_MODE, TEAMS_MODE
from CTFd.schemas.tags import TagSchema
from CTFd.schemas.hints import HintSchema
from CTFd.schemas.flags import FlagSchema
-from CTFd.utils import config
+from CTFd.utils import config, get_config
from CTFd.utils import user as current_user
from CTFd.utils.user import get_current_team
from CTFd.utils.user import get_current_user
@@ -490,11 +490,20 @@ class ChallengeSolves(Resource):
.filter(Solves.challenge_id == challenge_id, Model.banned == False, Model.hidden == False)\
.order_by(Solves.date.asc())
+ endpoint = None
+ if get_config('user_mode') == TEAMS_MODE:
+ endpoint = 'teams.public'
+ arg = 'team_id'
+ elif get_config('user_mode') == USERS_MODE:
+ endpoint = 'users.public'
+ arg = 'user_id'
+
for solve in solves:
response.append({
'account_id': solve.account_id,
'name': solve.account.name,
- 'date': isoformat(solve.date)
+ 'date': isoformat(solve.date),
+ 'account_url': url_for(endpoint, **{arg: solve.account_id})
})
return {
diff --git a/CTFd/themes/core/static/js/challenges.js b/CTFd/themes/core/static/js/challenges.js
index ad39a326..c46a41f0 100644
--- a/CTFd/themes/core/static/js/challenges.js
+++ b/CTFd/themes/core/static/js/challenges.js
@@ -220,7 +220,8 @@ function getsolves(id) {
var id = data[i].account_id;
var name = data[i].name;
var date = moment(data[i].date).local().fromNow();
- box.append('
| {1} | {2} |
'.format(id, htmlentities(name), date));
+ var account_url = data[i].account_url
+ box.append('| {2} | {3} |
'.format(account_url, id, htmlentities(name), date));
}
});
}
diff --git a/tests/api/v1/test_challenges.py b/tests/api/v1/test_challenges.py
index b644e366..623340c1 100644
--- a/tests/api/v1/test_challenges.py
+++ b/tests/api/v1/test_challenges.py
@@ -431,6 +431,61 @@ def test_api_challenge_get_solves_404():
destroy_ctfd(app)
+def test_api_challenge_solves_returns_correct_data():
+ """Test that /api/v1//solves returns expected data"""
+ app = create_ctfd()
+ with app.app_context():
+ register_user(app)
+ client = login_as_user(app)
+ chal = gen_challenge(app.db)
+ gen_solve(app.db, user_id=2, challenge_id=chal.id)
+ r = client.get('/api/v1/challenges/1/solves')
+ resp = r.get_json()['data']
+ solve = resp[0]
+ assert r.status_code == 200
+ assert solve.get('account_id') == 2
+ assert solve.get('name') == 'user'
+ assert solve.get('date') is not None
+ assert solve.get('account_url') == '/users/2'
+ destroy_ctfd(app)
+
+ app = create_ctfd(user_mode="teams")
+ with app.app_context():
+ register_user(app)
+ client = login_as_user(app)
+ team = gen_team(app.db)
+ user = Users.query.filter_by(id=2).first()
+ user.team_id = team.id
+ app.db.session.commit()
+ chal = gen_challenge(app.db)
+ gen_solve(app.db, user_id=2, team_id=1, challenge_id=chal.id)
+ r = client.get('/api/v1/challenges/1/solves')
+ resp = r.get_json()['data']
+ solve = resp[0]
+ assert r.status_code == 200
+ assert solve.get('account_id') == 1
+ assert solve.get('name') == 'team_name'
+ assert solve.get('date') is not None
+ assert solve.get('account_url') == '/teams/1'
+ destroy_ctfd(app)
+
+ app = create_ctfd(application_root='/ctf')
+ with app.app_context():
+ register_user(app)
+ client = login_as_user(app)
+ chal = gen_challenge(app.db)
+ gen_solve(app.db, user_id=2, challenge_id=chal.id)
+ r = client.get('/api/v1/challenges/1/solves')
+ resp = r.get_json()['data']
+ solve = resp[0]
+ assert r.status_code == 200
+ assert solve.get('account_id') == 2
+ assert solve.get('name') == 'user'
+ assert solve.get('date') is not None
+ assert solve.get('account_url') == '/ctf/users/2'
+ destroy_ctfd(app)
+
+
def test_api_challenge_get_files_non_admin():
"""Can a user get /api/v1/challenges//files if not admin"""
app = create_ctfd()