Repository: CTFd/CTFd

Issue:
Hidden scores, hides graphs for admins as well
Hidden scores, hides graphs for admins as well. 


Discussion:

Resolving patch:
diff --git a/CTFd/scoreboard.py b/CTFd/scoreboard.py
--- a/CTFd/scoreboard.py
+++ b/CTFd/scoreboard.py
@@ -2,9 +2,11 @@
 
 from CTFd.cache import cache, make_cache_key
 from CTFd.utils import config
+from CTFd.utils.config.visibility import scores_visible
 from CTFd.utils.decorators.visibility import check_score_visibility
 from CTFd.utils.helpers import get_infos
 from CTFd.utils.scores import get_standings
+from CTFd.utils.user import is_admin
 
 scoreboard = Blueprint("scoreboard", __name__)
 
@@ -18,5 +20,8 @@ def listing():
     if config.is_scoreboard_frozen():
         infos.append("Scoreboard has been frozen")
 
+    if is_admin() is True and scores_visible() is False:
+        infos.append("Scores are not currently visible to users")
+
     standings = get_standings()
     return render_template("scoreboard.html", standings=standings, infos=infos)
diff --git a/CTFd/utils/decorators/visibility.py b/CTFd/utils/decorators/visibility.py
--- a/CTFd/utils/decorators/visibility.py
+++ b/CTFd/utils/decorators/visibility.py
@@ -30,10 +30,18 @@ def _check_score_visibility(*args, **kwargs):
                     return redirect(url_for("auth.login", next=request.full_path))
 
         elif v == ScoreVisibilityTypes.HIDDEN:
-            return (
-                render_template("errors/403.html", error="Scores are currently hidden"),
-                403,
-            )
+            if is_admin():
+                return f(*args, **kwargs)
+            else:
+                if request.content_type == "application/json":
+                    abort(403)
+                else:
+                    return (
+                        render_template(
+                            "errors/403.html", error="Scores are currently hidden"
+                        ),
+                        403,
+                    )
 
         elif v == ScoreVisibilityTypes.ADMINS:
             if is_admin():


Test patch:
diff --git a/tests/admin/test_scoreboard.py b/tests/admin/test_scoreboard.py
--- a/tests/admin/test_scoreboard.py
+++ b/tests/admin/test_scoreboard.py
@@ -0,0 +1,54 @@
+from CTFd.models import Users
+from CTFd.utils import set_config
+from tests.helpers import (
+    create_ctfd,
+    destroy_ctfd,
+    login_as_user,
+    register_user,
+    simulate_user_activity,
+)
+
+
+def test_admins_can_see_scores_with_hidden_scores():
+    """Test that admins can see user scores when Score Visibility is set to hidden"""
+    app = create_ctfd()
+    with app.app_context():
+        register_user(app)
+        user = Users.query.filter_by(id=2).first()
+        simulate_user_activity(app.db, user=user)
+
+        admin = login_as_user(app, name="admin", password="password")
+        user = login_as_user(app)
+
+        set_config("score_visibility", "hidden")
+
+        # Users can see their own data
+        r = user.get("/api/v1/users/me/fails", json="")
+        assert r.status_code == 200
+        r = user.get("/api/v1/users/me/solves", json="")
+        assert r.status_code == 200
+
+        # Users cannot see public data
+        r = user.get("/api/v1/users/2/solves", json="")
+        assert r.status_code == 403
+        r = user.get("/api/v1/users/2/fails", json="")
+        assert r.status_code == 403
+        r = user.get("/scoreboard")
+        assert r.status_code == 403
+        r = user.get("/api/v1/scoreboard", json="")
+        assert r.status_code == 403
+
+        # Admins can see user data
+        r = admin.get("/api/v1/users/2/fails", json="")
+        assert r.status_code != 403
+
+        # Admins can see the scoreboard
+        r = admin.get("/scoreboard")
+        assert r.status_code != 403
+        assert "Scores are not currently visible to users" in r.get_data(as_text=True)
+
+        # Admins can see the scoreboard
+        r = admin.get("/api/v1/scoreboard", json="")
+        assert r.status_code != 403
+
+    destroy_ctfd(app)