From 5c901ee485c3abdf11dec71bdb7534f1f6a9eaa9 Mon Sep 17 00:00:00 2001 From: Radek Czajka Date: Sat, 30 Mar 2019 22:49:27 +0100 Subject: [PATCH] Add services and SSH keys. --- src/accounts/admin.py | 4 -- .../migrations/0003_auto_20190330_1311.py | 38 ++++++++++++++++ src/accounts/models.py | 22 --------- src/accounts/templates/account/base.html | 6 ++- src/cas/settings.py | 2 + src/cas/static/css/main.css | 2 +- src/cas/urls.py | 2 + src/services/__init__.py | 0 src/services/admin.py | 18 ++++++++ src/services/apps.py | 5 +++ src/services/migrations/0001_initial.py | 45 +++++++++++++++++++ src/services/migrations/__init__.py | 0 src/services/models.py | 33 ++++++++++++++ .../services/ssh_authorized_keys.txt | 4 ++ src/services/templatetags/__init__.py | 0 src/services/templatetags/services.py | 12 +++++ src/services/tests.py | 3 ++ src/services/urls.py | 7 +++ src/services/views.py | 15 +++++++ src/ssh_keys/__init__.py | 0 src/ssh_keys/admin.py | 5 +++ src/ssh_keys/apps.py | 5 +++ src/ssh_keys/forms.py | 6 +++ src/ssh_keys/migrations/0001_initial.py | 26 +++++++++++ src/ssh_keys/migrations/__init__.py | 0 src/ssh_keys/models.py | 11 +++++ src/ssh_keys/templates/ssh_keys/base.html | 8 ++++ .../templates/ssh_keys/sshkey_add.html | 15 +++++++ .../ssh_keys/sshkey_confirm_delete.html | 20 +++++++++ .../templates/ssh_keys/sshkey_list.html | 30 +++++++++++++ src/ssh_keys/urls.py | 10 +++++ src/ssh_keys/views.py | 27 +++++++++++ 32 files changed, 353 insertions(+), 28 deletions(-) delete mode 100644 src/accounts/admin.py create mode 100644 src/accounts/migrations/0003_auto_20190330_1311.py create mode 100644 src/services/__init__.py create mode 100644 src/services/admin.py create mode 100644 src/services/apps.py create mode 100644 src/services/migrations/0001_initial.py create mode 100644 src/services/migrations/__init__.py create mode 100644 src/services/models.py create mode 100644 src/services/templates/services/ssh_authorized_keys.txt create mode 100644 src/services/templatetags/__init__.py create mode 100644 src/services/templatetags/services.py create mode 100644 src/services/tests.py create mode 100644 src/services/urls.py create mode 100644 src/services/views.py create mode 100644 src/ssh_keys/__init__.py create mode 100644 src/ssh_keys/admin.py create mode 100644 src/ssh_keys/apps.py create mode 100644 src/ssh_keys/forms.py create mode 100644 src/ssh_keys/migrations/0001_initial.py create mode 100644 src/ssh_keys/migrations/__init__.py create mode 100644 src/ssh_keys/models.py create mode 100644 src/ssh_keys/templates/ssh_keys/base.html create mode 100644 src/ssh_keys/templates/ssh_keys/sshkey_add.html create mode 100644 src/ssh_keys/templates/ssh_keys/sshkey_confirm_delete.html create mode 100644 src/ssh_keys/templates/ssh_keys/sshkey_list.html create mode 100644 src/ssh_keys/urls.py create mode 100644 src/ssh_keys/views.py diff --git a/src/accounts/admin.py b/src/accounts/admin.py deleted file mode 100644 index f4af527..0000000 --- a/src/accounts/admin.py +++ /dev/null @@ -1,4 +0,0 @@ -from django.contrib import admin -from .models import Service - -admin.site.register(Service) diff --git a/src/accounts/migrations/0003_auto_20190330_1311.py b/src/accounts/migrations/0003_auto_20190330_1311.py new file mode 100644 index 0000000..1270244 --- /dev/null +++ b/src/accounts/migrations/0003_auto_20190330_1311.py @@ -0,0 +1,38 @@ +# Generated by Django 2.1.7 on 2019-03-30 13:11 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0002_servicegroup_serviceuser'), + ] + + operations = [ + migrations.RemoveField( + model_name='servicegroup', + name='group', + ), + migrations.RemoveField( + model_name='servicegroup', + name='service', + ), + migrations.RemoveField( + model_name='serviceuser', + name='service', + ), + migrations.RemoveField( + model_name='serviceuser', + name='user', + ), + migrations.DeleteModel( + name='Service', + ), + migrations.DeleteModel( + name='ServiceGroup', + ), + migrations.DeleteModel( + name='ServiceUser', + ), + ] diff --git a/src/accounts/models.py b/src/accounts/models.py index b0bd496..b0be8d4 100644 --- a/src/accounts/models.py +++ b/src/accounts/models.py @@ -1,28 +1,6 @@ -from django.conf import settings -from django.db import models from cas_provider.signals import cas_collect_custom_attributes -class Service(models.Model): - ordering = models.IntegerField() - name = models.CharField(max_length=255) - url = models.URLField() - image = models.ImageField(upload_to='accounts/service/') - - class Meta: - ordering = ('ordering', ) - - -class ServiceUser(models.Model): - service = models.ForeignKey(Service, on_delete=models.CASCADE) - user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) - - -class ServiceGroup(models.Model): - service = models.ForeignKey(Service, on_delete=models.CASCADE) - group = models.ForeignKey('auth.Group', on_delete=models.CASCADE) - - def user_attributes(sender, user, **kwargs): return { 'firstname': user.first_name, diff --git a/src/accounts/templates/account/base.html b/src/accounts/templates/account/base.html index c15aaad..18aa501 100644 --- a/src/accounts/templates/account/base.html +++ b/src/accounts/templates/account/base.html @@ -1,5 +1,6 @@ {% extends "base.html" %} {% load gravatar i18n %} +{% load use_ssh from services %} {% block content %} diff --git a/src/cas/settings.py b/src/cas/settings.py index 62725c7..d660614 100644 --- a/src/cas/settings.py +++ b/src/cas/settings.py @@ -84,6 +84,8 @@ LOCALE_PATHS = ( INSTALLED_APPS = ( 'accounts', + 'services', + 'ssh_keys', 'cas_provider', 'django_gravatar', diff --git a/src/cas/static/css/main.css b/src/cas/static/css/main.css index be4aa1c..3894e43 100644 --- a/src/cas/static/css/main.css +++ b/src/cas/static/css/main.css @@ -31,7 +31,7 @@ a:hover { } -form label, form input, form button { +label, input, button, textarea { display: block; width: 100%; } diff --git a/src/cas/urls.py b/src/cas/urls.py index 297b82b..abe01e8 100644 --- a/src/cas/urls.py +++ b/src/cas/urls.py @@ -16,6 +16,8 @@ urlpatterns = [ path('admin/', admin.site.urls), path('accounts/', include('accounts.urls')), + path('services/', include('services.urls')), + path('ssh/', include('ssh_keys.urls')), path('auth/', include('django.contrib.auth.urls')), ] diff --git a/src/services/__init__.py b/src/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/services/admin.py b/src/services/admin.py new file mode 100644 index 0000000..ae83a8f --- /dev/null +++ b/src/services/admin.py @@ -0,0 +1,18 @@ +from django.contrib import admin +from . import models + + +class HookInline(admin.TabularInline): + model = models.Hook + + +class ServiceAdmin(admin.ModelAdmin): + filter_horizontal = ['groups', 'users'] + inlines = [ + HookInline, + ] + + +admin.site.register(models.Service, ServiceAdmin) + + diff --git a/src/services/apps.py b/src/services/apps.py new file mode 100644 index 0000000..e40a94f --- /dev/null +++ b/src/services/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class ServicesConfig(AppConfig): + name = 'services' diff --git a/src/services/migrations/0001_initial.py b/src/services/migrations/0001_initial.py new file mode 100644 index 0000000..ce32d07 --- /dev/null +++ b/src/services/migrations/0001_initial.py @@ -0,0 +1,45 @@ +# Generated by Django 2.1.7 on 2019-03-30 13:31 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('auth', '0009_alter_user_last_name_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='Hook', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('email', models.CharField(max_length=255)), + ], + ), + migrations.CreateModel( + name='Service', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255)), + ('url', models.URLField(blank=True)), + ('key', models.CharField(blank=True, max_length=255)), + ('uses_ssh', models.BooleanField(default=False)), + ('groups', models.ManyToManyField(blank=True, to='auth.Group')), + ('users', models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'ordering': ('name',), + }, + ), + migrations.AddField( + model_name='hook', + name='service', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='services.Service'), + ), + ] diff --git a/src/services/migrations/__init__.py b/src/services/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/services/models.py b/src/services/models.py new file mode 100644 index 0000000..6e945ce --- /dev/null +++ b/src/services/models.py @@ -0,0 +1,33 @@ +from django.conf import settings +from django.contrib.auth.models import User +from django.db import models + + +class Service(models.Model): + name = models.CharField(max_length=255) + url = models.URLField(blank=True) + key = models.CharField(max_length=255, blank=True) + uses_ssh = models.BooleanField(default=False) + groups = models.ManyToManyField('auth.Group', blank=True) + users = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True) + + class Meta: + ordering = ('name', ) + + def __str__(self): + return self.name + + def all_users(self): + return User.objects.filter( + models.Q(service=self) | + models.Q(groups__service=self) + ).distinct() + + @classmethod + def for_user(cls, user): + return cls.objects.filter(models.Q(users=user) | models.Q(groups__user=user)) + + +class Hook(models.Model): + service = models.ForeignKey(Service, on_delete=models.CASCADE) + email = models.CharField(max_length=255) diff --git a/src/services/templates/services/ssh_authorized_keys.txt b/src/services/templates/services/ssh_authorized_keys.txt new file mode 100644 index 0000000..835c2e5 --- /dev/null +++ b/src/services/templates/services/ssh_authorized_keys.txt @@ -0,0 +1,4 @@ +{% for user in service.all_users %}# {{ user }} +{% for key in user.sshkey_set.all %}{{ key.key }} +{% endfor %} +{% endfor %} diff --git a/src/services/templatetags/__init__.py b/src/services/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/services/templatetags/services.py b/src/services/templatetags/services.py new file mode 100644 index 0000000..959e0f8 --- /dev/null +++ b/src/services/templatetags/services.py @@ -0,0 +1,12 @@ +from django.template import Library +from ..models import Service + + +register = Library() + + +@register.simple_tag(takes_context=True) +def use_ssh(context): + user = context['request'].user + if user.is_anonymous: return True + return Service.for_user(user).filter(uses_ssh=True).exists() diff --git a/src/services/tests.py b/src/services/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/src/services/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/src/services/urls.py b/src/services/urls.py new file mode 100644 index 0000000..fc34dcc --- /dev/null +++ b/src/services/urls.py @@ -0,0 +1,7 @@ +from django.urls import path +from . import views + + +urlpatterns = [ + path('/ssh/authorized_keys', views.SshAuthorizedKeysView.as_view()), +] diff --git a/src/services/views.py b/src/services/views.py new file mode 100644 index 0000000..f599592 --- /dev/null +++ b/src/services/views.py @@ -0,0 +1,15 @@ +from django.views.generic import DetailView +from .models import Service + + +class SshAuthorizedKeysView(DetailView): + model = Service + template_name = 'services/ssh_authorized_keys.txt' + content_type = 'text/plain' + + def get_object(self): + obj = super().get_object() + if self.request.GET.get('key') != obj.key: + obj = None + return obj + diff --git a/src/ssh_keys/__init__.py b/src/ssh_keys/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/ssh_keys/admin.py b/src/ssh_keys/admin.py new file mode 100644 index 0000000..b907416 --- /dev/null +++ b/src/ssh_keys/admin.py @@ -0,0 +1,5 @@ +from django.contrib import admin +from .models import SSHKey + + +admin.site.register(SSHKey) diff --git a/src/ssh_keys/apps.py b/src/ssh_keys/apps.py new file mode 100644 index 0000000..6663b50 --- /dev/null +++ b/src/ssh_keys/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class SshKeysConfig(AppConfig): + name = 'ssh_keys' diff --git a/src/ssh_keys/forms.py b/src/ssh_keys/forms.py new file mode 100644 index 0000000..f3c0f7c --- /dev/null +++ b/src/ssh_keys/forms.py @@ -0,0 +1,6 @@ +from django import forms +from .models import SSHKey + + +class SSHKeyForm(forms.ModelForm): + model = SSHKey diff --git a/src/ssh_keys/migrations/0001_initial.py b/src/ssh_keys/migrations/0001_initial.py new file mode 100644 index 0000000..61ca8ba --- /dev/null +++ b/src/ssh_keys/migrations/0001_initial.py @@ -0,0 +1,26 @@ +# Generated by Django 2.1.7 on 2019-03-30 13:11 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='SSHKey', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('key', models.TextField()), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/src/ssh_keys/migrations/__init__.py b/src/ssh_keys/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/ssh_keys/models.py b/src/ssh_keys/models.py new file mode 100644 index 0000000..960a5ec --- /dev/null +++ b/src/ssh_keys/models.py @@ -0,0 +1,11 @@ +from django.conf import settings +from django.db import models + + +class SSHKey(models.Model): + user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) + key = models.TextField() + created_at = models.DateTimeField(auto_now_add=True) + + class Meta: + ordering = ['created_at'] diff --git a/src/ssh_keys/templates/ssh_keys/base.html b/src/ssh_keys/templates/ssh_keys/base.html new file mode 100644 index 0000000..2e21241 --- /dev/null +++ b/src/ssh_keys/templates/ssh_keys/base.html @@ -0,0 +1,8 @@ +{% extends "account/base.html" %} + + +{% block menu %} + {% with menu='ssh' %} + {{ block.super }} + {% endwith %} +{% endblock %} diff --git a/src/ssh_keys/templates/ssh_keys/sshkey_add.html b/src/ssh_keys/templates/ssh_keys/sshkey_add.html new file mode 100644 index 0000000..1b67d03 --- /dev/null +++ b/src/ssh_keys/templates/ssh_keys/sshkey_add.html @@ -0,0 +1,15 @@ +{% extends 'ssh_keys/base.html' %} +{% load i18n %} + + +{% block accounts-content %} +

{% trans "Add SSH key" %}

+ +
+ {% csrf_token %} + {{ form.as_p }} + +
+{% endblock %} diff --git a/src/ssh_keys/templates/ssh_keys/sshkey_confirm_delete.html b/src/ssh_keys/templates/ssh_keys/sshkey_confirm_delete.html new file mode 100644 index 0000000..7f3d109 --- /dev/null +++ b/src/ssh_keys/templates/ssh_keys/sshkey_confirm_delete.html @@ -0,0 +1,20 @@ +{% extends "ssh_keys/base.html" %} +{% load i18n %} + + +{% block accounts-content %} +

{% trans "Confirm deleting an SSH key" %}

+ +

Are you sure you want to delete this key?

+ +
+ {% csrf_token %} +

+ {{ object.key }}
+ {% trans "Added at" %} {{ object.created_at }}. +

+ +
+{% endblock %} diff --git a/src/ssh_keys/templates/ssh_keys/sshkey_list.html b/src/ssh_keys/templates/ssh_keys/sshkey_list.html new file mode 100644 index 0000000..a4f5155 --- /dev/null +++ b/src/ssh_keys/templates/ssh_keys/sshkey_list.html @@ -0,0 +1,30 @@ +{% extends "account/base.html" %} +{% load i18n %} + + +{% block menu %} + {% with menu="ssh" %} + {{ block.super }} + {% endwith %} +{% endblock %} + + +{% block accounts-content %} +

