Repository: CTFd/CTFd

Issue:
Feature Request : Search users by IP address
It's really hard to search an IP address on the UI now.

It'd be nice to have a search feature in `admin/users` search a user by an IP address. CTFd already logs IP addresses of each login so it'll be very useful when wanting to ban certain users for abusing rules.



Discussion:
I'll try to implement this if this sounds good to you @ColdHeat :)

Resolving patch:
diff --git a/CTFd/admin/teams.py b/CTFd/admin/teams.py
--- a/CTFd/admin/teams.py
+++ b/CTFd/admin/teams.py
@@ -43,7 +43,7 @@ def teams_listing():
         return render_template(
             "admin/teams/teams.html",
             teams=teams,
-            pages=None,
+            pages=0,
             curr_page=None,
             q=q,
             field=field,
diff --git a/CTFd/admin/users.py b/CTFd/admin/users.py
--- a/CTFd/admin/users.py
+++ b/CTFd/admin/users.py
@@ -42,10 +42,18 @@ def users_listing():
                 .order_by(Users.id.asc())
                 .all()
             )
+        elif field == "ip":
+            users = (
+                Users.query.join(Tracking, Users.id == Tracking.user_id)
+                .filter(Tracking.ip.like("%{}%".format(q)))
+                .order_by(Users.id.asc())
+                .all()
+            )
+
         return render_template(
             "admin/users/users.html",
             users=users,
-            pages=None,
+            pages=0,
             curr_page=None,
             q=q,
             field=field,


Test patch:
diff --git a/tests/admin/test_users.py b/tests/admin/test_users.py
--- a/tests/admin/test_users.py
+++ b/tests/admin/test_users.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from tests.helpers import (
+    create_ctfd,
+    destroy_ctfd,
+    login_as_user,
+    gen_user,
+    gen_tracking,
+)
+
+
+def test_admin_user_ip_search():
+    """Can an admin search user IPs"""
+    app = create_ctfd()
+    with app.app_context():
+        u1 = gen_user(app.db, name="user1", email="user1@ctfd.io")
+        gen_tracking(app.db, user_id=u1.id, ip="1.1.1.1")
+
+        u2 = gen_user(app.db, name="user2", email="user2@ctfd.io")
+        gen_tracking(app.db, user_id=u2.id, ip="2.2.2.2")
+
+        u3 = gen_user(app.db, name="user3", email="user3@ctfd.io")
+        gen_tracking(app.db, user_id=u3.id, ip="3.3.3.3")
+
+        u4 = gen_user(app.db, name="user4", email="user4@ctfd.io")
+        gen_tracking(app.db, user_id=u4.id, ip="3.3.3.3")
+        gen_tracking(app.db, user_id=u4.id, ip="4.4.4.4")
+
+        with login_as_user(app, name="admin", password="password") as admin:
+            r = admin.get("/admin/users?field=ip&q=1.1.1.1")
+            resp = r.get_data(as_text=True)
+            assert "user1" in resp
+            assert "user2" not in resp
+            assert "user3" not in resp
+
+            r = admin.get("/admin/users?field=ip&q=2.2.2.2")
+            resp = r.get_data(as_text=True)
+            assert "user1" not in resp
+            assert "user2" in resp
+            assert "user3" not in resp
+
+            r = admin.get("/admin/users?field=ip&q=3.3.3.3")
+            resp = r.get_data(as_text=True)
+            assert "user1" not in resp
+            assert "user2" not in resp
+            assert "user3" in resp
+            assert "user4" in resp
+    destroy_ctfd(app)