mirror of https://github.com/JohnHammond/CTFd.git
Merge pull request #1371 from CTFd/ip-address-admin-modal
* IP Tracking cache returns the IPs used by the user in the last hour. This way we can track the "Last Seen" value better for GET requests. * Moves IP addresses in the Admin Panel for Users and Teams into a modal * Closes #11462.4.0-dev
commit
fe97385f69
|
@ -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):
|
||||
|
|
|
@ -221,6 +221,10 @@ $(() => {
|
|||
$("#team-award-modal").modal("toggle");
|
||||
});
|
||||
|
||||
$(".addresses-team").click(function(event) {
|
||||
$("#team-addresses-modal").modal("toggle");
|
||||
});
|
||||
|
||||
$("#user-award-form").submit(function(e) {
|
||||
e.preventDefault();
|
||||
const params = $("#user-award-form").serializeJSON(true);
|
||||
|
|
|
@ -419,6 +419,10 @@ $(() => {
|
|||
$("#user-email-modal").modal("toggle");
|
||||
});
|
||||
|
||||
$(".addresses-user").click(function(event) {
|
||||
$("#user-addresses-modal").modal("toggle");
|
||||
});
|
||||
|
||||
$("#user-mail-form").submit(emailUser);
|
||||
|
||||
$(".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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -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>
|
|
@ -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 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="container">
|
||||
<h1 id="team-id" class="text-center">{{ team.name }}</h1>
|
||||
|
@ -109,6 +125,7 @@
|
|||
<small>points</small>
|
||||
{% endif %}
|
||||
</h3>
|
||||
<hr class="w-50">
|
||||
<div class="pt-3">
|
||||
<a class="edit-team">
|
||||
<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>
|
||||
</a>
|
||||
</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>
|
||||
|
||||
|
@ -356,37 +378,6 @@
|
|||
</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>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -52,6 +52,22 @@
|
|||
</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="container">
|
||||
<h1 id="team-id" class="text-center p-0 m-0">{{ user.name }}</h1>
|
||||
|
@ -112,6 +128,7 @@
|
|||
<small>points</small>
|
||||
{% endif %}
|
||||
</h3>
|
||||
<hr class="w-50">
|
||||
<div class="pt-3">
|
||||
<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>
|
||||
|
@ -126,6 +143,11 @@
|
|||
<i class="btn-fa fas fa-trash-alt fa-2x px-2" data-toggle="tooltip" data-placement="top" title="Delete User"></i>
|
||||
</a>
|
||||
</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>
|
||||
|
||||
|
@ -335,31 +357,6 @@
|
|||
</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>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -7,7 +7,7 @@ from flask import abort, redirect, render_template, request, session, url_for
|
|||
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.security.csrf import generate_nonce
|
|||
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 init_request_processors(app):
|
|||
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":
|
||||
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 init_request_processors(app):
|
|||
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():
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue