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

1.3.0 #106

Merged
merged 4 commits into from
Dec 17, 2024
Merged

1.3.0 #106

Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions .github/actions/services/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,9 @@ runs:
shell:
bash

- name: Execute docker-compose up
- name: Execute docker compose up
run: |
CLI=docker-compose
CLI="docker compose"
if [[ ${{ inputs.use_postgres }} != 'false' ]]; then
CLI="${CLI} -f postgres.yml"
fi
Expand Down
6 changes: 5 additions & 1 deletion .github/configurations/python_linters/.flake8
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,8 @@ ignore =

exclude =
*/migrations/*,
Dockerfile
Dockerfile

per-file-ignores =
# imported but unused
certego.py: E231
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,22 @@
## 1.3.x
### 1.3.0
#### Feature
* Added configuration panel in order to set custom preferences
* Added more fields in the Alert.login_raw_data dict in order to have more info about previous location for imp_travel detection
### Changes
* Set default settings values in the *settings.certego.py* file
* Moved Enums into *costants.py* file

## 1.2.x
### 1.2.12
#### Bugfix
* Cleaned venv from useless packages
* Added pytz in requirements because it's needed by celery_beat
* Registered UserAdmin in authentication
### 1.2.11
#### Bugfix
* Fixed the update of the login.updated field
* Added logging for the clear_models_periodically function
### 1.2.10
#### Changes
* Added settings into the Config model (instead of into the settings.py file)
Expand Down
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,15 @@ How to create and submit a PR:
If you didn't install pre-commit, it is necessary to run linters manually:
* Flake8
```bash
flake8 . --show-source --config ../.github/configurations/.flake8
flake8 . --show-source --config ../.github/configurations/python_linters/.flake8
```
* Black
```bash
black --config ../.github/configurations/.black .
black --config ../.github/configurations/python_linters/.black .
```
* Isort
```bash
isort --sp ../.github/configurations/.isort.cfg --profile black .
isort --sp ../.github/configurations/python_linters/.isort.cfg --profile black .
```

3. **IF** your changes include differences in the template view, **include sceenshots of the before and after**.
Expand Down
8 changes: 7 additions & 1 deletion buffalogs/authentication/admin.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
from django.contrib import admin

# Register your models here.
from .models import User


@admin.register(User)
class UserAdmin(admin.ModelAdmin):
list_display = ("id", "username", "email", "is_staff", "is_verified", "created_at", "updated_at", "avatar")
search_fields = ("id", "username", "email", "avatar")
3 changes: 0 additions & 3 deletions buffalogs/authentication/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,3 @@ def __str__(self):
def tokens(self):
refresh = RefreshToken.for_user(self)
return {"refresh": str(refresh), "access": str(refresh.access_token)}


# Create your models here.
11 changes: 11 additions & 0 deletions buffalogs/buffalogs/settings/certego.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@
CERTEGO_BUFFALOGS_POSTGRES_PORT = os.environ.get("BUFFALOGS_POSTGRES_PORT", "5432")
CERTEGO_BUFFALOGS_ELASTIC_INDEX = os.environ.get("BUFFALOGS_ELASTIC_INDEX", "weblog-*,cloud-*,fw-proxy-*,filebeat-*")
CERTEGO_BUFFALOGS_SECRET_KEY = os.environ.get("BUFFALOGS_SECRET_KEY", "django-insecure-am9z-fi-x*aqxlb-@abkhb@pu!0da%0a77h%-8d(dwzrrktwhu")
CERTEGO_BUFFALOGS_IGNORED_USERS = ["Not Available", "N/A"]
CERTEGO_BUFFALOGS_ENABLED_USERS = []
CERTEGO_BUFFALOGS_ALLOWED_COUNTRIES = []
CERTEGO_BUFFALOGS_IGNORED_IPS = ["127.0.0.1"]
CERTEGO_BUFFALOGS_VIP_USERS = []
CERTEGO_BUFFALOGS_DISTANCE_KM_ACCEPTED = 100
CERTEGO_BUFFALOGS_VEL_TRAVEL_ACCEPTED = 300
CERTEGO_BUFFALOGS_USER_MAX_DAYS = 60
CERTEGO_BUFFALOGS_LOGIN_MAX_DAYS = 30
CERTEGO_BUFFALOGS_ALERT_MAX_DAYS = 30
CERTEGO_BUFFALOGS_IP_MAX_DAYS = 30

if CERTEGO_BUFFALOGS_ENVIRONMENT == ENVIRONMENT_DOCKER:

Expand Down
Binary file modified buffalogs/celerybeat-schedule
Binary file not shown.
89 changes: 89 additions & 0 deletions buffalogs/impossible_travel/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
from enum import Enum


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

* No risk: the user has triggered 0 alerts
* Low: the user has triggered 1 or 2 alerts
* Medium: the user has triggered 3 or 4 alerts
* 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)

@classmethod
def get_risk_level(cls, value):
# map risk value
if value == 0:
return cls.NO_RISK.value
elif 1 <= value <= 2:
return cls.LOW.value
elif 3 <= value <= 4:
return cls.MEDIUM.value
elif value >= 5:
return cls.HIGH.value
else:
raise ValueError("Risk value not valid")


class AlertDetectionType(Enum):
"""Types of possible alert detections

* 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
"""

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)

@classmethod
def get_label_from_value(cls, value):
for item in cls:
if item.value == value:
return item.name
return None


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

* ISP_FILTER: exclude from the detection a list of whitelisted ISP
* IS_MOBILE_FILTER: if Config.ignore_mobile_logins flag is checked, exclude from the detection the mobile devices
* IS_VIP_FILTER: if Config.alert_is_vip_only flag is checked, only the vip users (in the Config.vip_users list) send alerts
* ALLOWED_COUNTRY_FILTER: if the country of the login is in the Config.allowed_countries list, the alert isn't sent
* IGNORED_USER_FILTER: if the user is in the Config.ignored_users list OR the user is not in the Config.enabled_users list, the alert isn't sent
* ALERT_MINIMUM_RISK_SCORE_FILTER: if the user hasn't, at least, a User.risk_score equals to the one sets in Config.alert_minimum_risk_score,
* 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)
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
# Generated by Django 5.1.4 on 2024-12-13 10:25

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


class Migration(migrations.Migration):

dependencies = [
(
"impossible_travel",
"0010_config_alert_max_days_config_distance_accepted_and_more",
),
]

operations = [
migrations.AddField(
model_name="alert",
name="filter_type",
field=django.contrib.postgres.fields.ArrayField(
base_field=models.CharField(
blank=True,
choices=[
("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"),
],
max_length=50,
),
blank=True,
default=list,
help_text="List of filters that disabled the related alert",
size=None,
),
),
migrations.AddField(
model_name="alert",
name="is_filtered",
field=models.BooleanField(
default=False,
help_text="Show if the alert has been filtered because of some filter (listed in the filter_type field)",
),
),
migrations.AddField(
model_name="config",
name="alert_is_vip_only",
field=models.BooleanField(
default=False,
help_text="Flag to send alert only related to the users in the vip_users list",
),
),
migrations.AddField(
model_name="config",
name="alert_minimum_risk_score",
field=models.CharField(
choices=[
("NO_RISK", "No risk"),
("LOW", "Low"),
("MEDIUM", "Medium"),
("HIGH", "High"),
],
default="No risk",
help_text="Select the risk_score that users should have at least to send alert",
max_length=30,
),
),
migrations.AddField(
model_name="config",
name="enabled_users",
field=django.contrib.postgres.fields.ArrayField(
base_field=models.CharField(max_length=50),
blank=True,
default=impossible_travel.models.get_default_enabled_users,
help_text="List of selected users on which the detection will perform",
size=None,
),
),
migrations.AddField(
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 threshold alert"),
("LOGIN_ANONYMIZER_IP", "Login from anonymizer IP"),
("ATYPICAL_COUNTRY", "Login from atypical country"),
],
max_length=50,
),
default=list,
help_text="List of alerts' types to exclude from the alerting",
size=None,
),
),
migrations.AddField(
model_name="config",
name="ignore_mobile_logins",
field=models.BooleanField(
default=False,
help_text="Flag to ignore mobile devices from the detection",
),
),
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 threshold alert"),
("LOGIN_ANONYMIZER_IP", "Login from anonymizer IP"),
("ATYPICAL_COUNTRY", "Login from atypical country"),
],
max_length=30,
),
),
migrations.AlterField(
model_name="config",
name="allowed_countries",
field=django.contrib.postgres.fields.ArrayField(
base_field=models.CharField(max_length=20),
blank=True,
default=impossible_travel.models.get_default_allowed_countries,
help_text="List of countries to exclude from the detection, because 'trusted' for the customer",
size=None,
),
),
migrations.AlterField(
model_name="config",
name="ignored_ips",
field=django.contrib.postgres.fields.ArrayField(
base_field=models.CharField(max_length=50),
blank=True,
default=impossible_travel.models.get_default_ignored_ips,
help_text="List of IPs to remove from the detection",
size=None,
),
),
migrations.AlterField(
model_name="config",
name="ignored_users",
field=django.contrib.postgres.fields.ArrayField(
base_field=models.CharField(max_length=50),
blank=True,
default=impossible_travel.models.get_default_ignored_users,
help_text="List of users to be ignored from the detection",
size=None,
),
),
migrations.AlterField(
model_name="config",
name="vip_users",
field=django.contrib.postgres.fields.ArrayField(
base_field=models.CharField(max_length=50),
blank=True,
default=impossible_travel.models.get_default_vip_users,
help_text="List of users considered more sensitive",
size=None,
),
),
migrations.AlterField(
model_name="user",
name="risk_score",
field=models.CharField(
choices=[
("NO_RISK", "No risk"),
("LOW", "Low"),
("MEDIUM", "Medium"),
("HIGH", "High"),
],
default="No risk",
max_length=30,
),
),
]
Loading
Loading