Skip to content

Commit

Permalink
Affiche la ville de l'IP sur la page des comptes utilisant la même IP
Browse files Browse the repository at this point in the history
  • Loading branch information
philippemilink committed Oct 15, 2023
1 parent c29c53f commit 8bae2bc
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 37 deletions.
10 changes: 8 additions & 2 deletions templates/member/admin/memberip.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@

{% block content %}
<p>
{% captureas city %}{% if ip_location %} ({{ ip_location }}){% endif %}{% endcaptureas %}
{% blocktrans %}
Liste des membres dont la dernière IP connue est <code>{{ ip }}</code>
Liste des membres dont la dernière IP connue est <code>{{ ip }}</code>{{ city }}
{% endblocktrans %}
</p>

Expand Down Expand Up @@ -55,7 +56,12 @@
</div>

<p>
En IPv6, les adresses sont attribuées par bloc d'IP. Un bot de spam peut donc facilement changer d'adresse IP au sein de ce bloc. Sont affichés ici tous les membres dont l'IPv6 fait partie du même bloc que l'IP demandée.
{% blocktrans %}
En IPv6, les adresses sont attribuées par bloc d'IP. Un bot de spam
peut donc facilement changer d'adresse IP au sein de ce bloc. Sont
affichés ici tous les membres dont l'IPv6 fait partie du même bloc que
l'IP demandée.
{% endblocktrans %}
</p>
{% endif %}
{% endblock %}
36 changes: 10 additions & 26 deletions zds/member/models.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
import logging
from datetime import datetime
from geoip2.errors import AddressNotFoundError
from hashlib import md5
import logging

from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.gis.geoip2 import GeoIP2, GeoIP2Exception
from django.urls import reverse
from django.db import models
from django.dispatch import receiver
from django.urls import reverse
from django.utils.translation import gettext_lazy as _

from zds.forum.models import Post, Topic
from zds.notification.models import TopicAnswerSubscription
from zds.forum.models import Forum, Post, Topic
from zds.member import NEW_PROVIDER_USES
from zds.member.managers import ProfileManager
from zds.member.utils import get_geo_location_from_ip
from zds.notification.models import TopicAnswerSubscription
from zds.tutorialv2.models.database import PublishableContent
from zds.utils import old_slugify
from zds.utils.models import Alert, Licence, Hat

from zds.forum.models import Forum
import homoglyphs as hg


Expand Down Expand Up @@ -91,30 +89,16 @@ def get_absolute_url(self):

def get_city(self):
"""
Uses geo-localization to get physical localization of a profile through its last IP address.
This works relatively well with IPv4 addresses (~city level), but is very imprecise with IPv6 or exotic internet
providers.
The result is cached on an instance level because this method is called a lot in the profile.
Uses geo-localization to get physical localization of a profile through
its last IP address.
The result is cached on an instance level because this method is called
a lot in the profile.
:return: The city and the country name of this profile.
"""
if self._cached_city is not None and self._cached_city[0] == self.last_ip_address:
return self._cached_city[1]

try:
geo = GeoIP2().city(self.last_ip_address)
except AddressNotFoundError:
geo_location = ""
except GeoIP2Exception as e:
geo_location = ""
logging.getLogger(__name__).warning(
f"GeoIP2 failed with the following message: '{e}'. "
"The Geolite2 database might not be installed or configured correctly. "
"Check the documentation for guidance on how to install it properly."
)
else:
city = geo["city"]
country = geo["country_name"]
geo_location = ", ".join(i for i in [city, country] if i)
geo_location = get_geo_location_from_ip(self.last_ip_address)

self._cached_city = (self.last_ip_address, geo_location)

Expand Down
35 changes: 31 additions & 4 deletions zds/member/utils.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from geoip2.errors import AddressNotFoundError
import logging

from django.conf import settings
from django.contrib.auth.models import User
from social_django.middleware import SocialAuthExceptionMiddleware
from django.contrib import messages
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import User
from django.contrib.gis.geoip2 import GeoIP2, GeoIP2Exception
from django.urls import reverse
import logging
from django.utils.translation import gettext_lazy as _
from social_django.middleware import SocialAuthExceptionMiddleware

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -49,3 +52,27 @@ def get_anonymous_account() -> User:
Used for example as a replacement for unregistered users.
"""
return User.objects.get(username=settings.ZDS_APP["member"]["anonymous_account"])


def get_geo_location_from_ip(ip: str) -> str:
"""
Uses geo-localization to get physical localization of an IP address.
This works relatively well with IPv4 addresses (~city level), but is very
imprecise with IPv6 or exotic internet providers.
:return: The city and the country name of this IP.
"""
try:
geo = GeoIP2().city(self.last_ip_address)
except AddressNotFoundError:
return ""
except GeoIP2Exception as e:
logger.warning(
f"GeoIP2 failed with the following message: '{e}'. "
"The Geolite2 database might not be installed or configured correctly. "
"Check the documentation for guidance on how to install it properly."
)
return ""
else:
city = geo["city"]
country = geo["country_name"]
return ", ".join(i for i in [city, country] if i)
14 changes: 9 additions & 5 deletions zds/member/views/moderation.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import ipaddress
import logging

from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import login_required, permission_required
from django.core.exceptions import PermissionDenied
from django.db import transaction
from django.http import HttpResponseBadRequest
from django.urls import reverse
from django.http import Http404
from django.http import Http404, HttpResponseBadRequest
from django.shortcuts import redirect, render, get_object_or_404
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.views.decorators.http import require_POST

Expand All @@ -23,7 +23,7 @@
from zds.member.decorator import can_write_and_read_now
from zds.member.forms import MiniProfileForm
from zds.member.models import Profile, KarmaNote
import logging
from zds.member.utils import get_geo_location_from_ip


@login_required
Expand All @@ -32,7 +32,11 @@ def member_from_ip(request, ip_address):
"""List users connected from a particular IP, and an IPV6 subnetwork."""

members = Profile.objects.filter(last_ip_address=ip_address).order_by("-last_visit")
members_and_ip = {"members": members, "ip": ip_address}
members_and_ip = {
"members": members,
"ip": ip_address,
"ip_location": get_geo_location_from_ip(ip_address),
}

if ":" in ip_address: # Check if it's an IPV6
network_ip = ipaddress.ip_network(ip_address + "/64", strict=False).network_address # Get the network / block
Expand Down

0 comments on commit 8bae2bc

Please sign in to comment.