Add Do Not Track support

This commit is contained in:
R. Miles McCain 2020-04-22 13:18:25 -04:00
parent 3d10ddf711
commit c2f4724b11
No known key found for this signature in database
GPG Key ID: 91CB47BDDF2671A5
7 changed files with 36 additions and 8 deletions

View File

@ -131,9 +131,9 @@ SERVER_EMAIL="Shynet <noreply@shynet.example.com>"
## FAQ ## FAQ
**Does Shynet respond to Do Not Track (DNT) signals?** It's on the [roadmap](#roadmap). While there isn't any standardized way to handle DNT requests, the plan is to give services the option of ignoring DNT signals entirely, limiting collected data (i.e. not collecting any visitor-related details such as browser, IP, etc.), or excluding DNT requests from analytics entirely. **Does Shynet respond to Do Not Track (DNT) signals?** Yes. While there isn't any standardized way to handle DNT requests, Shynet allows you to specify whether you want to collect any data from users with DNT enabled on a per-service basis. (By default, Shynet will _not_ collect any data from users who specify DNT.)
**Is this GDPR compliant?** I think so, but it also depends on how you use it. If you're worried about GDPR, you should talk to a lawyer about your particular data collection practices. I am not a lawyer. **Is this GDPR compliant?** I think so, but it also depends on how you use it. If you're worried about GDPR, you should talk to a lawyer about your particular data collection practices. I'm not a lawyer. (And this isn't legal advice.)
## Roadmap ## Roadmap
@ -145,7 +145,6 @@ The following features are planned:
* **Better collaboration interface** (the current interface is... a draft) * **Better collaboration interface** (the current interface is... a draft)
* **Data deletion tool** (easily prune user data by specifying an ID or IP) * **Data deletion tool** (easily prune user data by specifying an ID or IP)
* **Differential privacy** (explore and share your data without revealing any personal information) * **Differential privacy** (explore and share your data without revealing any personal information)
* **Do Not Track support** (change tracking behavior in response to DNT signals)
## In the Wild ## In the Wild

View File

@ -18,7 +18,7 @@
{% endfor %} {% endfor %}
{% elif field|is_radio %} {% elif field|is_radio %}
{% if field.auto_id %} {% if field.auto_id %}
<label class="label my-1" for="{{field.auto_id}}">{{ field.label }}</label> <label class="label block" for="{{field.auto_id}}">{{ field.label }}</label>
{% endif %} {% endif %}
{% for choice in field %} {% for choice in field %}
<label class="switch my-1"> <label class="switch my-1">

View File

@ -42,12 +42,15 @@ def _geoip2_lookup(ip):
@shared_task @shared_task
def ingress_request( def ingress_request(
service_uuid, tracker, time, payload, ip, location, user_agent, identifier="" service_uuid, tracker, time, payload, ip, location, user_agent, dnt=False, identifier=""
): ):
try: try:
service = Service.objects.get(pk=service_uuid, status=Service.ACTIVE) service = Service.objects.get(pk=service_uuid, status=Service.ACTIVE)
log.debug(f"Linked to service {service}") log.debug(f"Linked to service {service}")
if dnt and service.respect_dnt:
return
ip_data = _geoip2_lookup(ip) ip_data = _geoip2_lookup(ip)
log.debug(f"Found geoip2 data") log.debug(f"Found geoip2 data")

View File

@ -18,6 +18,7 @@ def ingress(request, service_uuid, identifier, tracker, payload):
client_ip, is_routable = get_client_ip(request) client_ip, is_routable = get_client_ip(request)
location = request.META.get("HTTP_REFERER", "").strip() location = request.META.get("HTTP_REFERER", "").strip()
user_agent = request.META.get("HTTP_USER_AGENT", "").strip() user_agent = request.META.get("HTTP_USER_AGENT", "").strip()
dnt = request.META.get("HTTP_DNT", "0").strip() == "1"
ingress_request.delay( ingress_request.delay(
service_uuid, service_uuid,
@ -27,7 +28,8 @@ def ingress(request, service_uuid, identifier, tracker, payload):
client_ip, client_ip,
location, location,
user_agent, user_agent,
identifier, dnt=dnt,
identifier=identifier,
) )

View File

@ -0,0 +1,18 @@
# Generated by Django 3.0.5 on 2020-04-22 17:03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0002_auto_20200415_1742'),
]
operations = [
migrations.AddField(
model_name='service',
name='respect_dnt',
field=models.BooleanField(default=True),
),
]

View File

@ -41,6 +41,7 @@ class Service(models.Model):
status = models.CharField( status = models.CharField(
max_length=2, choices=SERVICE_STATUSES, default=ACTIVE, db_index=True max_length=2, choices=SERVICE_STATUSES, default=ACTIVE, db_index=True
) )
respect_dnt = models.BooleanField(default=True)
class Meta: class Meta:
ordering = ["name", "uuid"] ordering = ["name", "uuid"]
@ -60,7 +61,9 @@ class Service(models.Model):
end_time = timezone.now() end_time = timezone.now()
main_data = self.get_relative_stats(start_time, end_time) main_data = self.get_relative_stats(start_time, end_time)
comparison_data = self.get_relative_stats(start_time - (end_time - start_time), start_time) comparison_data = self.get_relative_stats(
start_time - (end_time - start_time), start_time
)
main_data["compare"] = comparison_data main_data["compare"] = comparison_data
return main_data return main_data

View File

@ -7,14 +7,16 @@ from core.models import Service
class ServiceForm(forms.ModelForm): class ServiceForm(forms.ModelForm):
class Meta: class Meta:
model = Service model = Service
fields = ["name", "link", "origins", "collaborators"] fields = ["name", "link", "origins", "respect_dnt", "collaborators"]
widgets = { widgets = {
"name": forms.TextInput(), "name": forms.TextInput(),
"origins": forms.TextInput(), "origins": forms.TextInput(),
"collaborators": forms.CheckboxSelectMultiple(), "collaborators": forms.CheckboxSelectMultiple(),
"respect_dnt": forms.RadioSelect(choices=[(True, "Yes"), (False, "No")])
} }
labels = { labels = {
"origins": "Allowed Hostnames", "origins": "Allowed Hostnames",
"respect_dnt": "Respect DNT",
} }
help_texts = { help_texts = {
"name": _("What should the service be called?"), "name": _("What should the service be called?"),
@ -22,4 +24,5 @@ class ServiceForm(forms.ModelForm):
"origins": _( "origins": _(
"At what hostnames does the service operate? This sets CORS headers, so use '*' if you're not sure (or don't care)." "At what hostnames does the service operate? This sets CORS headers, so use '*' if you're not sure (or don't care)."
), ),
"respect_dnt": "Should visitors who have enabled <a href='https://en.wikipedia.org/wiki/Do_Not_Track'>Do Not Track</a> be excluded from all data?"
} }