Skip to content

Commit

Permalink
refactor: add stream-event geo-ip
Browse files Browse the repository at this point in the history
  • Loading branch information
ohrstrom committed Jun 26, 2024
1 parent 6d55163 commit 2b9d35d
Show file tree
Hide file tree
Showing 9 changed files with 176 additions and 17 deletions.
12 changes: 6 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,21 @@ lint-be:
lint-fe:
yarn lint

.PHONY: fix-be
fix-be:
.PHONY: format-be
format-be:
poetry run isort obr_core/
poetry run ruff check --fix-only obr_core/
poetry run black obr_core/

.PHONY: fix-fe
fix-fe:
.PHONY: format-fe
format-fe:
yarn fix

.PHONY: lint
lint: lint-be lint-fe

.PHONY: fix
fix: fix-be fix-fe
.PHONY: format
fix: format-be format-fe

.PHONY: test-be
test-be:
Expand Down
25 changes: 17 additions & 8 deletions obr_core/account/apple_id_login/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import base64
import datetime
import logging
import time
from datetime import datetime, timezone

import jwt
import requests
Expand All @@ -11,7 +11,7 @@

SOCIAL_AUTH_PROVIDER = "apple-id"

ID_TOKEN_ISSUER = "https://appleid.apple.com"
ID_TOKEN_ISSUER = "https://appleid.apple.com" # noqa S105

APP_ID = "ch.digris.obrapp"
APP_TEAM_ID = "236JDQVAKF"
Expand All @@ -36,7 +36,7 @@ def base64_decode(value):

def get_public_key(key_id):
url = "https://appleid.apple.com/auth/keys"
r = requests.get(url)
r = requests.get(url, timeout=5)
keys = r.json()["keys"]
return next((key for key in keys if key["kid"] == key_id), None)

Expand Down Expand Up @@ -77,6 +77,7 @@ def verify_authorization_code(authorization_code):
headers={
"Content-Type": "application/x-www-form-urlencoded",
},
timeout=5,
)

print(r.status_code)
Expand All @@ -95,13 +96,18 @@ def decode_id_token(id_token):
audience=APP_ID,
algorithms=["RS256"],
)
except PyJWTError as error:
raise AppleIdLoginError(f"token validation failed by {error}")
except PyJWTError as e:
raise AppleIdLoginError(f"token validation failed by {e}") from e

return decoded


def get_or_create_user(request, id_token, authorization_code, profile=None):
def get_or_create_user( # noqa C901
request,
id_token,
authorization_code,
profile=None,
):
payload = decode_id_token(id_token)
print("payload", payload)

Expand All @@ -118,8 +124,11 @@ def get_or_create_user(request, id_token, authorization_code, profile=None):
if audience != APP_ID:
raise AppleIdLoginError(f"invalid audience: {audience}")

