Repository: CTFd/CTFd

Issue:
Make IP List be a Tab
I feel like the IP addresses shown at the bottom of the admin Users page should be in a tab like the rest of the data on that page. It feels out of place how it is displayed currently.



Discussion:
Hmm I don't really agree with a tab. It's not really challenge data or something that would  change the score. 

Maybe it would be better as a modal. 
That might work out better.  Just needs something because it looks out of place where it is currently.
@jimender2 perhaps you could mock up an example of where the IP data might go? 

Resolving patch:
diff --git a/CTFd/cache/__init__.py b/CTFd/cache/__init__.py
--- a/CTFd/cache/__init__.py
+++ b/CTFd/cache/__init__.py
@@ -46,10 +46,10 @@ def clear_pages():
     cache.delete_memoized(get_page)
 
 
-def clear_user_ips(user_id):
-    from CTFd.utils.user import get_user_ips
+def clear_user_recent_ips(user_id):
+    from CTFd.utils.user import get_user_recent_ips
 
-    cache.delete_memoized(get_user_ips, user_id=user_id)
+    cache.delete_memoized(get_user_recent_ips, user_id=user_id)
 
 
 def clear_user_session(user_id):
diff --git a/CTFd/utils/initialization/__init__.py b/CTFd/utils/initialization/__init__.py
--- a/CTFd/utils/initialization/__init__.py
+++ b/CTFd/utils/initialization/__init__.py
@@ -7,7 +7,7 @@
 from sqlalchemy.exc import IntegrityError, InvalidRequestError
 from werkzeug.wsgi import DispatcherMiddleware
 
-from CTFd.cache import clear_user_ips
+from CTFd.cache import clear_user_recent_ips
 from CTFd.exceptions import UserNotFoundException, UserTokenExpiredException
 from CTFd.models import Tracking, db
 from CTFd.utils import config, get_config, markdown
@@ -42,7 +42,7 @@
 from CTFd.utils.user import (
     authed,
     get_current_user_attrs,
-    get_current_user_ips,
+    get_current_user_recent_ips,
     get_current_team_attrs,
     get_ip,
     is_admin,
@@ -181,18 +181,20 @@ def tracker():
             return
 
         if authed():
-            user_ips = get_current_user_ips()
+            user_ips = get_current_user_recent_ips()
             ip = get_ip()
+
             track = None
-            if ip not in user_ips:
-                track = Tracking(ip=get_ip(), user_id=session["id"])
-                db.session.add(track)
-            else:
-                if request.method != "GET":
-                    track = Tracking.query.filter_by(
-                        ip=get_ip(), user_id=session["id"]
-                    ).first()
+            if (ip not in user_ips) or (request.method != "GET"):
+                track = Tracking.query.filter_by(
+                    ip=get_ip(), user_id=session["id"]
+                ).first()
+
+                if track:
                     track.date = datetime.datetime.utcnow()
+                else:
+                    track = Tracking(ip=get_ip(), user_id=session["id"])
+                    db.session.add(track)
 
             if track:
                 try:
@@ -200,7 +202,7 @@ def tracker():
                 except (InvalidRequestError, IntegrityError):
                     db.session.rollback()
                     logout_user()
-                clear_user_ips(user_id=session["id"])
+                clear_user_recent_ips(user_id=session["id"])
 
     @app.before_request
     def banned():
diff --git a/CTFd/utils/user/__init__.py b/CTFd/utils/user/__init__.py
--- a/CTFd/utils/user/__init__.py
+++ b/CTFd/utils/user/__init__.py
@@ -120,21 +120,22 @@ def get_ip(req=None):
     return remote_addr
 
 
-def get_current_user_ips():
+def get_current_user_recent_ips():
     if authed():
-        return get_user_ips(user_id=session["id"])
+        return get_user_recent_ips(user_id=session["id"])
     else:
         return None
 
 
 @cache.memoize(timeout=60)
-def get_user_ips(user_id):
+def get_user_recent_ips(user_id):
+    hour_ago = datetime.datetime.now() - datetime.timedelta(hours=1)
     addrs = (
         Tracking.query.with_entities(Tracking.ip.distinct())
-        .filter_by(user_id=user_id)
+        .filter(Tracking.user_id == user_id, Tracking.date >= hour_ago)
         .all()
     )
-    return [ip for ip, in addrs]
+    return set([ip for (ip,) in addrs])
 
 
 def get_wrong_submissions_per_minute(account_id):


Test patch: