Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix alert name representation #108

Merged
merged 20 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
### 1.3.1
### Changes
* Forced the existence of only 1 Config object with id=1
#### Bugfix
* Fixed alert.name representation enums
### 1.3.0
#### Feature
* Added configuration panel in order to set custom preferences
Expand Down
6 changes: 5 additions & 1 deletion buffalogs/impossible_travel/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,17 @@ class UserAdmin(admin.ModelAdmin):

@admin.register(Alert)
class AlertAdmin(admin.ModelAdmin):
list_display = ("id", "created", "updated", "get_username", "name", "description", "login_raw_data", "is_vip")
list_display = ("id", "created", "updated", "get_username", "get_alert_value", "description", "login_raw_data", "is_vip")
search_fields = ("user__username", "name", "is_vip")

@admin.display(description="username")
def get_username(self, obj):
return obj.user.username

@admin.display(description="alert_value")
def get_alert_value(self, obj):
return obj.name


@admin.register(TaskSettings)
class TaskSettingsAdmin(admin.ModelAdmin):
Expand Down
63 changes: 27 additions & 36 deletions buffalogs/impossible_travel/constants.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from enum import Enum

from django.db import models
from django.utils.translation import gettext_lazy as _

class UserRiskScoreType(Enum):

class UserRiskScoreType(models.TextChoices):
"""Possible types of user risk scores, based on number of alerts that they have triggered

* No risk: the user has triggered 0 alerts
Expand All @@ -10,14 +13,10 @@ class UserRiskScoreType(Enum):
* High: the user has triggered more than 4 alerts
"""

NO_RISK = "No risk"
LOW = "Low"
MEDIUM = "Medium"
HIGH = "High"

@classmethod
def choices(cls):
return tuple((i.name, i.value) for i in cls)
NO_RISK = "No risk", _("User has no risk")
LOW = "Low", _("User has a low risk")
MEDIUM = "Medium", _("User has a medium risk")
HIGH = "High", _("User has a high risk")

@classmethod
def get_risk_level(cls, value):
Expand All @@ -34,27 +33,23 @@ def get_risk_level(cls, value):
raise ValueError("Risk value not valid")


class AlertDetectionType(Enum):
"""Types of possible alert detections
class AlertDetectionType(models.TextChoices):
"""Types of possible alert detections in the format (name=value,label)

* NEW_DEVICE: Login from a new user-agent used by the user
* IMP_TRAVEL: Alert if the user logs into the system from a significant distance () within a range of time that cannot be covered by conventional means of transport
* NEW_COUNTRY: The user made a login from a country where they have never logged in before
* USER_RISK_THRESHOLD:
* LOGIN_ANONYMIZER_IP:
* ATYPICAL_COUNTRY
* USER_RISK_THRESHOLD: Alert if the user.risk_score value is equal or higher than the Config.alert_minimum_risk_score
* LOGIN_ANONYMIZER_IP: Alert if the login has been made from an anonymizer IP
* ATYPICAL_COUNTRY: Alert if the login has been made from a country not visited recently
"""

NEW_DEVICE = "Login from new device"
IMP_TRAVEL = "Impossible Travel detected"
NEW_COUNTRY = "Login from new country"
USER_RISK_THRESHOLD = "User risk threshold alert"
LOGIN_ANONYMIZER_IP = "Login from anonymizer IP"
ATYPICAL_COUNTRY = "Login from atypical country"

@classmethod
def choices(cls):
return tuple((i.name, i.value) for i in cls)
NEW_DEVICE = "New Device", _("Login from new device")
IMP_TRAVEL = "Imp Travel", _("Impossible Travel detected")
NEW_COUNTRY = "New Country", _("Login from new country")
USER_RISK_THRESHOLD = "User Risk Threshold", _("User risk higher than threshold")
LOGIN_ANONYMIZER_IP = "Login Anonymizer Ip", _("Login from an anonymizer IP")
ATYPICAL_COUNTRY = "Atypical Country", _("Login from a country not visited recently")

@classmethod
def get_label_from_value(cls, value):
Expand All @@ -64,7 +59,7 @@ def get_label_from_value(cls, value):
return None


class AlertFilterType(Enum):
class AlertFilterType(models.TextChoices):
"""Types of possible detection filter applied on alerts to be ignored

* ISP_FILTER: exclude from the detection a list of whitelisted ISP
Expand All @@ -76,14 +71,10 @@ class AlertFilterType(Enum):
* FILTERED_ALERTS: if the alert type (AlertDetectionType) is in the Config.filtered_alerts, the alert isn't sent
"""

ISP_FILTER = "isp_filter"
IS_MOBILE_FILTER = "is_mobile_filter"
IS_VIP_FILTER = "is_vip_filter"
ALLOWED_COUNTRY_FILTER = "allowed_country_filter"
IGNORED_USER_FILTER = "ignored_user_filter"
ALERT_MINIMUM_RISK_SCORE_FILTER = "alert_minimum_risk_score_filter"
FILTERED_ALERTS = "filtered_alerts"

@classmethod
def choices(cls):
return tuple((i.name, i.value) for i in cls)
ISP_FILTER = "isp_filter", _("Alert filtered because the ISP is whitelisted")
IS_MOBILE_FILTER = "is_mobile_filter", _("Alert filtered because login from a mobile device")
IS_VIP_FILTER = "is_vip_filter", _("Alert filtered because the user is not vip")
ALLOWED_COUNTRY_FILTER = "allowed_country_filter", _("Alert filtered because the country is whitelisted")
IGNORED_USER_FILTER = "ignored_user_filter", _("Alert filtered because the user is ignored")
ALERT_MINIMUM_RISK_SCORE_FILTER = "alert_minimum_risk_score_filter", _("Alert filtered because the risk_score is lower than the threshold")
FILTERED_ALERTS = "filtered_alerts", _("Alert filtered because this detection type is excluded")
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# Generated by Django 5.1.4 on 2024-12-20 09:20

import django.contrib.postgres.fields
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("impossible_travel", "0011_alert_filter_type_alert_is_filtered_and_more"),
]

operations = [
migrations.AlterField(
model_name="alert",
name="filter_type",
field=django.contrib.postgres.fields.ArrayField(
base_field=models.CharField(
blank=True,
choices=[
("isp_filter", "Alert filtered because the ISP is whitelisted"),
(
"is_mobile_filter",
"Alert filtered because login from a mobile device",
),
("is_vip_filter", "Alert filtered because the user is not vip"),
(
"allowed_country_filter",
"Alert filtered because the country is whitelisted",
),
(
"ignored_user_filter",
"Alert filtered because the user is ignored",
),
(
"alert_minimum_risk_score_filter",
"Alert filtered because the risk_score is lower than the threshold",
),
(
"filtered_alerts",
"Alert filtered because this detection type is excluded",
),
],
max_length=50,
),
blank=True,
default=list,
help_text="List of filters that disabled the related alert",
size=None,
),
),
migrations.AlterField(
model_name="alert",
name="name",
field=models.CharField(
choices=[
("New Device", "Login from new device"),
("Imp Travel", "Impossible Travel detected"),
("New Country", "Login from new country"),
("User Risk Threshold", "User risk higher than threshold"),
("Login Anonymizer Ip", "Login from an anonymizer IP"),
("Atypical Country", "Login from a country not visited recently"),
],
max_length=30,
),
),
migrations.AlterField(
model_name="config",
name="alert_minimum_risk_score",
field=models.CharField(
choices=[
("No risk", "User has no risk"),
("Low", "User has a low risk"),
("Medium", "User has a medium risk"),
("High", "User has a high risk"),
],
default="No risk",
help_text="Select the risk_score that users should have at least to send alert",
max_length=30,
),
),
migrations.AlterField(
model_name="config",
name="filtered_alerts_types",
field=django.contrib.postgres.fields.ArrayField(
base_field=models.CharField(
blank=True,
choices=[
("New Device", "Login from new device"),
("Imp Travel", "Impossible Travel detected"),
("New Country", "Login from new country"),
("User Risk Threshold", "User risk higher than threshold"),
("Login Anonymizer Ip", "Login from an anonymizer IP"),
(
"Atypical Country",
"Login from a country not visited recently",
),
],
max_length=50,
),
default=list,
help_text="List of alerts' types to exclude from the alerting",
size=None,
),
),
migrations.AlterField(
model_name="user",
name="risk_score",
field=models.CharField(
choices=[
("No risk", "User has no risk"),
("Low", "User has a low risk"),
("Medium", "User has a medium risk"),
("High", "User has a high risk"),
],
default="No risk",
max_length=30,
),
),
]
10 changes: 5 additions & 5 deletions buffalogs/impossible_travel/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@


class User(models.Model):
risk_score = models.CharField(choices=UserRiskScoreType.choices(), max_length=30, null=False, default=UserRiskScoreType.NO_RISK.value)
risk_score = models.CharField(choices=UserRiskScoreType.choices, max_length=30, null=False, default=UserRiskScoreType.NO_RISK.value)
username = models.TextField(unique=True, db_index=True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
Expand All @@ -31,7 +31,7 @@ class Login(models.Model):

class Alert(models.Model):
name = models.CharField(
choices=AlertDetectionType.choices(),
choices=AlertDetectionType.choices,
max_length=30,
null=False,
)
Expand All @@ -43,7 +43,7 @@ class Alert(models.Model):
is_vip = models.BooleanField(default=False)
is_filtered = models.BooleanField(default=False, help_text="Show if the alert has been filtered because of some filter (listed in the filter_type field)")
filter_type = ArrayField(
models.CharField(max_length=50, choices=AlertFilterType.choices(), blank=True),
models.CharField(max_length=50, choices=AlertFilterType.choices, blank=True),
blank=True,
default=list,
help_text="List of filters that disabled the related alert",
Expand Down Expand Up @@ -104,14 +104,14 @@ class Config(models.Model):
vip_users = ArrayField(models.CharField(max_length=50), blank=True, default=get_default_vip_users, help_text="List of users considered more sensitive")
alert_is_vip_only = models.BooleanField(default=False, help_text="Flag to send alert only related to the users in the vip_users list")
alert_minimum_risk_score = models.CharField(
choices=UserRiskScoreType.choices(),
choices=UserRiskScoreType.choices,
max_length=30,
blank=False,
default=UserRiskScoreType.NO_RISK.value,
help_text="Select the risk_score that users should have at least to send alert",
)
filtered_alerts_types = ArrayField(
models.CharField(max_length=50, choices=AlertDetectionType.choices(), blank=True),
models.CharField(max_length=50, choices=AlertDetectionType.choices, blank=True),
default=list,
help_text="List of alerts' types to exclude from the alerting",
)
Expand Down
Loading