{% trans "SSH keys" %}

+ + {% for key in object_list %} +

+ {{ key.key }}
+ {% trans "Added at" %} {{ key.created_at }}
+ + {% trans "Delete" %} + +

+
+ {% endfor %} + + + {% trans "Add" %} + + +{% endblock %} diff --git a/src/ssh_keys/urls.py b/src/ssh_keys/urls.py new file mode 100644 index 0000000..deabb5c --- /dev/null +++ b/src/ssh_keys/urls.py @@ -0,0 +1,10 @@ +from django.urls import path +from . import views + + +urlpatterns = [ + path('', views.SSHKeysView.as_view(), name='ssh_keys'), + path('/delete/', views.DeleteSSHKeyView.as_view(), name='ssh_keys_delete'), + path('add/', views.AddSSHKeyView.as_view(), name='ssh_keys_add'), + +] diff --git a/src/ssh_keys/views.py b/src/ssh_keys/views.py new file mode 100644 index 0000000..684dcf6 --- /dev/null +++ b/src/ssh_keys/views.py @@ -0,0 +1,27 @@ +from django.contrib.auth.mixins import LoginRequiredMixin +from django.views.generic import ListView, CreateView, DeleteView +from .models import SSHKey + + +class SSHKeysView(LoginRequiredMixin, ListView): + def get_queryset(self): + return SSHKey.objects.filter(user=self.request.user) + + +class AddSSHKeyView(LoginRequiredMixin, CreateView): + fields = ['key'] + model = SSHKey + success_url = '/ssh/' + template_name = 'ssh_keys/sshkey_add.html' + + def form_valid(self, form): + form.instance.user = self.request.user + return super().form_valid(form) + + +class DeleteSSHKeyView(LoginRequiredMixin, DeleteView): + success_url = '/ssh/' + + def get_queryset(self): + return SSHKey.objects.filter(user=self.request.user) + -- 2.20.1