if datetime.fromtimestamp(expires_at, tz=timezone.utc) < datetime.now(
tz=timezone.utc,
if datetime.datetime.fromtimestamp(
expires_at,
tz=datetime.UTC,
) < datetime.datetime.now(
tz=datetime.UTC,
):
raise AppleIdLoginError("token has expired")

Expand Down
Empty file added obr_core/geoip/__init__.py
Empty file.
6 changes: 6 additions & 0 deletions obr_core/geoip/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class GeoipConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "geoip"
28 changes: 28 additions & 0 deletions obr_core/geoip/lookup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import json

import requests

BASE_URL = "https://json.geoiplookup.io/"


class GeoipError(Exception):
pass


def geoip(ip):
url = f"{BASE_URL}{ip}"

try:
r = requests.get(url, timeout=(1, 5))
except requests.exceptions.RequestException as e:
raise GeoipError(f"error connecting: {e}") from e

if not r.status_code == 200:
raise GeoipError(f"invalid status-code returned: {r.status_code}")

try:
result = r.json()
except json.JSONDecodeError as e:
raise GeoipError(f"error decoding JSON response: {e}") from e

return result
13 changes: 13 additions & 0 deletions obr_core/stats/admin/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,14 @@ class StreamEventAdmin(
"bytes_sent",
"referer",
"user_agent_display",
"geoip_country",
"geoip_region",
"geoip_city",
]
list_filter = [
"time_start",
"time_end",
"geoip_country",
"path",
]
date_hierarchy = "time_start"
Expand All @@ -70,6 +74,15 @@ class StreamEventAdmin(
"referer",
"user_agent",
]
readonly_fields = [
"device_key",
"geoip_city",
"geoip_region",
"geoip_country",
]

def has_change_permission(self, request, obj=None):
return False

@admin.display(description="UA")
def user_agent_display(self, obj):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Generated by Django 4.2.7 on 2024-06-26 08:25

from django.db import migrations, models

import django_countries.fields


class Migration(migrations.Migration):
dependencies = [
("stats", "0012_streamevent_device_key"),
]

operations = [
migrations.AddField(
model_name="streamevent",
name="geoip_city",
field=models.CharField(blank=True, default="", max_length=128),
),
migrations.AddField(
model_name="streamevent",
name="geoip_country",
field=django_countries.fields.CountryField(
blank=True, default="", max_length=2
),
),
migrations.AddField(
model_name="streamevent",
name="geoip_region",
field=models.CharField(blank=True, default="", max_length=128),
),
migrations.AlterField(
model_name="streamevent",
name="device_key",
field=models.CharField(
blank=True, db_index=True, default="", max_length=64
),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Generated by Django 4.2.7 on 2024-06-26 08:26

from django.db import migrations, models

import django_countries.fields


class Migration(migrations.Migration):
dependencies = [
("stats", "0013_streamevent_geoip_city_streamevent_geoip_country_and_more"),
]

operations = [
migrations.AlterField(
model_name="streamevent",
name="geoip_city",
field=models.CharField(
blank=True, db_index=True, default="", max_length=128
),
),
migrations.AlterField(
model_name="streamevent",
name="geoip_country",
field=django_countries.fields.CountryField(
blank=True, db_index=True, default="", max_length=2
),
),
migrations.AlterField(
model_name="streamevent",
name="geoip_region",
field=models.CharField(
blank=True, db_index=True, default="", max_length=128
),
),
]
36 changes: 33 additions & 3 deletions obr_core/stats/models/event.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import logging

from django.db import models
from django.db.models import F, Window
from django.db.models.functions import Lead
from django.db.models.signals import pre_save
from django.dispatch import receiver

from common.models.mixins import CTModelMixin, CTUIDModelMixin
from django_countries.fields import CountryField
from geoip import lookup
from user_identity.utils import generate_device_key

logger = logging.getLogger(__name__)


class PlayerEventQuerySet(
models.QuerySet,
Expand Down Expand Up @@ -134,6 +140,26 @@ class StreamEvent(
default="",
blank=True,
)
geoip_city = models.CharField(
verbose_name="city",
max_length=128,
blank=True,
default="",
db_index=True,
)
geoip_region = models.CharField(
verbose_name="region",
max_length=128,
blank=True,
default="",
db_index=True,
)
geoip_country = CountryField(
verbose_name="country",
blank=True,
default="",
db_index=True,
)

class Meta:
app_label = "stats"
Expand All @@ -154,6 +180,10 @@ def stream_event_pre_save(sender, instance, **kwargs):
)

if instance.ip:
instance.device_key = generate_device_key(
instance.ip,
)
try:
geoip_data = lookup.geoip(instance.ip)
instance.geoip_city = geoip_data.get("city", "")[:128]
instance.geoip_region = geoip_data.get("region", "")[:128]
instance.geoip_country = geoip_data.get("country_code", "")
except lookup.GeoipError as e:
logger.info(f"geoip error: {e}")

0 comments on commit 2b9d35d

Please sign in to comment.