mirror of
https://github.com/aljazceru/CTFd.git
synced 2025-12-19 06:54:20 +01:00
Rewrite IP tracking code to only return recent IPs. Move IPs into a modal for admin panel.
This commit is contained in:
6
CTFd/cache/__init__.py
vendored
6
CTFd/cache/__init__.py
vendored
@@ -46,10 +46,10 @@ def clear_pages():
|
|||||||
cache.delete_memoized(get_page)
|
cache.delete_memoized(get_page)
|
||||||
|
|
||||||
|
|
||||||
def clear_user_ips(user_id):
|
def clear_user_recent_ips(user_id):
|
||||||
from CTFd.utils.user import get_user_ips
|
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):
|
def clear_user_session(user_id):
|
||||||
|
|||||||
@@ -221,6 +221,10 @@ $(() => {
|
|||||||
$("#team-award-modal").modal("toggle");
|
$("#team-award-modal").modal("toggle");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$(".addresses-team").click(function(event) {
|
||||||
|
$("#team-addresses-modal").modal("toggle");
|
||||||
|
});
|
||||||
|
|
||||||
$("#user-award-form").submit(function(e) {
|
$("#user-award-form").submit(function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const params = $("#user-award-form").serializeJSON(true);
|
const params = $("#user-award-form").serializeJSON(true);
|
||||||
|
|||||||
@@ -419,6 +419,10 @@ $(() => {
|
|||||||
$("#user-email-modal").modal("toggle");
|
$("#user-email-modal").modal("toggle");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$(".addresses-user").click(function(event) {
|
||||||
|
$("#user-addresses-modal").modal("toggle");
|
||||||
|
});
|
||||||
|
|
||||||
$("#user-mail-form").submit(emailUser);
|
$("#user-mail-form").submit(emailUser);
|
||||||
|
|
||||||
$(".delete-submission").click(deleteUserSubmission);
|
$(".delete-submission").click(deleteUserSubmission);
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
28
CTFd/themes/admin/templates/modals/teams/addresses.html
Normal file
28
CTFd/themes/admin/templates/modals/teams/addresses.html
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td class="text-center"><b>User</b></td>
|
||||||
|
<td class="text-center"><b>IP Address</b></td>
|
||||||
|
<td class="text-center"><b>Last Seen</b></td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for addr in addrs %}
|
||||||
|
<tr>
|
||||||
|
<td class="text-center">
|
||||||
|
<a href="{{ url_for("admin.users_detail", user_id=addr.user_id) }}">
|
||||||
|
{{ addr.user.name }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">{{ addr.ip }}</td>
|
||||||
|
<td class="text-center solve-time">
|
||||||
|
<span data-time="{{ addr.date | isoformat }}"></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
22
CTFd/themes/admin/templates/modals/users/addresses.html
Normal file
22
CTFd/themes/admin/templates/modals/users/addresses.html
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td class="text-center"><b>IP Address</b></td>
|
||||||
|
<td class="text-center"><b>Last Seen</b></td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for addr in addrs %}
|
||||||
|
<tr>
|
||||||
|
<td class="text-center">{{ addr.ip }}</td>
|
||||||
|
<td class="text-center solve-time">
|
||||||
|
<span data-time="{{ addr.date | isoformat }}"></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -61,6 +61,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="team-addresses-modal" class="modal fade">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h2 class="modal-action text-center w-100">IP Addresses</h2>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body clearfix">
|
||||||
|
{% include "admin/modals/teams/addresses.html" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="jumbotron">
|
<div class="jumbotron">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1 id="team-id" class="text-center">{{ team.name }}</h1>
|
<h1 id="team-id" class="text-center">{{ team.name }}</h1>
|
||||||
@@ -109,6 +125,7 @@
|
|||||||
<small>points</small>
|
<small>points</small>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</h3>
|
</h3>
|
||||||
|
<hr class="w-50">
|
||||||
<div class="pt-3">
|
<div class="pt-3">
|
||||||
<a class="edit-team">
|
<a class="edit-team">
|
||||||
<i class="btn-fa fas fa-pencil-alt fa-2x px-2" data-toggle="tooltip" data-placement="top"
|
<i class="btn-fa fas fa-pencil-alt fa-2x px-2" data-toggle="tooltip" data-placement="top"
|
||||||
@@ -126,6 +143,11 @@
|
|||||||
title="Delete Team"></i>
|
title="Delete Team"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="pt-3">
|
||||||
|
<a class="addresses-team">
|
||||||
|
<i class="btn-fa fas fa-network-wired fa-2x px-2" data-toggle="tooltip" data-placement="top" title="IP Addresses"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -356,37 +378,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row pt-5">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<table class="table table-striped">
|
|
||||||
<h3 class="text-center">IP Addresses</h3>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td class="text-center"><b>User</b></td>
|
|
||||||
<td class="text-center"><b>IP Address</b></td>
|
|
||||||
<td class="text-center"><b>Last Seen</b></td>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for addr in addrs %}
|
|
||||||
<tr>
|
|
||||||
<td class="text-center">
|
|
||||||
<a href="{{ url_for("admin.users_detail", user_id=addr.user_id) }}">
|
|
||||||
{{ addr.user.name }}
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td class="text-center">{{ addr.ip }}</td>
|
|
||||||
<td class="text-center solve-time">
|
|
||||||
<span data-time="{{ addr.date | isoformat }}"></span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -52,6 +52,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="user-addresses-modal" class="modal fade">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h2 class="modal-action text-center w-100">IP Addresses</h2>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body clearfix">
|
||||||
|
{% include "admin/modals/users/addresses.html" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="jumbotron">
|
<div class="jumbotron">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1 id="team-id" class="text-center p-0 m-0">{{ user.name }}</h1>
|
<h1 id="team-id" class="text-center p-0 m-0">{{ user.name }}</h1>
|
||||||
@@ -112,6 +128,7 @@
|
|||||||
<small>points</small>
|
<small>points</small>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</h3>
|
</h3>
|
||||||
|
<hr class="w-50">
|
||||||
<div class="pt-3">
|
<div class="pt-3">
|
||||||
<a class="edit-user">
|
<a class="edit-user">
|
||||||
<i class="btn-fa fas fa-user-edit fa-2x px-2" data-toggle="tooltip" data-placement="top" title="Edit User"></i>
|
<i class="btn-fa fas fa-user-edit fa-2x px-2" data-toggle="tooltip" data-placement="top" title="Edit User"></i>
|
||||||
@@ -126,11 +143,16 @@
|
|||||||
<i class="btn-fa fas fa-trash-alt fa-2x px-2" data-toggle="tooltip" data-placement="top" title="Delete User"></i>
|
<i class="btn-fa fas fa-trash-alt fa-2x px-2" data-toggle="tooltip" data-placement="top" title="Delete User"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="pt-3">
|
||||||
|
<a class="addresses-user">
|
||||||
|
<i class="btn-fa fas fa-network-wired fa-2x px-2" data-toggle="tooltip" data-placement="top" title="IP Addresses"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row" style="min-height: 300px;">
|
||||||
{% if solves %}
|
{% if solves %}
|
||||||
<div id="keys-pie-graph" class="col-md-6">
|
<div id="keys-pie-graph" class="col-md-6">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
@@ -167,7 +189,7 @@
|
|||||||
aria-controls="nav-missing" aria-selected="false">Missing</a>
|
aria-controls="nav-missing" aria-selected="false">Missing</a>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="tab-content" id="nav-tabContent">
|
<div class="tab-content" id="nav-tabContent" style="min-height: 300px;">
|
||||||
<div class="tab-pane fade show active" id="nav-solves" role="tabpanel" aria-labelledby="nav-solves-tab">
|
<div class="tab-pane fade show active" id="nav-solves" role="tabpanel" aria-labelledby="nav-solves-tab">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
@@ -335,31 +357,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row pt-5">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<table class="table table-striped">
|
|
||||||
<h3 class="text-center">IP Addresses</h3>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td class="text-center"><b>IP Address</b></td>
|
|
||||||
<td class="text-center"><b>Last Seen</b></td>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for addr in addrs %}
|
|
||||||
<tr>
|
|
||||||
<td class="text-center">{{ addr.ip }}</td>
|
|
||||||
<td class="text-center solve-time">
|
|
||||||
<span data-time="{{ addr.date | isoformat }}"></span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from flask import abort, redirect, render_template, request, session, url_for
|
|||||||
from sqlalchemy.exc import IntegrityError, InvalidRequestError
|
from sqlalchemy.exc import IntegrityError, InvalidRequestError
|
||||||
from werkzeug.wsgi import DispatcherMiddleware
|
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.exceptions import UserNotFoundException, UserTokenExpiredException
|
||||||
from CTFd.models import Tracking, db
|
from CTFd.models import Tracking, db
|
||||||
from CTFd.utils import config, get_config, markdown
|
from CTFd.utils import config, get_config, markdown
|
||||||
@@ -42,7 +42,7 @@ from CTFd.utils.security.csrf import generate_nonce
|
|||||||
from CTFd.utils.user import (
|
from CTFd.utils.user import (
|
||||||
authed,
|
authed,
|
||||||
get_current_user_attrs,
|
get_current_user_attrs,
|
||||||
get_current_user_ips,
|
get_current_user_recent_ips,
|
||||||
get_current_team_attrs,
|
get_current_team_attrs,
|
||||||
get_ip,
|
get_ip,
|
||||||
is_admin,
|
is_admin,
|
||||||
@@ -181,18 +181,20 @@ def init_request_processors(app):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if authed():
|
if authed():
|
||||||
user_ips = get_current_user_ips()
|
user_ips = get_current_user_recent_ips()
|
||||||
ip = get_ip()
|
ip = get_ip()
|
||||||
|
|
||||||
track = None
|
track = None
|
||||||
if ip not in user_ips:
|
if ip not in user_ips:
|
||||||
track = Tracking(ip=get_ip(), user_id=session["id"])
|
track = Tracking.query.filter_by(
|
||||||
db.session.add(track)
|
ip=get_ip(), user_id=session["id"]
|
||||||
else:
|
).first()
|
||||||
if request.method != "GET":
|
|
||||||
track = Tracking.query.filter_by(
|
if track:
|
||||||
ip=get_ip(), user_id=session["id"]
|
|
||||||
).first()
|
|
||||||
track.date = datetime.datetime.utcnow()
|
track.date = datetime.datetime.utcnow()
|
||||||
|
else:
|
||||||
|
track = Tracking(ip=get_ip(), user_id=session["id"])
|
||||||
|
db.session.add(track)
|
||||||
|
|
||||||
if track:
|
if track:
|
||||||
try:
|
try:
|
||||||
@@ -200,7 +202,7 @@ def init_request_processors(app):
|
|||||||
except (InvalidRequestError, IntegrityError):
|
except (InvalidRequestError, IntegrityError):
|
||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
logout_user()
|
logout_user()
|
||||||
clear_user_ips(user_id=session["id"])
|
clear_user_recent_ips(user_id=session["id"])
|
||||||
|
|
||||||
@app.before_request
|
@app.before_request
|
||||||
def banned():
|
def banned():
|
||||||
|
|||||||
@@ -120,21 +120,22 @@ def get_ip(req=None):
|
|||||||
return remote_addr
|
return remote_addr
|
||||||
|
|
||||||
|
|
||||||
def get_current_user_ips():
|
def get_current_user_recent_ips():
|
||||||
if authed():
|
if authed():
|
||||||
return get_user_ips(user_id=session["id"])
|
return get_user_recent_ips(user_id=session["id"])
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@cache.memoize(timeout=60)
|
@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 = (
|
addrs = (
|
||||||
Tracking.query.with_entities(Tracking.ip.distinct())
|
Tracking.query.with_entities(Tracking.ip.distinct())
|
||||||
.filter_by(user_id=user_id)
|
.filter(Tracking.user_id == user_id, Tracking.date >= hour_ago)
|
||||||
.all()
|
.all()
|
||||||
)
|
)
|
||||||
return [ip for ip, in addrs]
|
return set([ip for (ip,) in addrs])
|
||||||
|
|
||||||
|
|
||||||
def get_wrong_submissions_per_minute(account_id):
|
def get_wrong_submissions_per_minute(account_id):
|
||||||
|
|||||||
Reference in New Issue
Block a user