Add script injection option

This commit is contained in:
R. Miles McCain 2020-06-28 18:59:05 +00:00
parent 2e7620f1eb
commit 510df192d8
No known key found for this signature in database
GPG Key ID: F1053629E2905557
8 changed files with 60 additions and 5 deletions

View File

@ -92,4 +92,6 @@ def is_file(field):
def add_class(field, css_class): def add_class(field, css_class):
if len(field.errors) > 0: if len(field.errors) > 0:
css_class += " ~critical" css_class += " ~critical"
if field.field.widget.attrs.get("class") != None:
css_class += " " + field.field.widget.attrs["class"]
return field.as_widget(attrs={"class": field.css_classes(extra_classes=css_class)}) return field.as_widget(attrs={"class": field.css_classes(extra_classes=css_class)})

View File

@ -43,3 +43,12 @@ var Shynet = {
}; };
window.addEventListener("load", Shynet.newPageLoad); window.addEventListener("load", Shynet.newPageLoad);
{% if script_inject %}
// The following is script is not part of Shynet, and was instead
// provided by this site's administrator.
//
// -- START --
{{script_inject|safe}}
// -- END --
{% endif %}

View File

@ -114,11 +114,14 @@ class ScriptView(ValidateServiceOriginsMixin, View):
return render( return render(
self.request, self.request,
"analytics/scripts/page.js", "analytics/scripts/page.js",
context={ context=dict(
"endpoint": endpoint, {
"protocol": protocol, "endpoint": endpoint,
"heartbeat_frequency": heartbeat_frequency, "protocol": protocol,
}, "heartbeat_frequency": heartbeat_frequency,
"script_inject": self.get_script_inject(),
}
),
content_type="application/javascript", content_type="application/javascript",
) )
@ -134,3 +137,12 @@ class ScriptView(ValidateServiceOriginsMixin, View):
return HttpResponse( return HttpResponse(
json.dumps({"status": "OK"}), content_type="application/json" json.dumps({"status": "OK"}), content_type="application/json"
) )
def get_script_inject(self):
service_uuid = self.kwargs.get("service_uuid")
script_inject = cache.get(f"script_inject_{service_uuid}")
if script_inject == None:
service = Service.objects.get(uuid=service_uuid)
script_inject = service.script_inject
cache.set(f"script_inject_{service_uuid}", script_inject, timeout=3600)
return script_inject

View File

@ -0,0 +1,23 @@
# Generated by Django 3.1b1 on 2020-06-28 18:03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0007_service_ignore_robots'),
]
operations = [
migrations.AddField(
model_name='service',
name='script_inject',
field=models.TextField(blank=True, default=''),
),
migrations.AlterField(
model_name='user',
name='first_name',
field=models.CharField(blank=True, max_length=150, verbose_name='first name'),
),
]

View File

@ -73,6 +73,7 @@ class Service(models.Model):
hide_referrer_regex = models.TextField( hide_referrer_regex = models.TextField(
default="", blank=True, validators=[_validate_regex] default="", blank=True, validators=[_validate_regex]
) )
script_inject = models.TextField(default="", blank=True)
class Meta: class Meta:
ordering = ["name", "uuid"] ordering = ["name", "uuid"]

View File

@ -18,6 +18,7 @@ class ServiceForm(forms.ModelForm):
"hide_referrer_regex", "hide_referrer_regex",
"origins", "origins",
"collaborators", "collaborators",
"script_inject"
] ]
widgets = { widgets = {
"name": forms.TextInput(), "name": forms.TextInput(),
@ -27,6 +28,7 @@ class ServiceForm(forms.ModelForm):
"collect_ips": forms.RadioSelect(choices=[(True, "Yes"), (False, "No")]), "collect_ips": forms.RadioSelect(choices=[(True, "Yes"), (False, "No")]),
"ignore_robots": forms.RadioSelect(choices=[(True, "Yes"), (False, "No")]), "ignore_robots": forms.RadioSelect(choices=[(True, "Yes"), (False, "No")]),
"hide_referrer_regex": forms.TextInput(), "hide_referrer_regex": forms.TextInput(),
"script_inject": forms.Textarea(attrs={'class':'font-mono', 'rows': 5})
} }
labels = { labels = {
"origins": "Allowed Hostnames", "origins": "Allowed Hostnames",
@ -35,6 +37,7 @@ class ServiceForm(forms.ModelForm):
"ignored_ips": "Ignored IP addresses", "ignored_ips": "Ignored IP addresses",
"ignore_robots": "Ignore robots", "ignore_robots": "Ignore robots",
"hide_referrer_regex": "Hide specific referrers", "hide_referrer_regex": "Hide specific referrers",
"script_inject": "Additional injected JS",
} }
help_texts = { help_texts = {
"name": _("What should the service be called?"), "name": _("What should the service be called?"),
@ -47,6 +50,7 @@ class ServiceForm(forms.ModelForm):
"ignored_ips": "A comma-separated list of IP addresses or IP ranges (IPv4 and IPv6) to exclude from tracking (e.g., '192.168.0.2, 127.0.0.1/32').", "ignored_ips": "A comma-separated list of IP addresses or IP ranges (IPv4 and IPv6) to exclude from tracking (e.g., '192.168.0.2, 127.0.0.1/32').",
"ignore_robots": "Should sessions generated by bots be excluded from tracking?", "ignore_robots": "Should sessions generated by bots be excluded from tracking?",
"hide_referrer_regex": "Any referrers that match this <a href='https://regexr.com/'>RegEx</a> will not be listed in the referrer summary. Sessions will still be tracked normally. No effect if left blank.", "hide_referrer_regex": "Any referrers that match this <a href='https://regexr.com/'>RegEx</a> will not be listed in the referrer summary. Sessions will still be tracked normally. No effect if left blank.",
"script_inject": "Optional additional JavaScript to inject at the end of the Shynet script. This code will be injected on every page where this service is installed.",
} }
collaborators = forms.CharField( collaborators = forms.CharField(

View File

@ -13,4 +13,5 @@
{{form.ignore_robots|a17t}} {{form.ignore_robots|a17t}}
{{form.hide_referrer_regex|a17t}} {{form.hide_referrer_regex|a17t}}
{{form.origins|a17t}} {{form.origins|a17t}}
{{form.script_inject|a17t}}
</details> </details>

View File

@ -84,6 +84,9 @@ class ServiceUpdateView(
cache.set( cache.set(
f"service_origins_{self.object.uuid}", self.object.origins, timeout=3600 f"service_origins_{self.object.uuid}", self.object.origins, timeout=3600
) )
cache.set(
f"script_inject_{self.object.uuid}", self.object.script_inject, timeout=3600
)
return resp return resp
def get_context_data(self, *args, **kwargs): def get_context_data(self, *args, **kwargs):