+++ /dev/null
-from django.contrib import admin
-from .models import Service
-
-admin.site.register(Service)
--- /dev/null
+# 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',
+ ),
+ ]
-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,
{% extends "base.html" %}
{% load gravatar i18n %}
+{% load use_ssh from services %}
{% block content %}
<aside class="menu">
<p><a {% if menu == 'profile' %}class="active"{% endif %} href="{% url 'accounts_profile' %}">{% trans "Your profile" %}</a></p>
<p><a {% if menu == 'password' %}class="active"{% endif %} href="{% url 'password_change' %}">{% trans "Password change" %}</a></p>
<!--p><a {% if menu == 'email' %}class="active"{% endif %} href="">{% trans "E-mail" %}</a></p-->
- <!--p><a {% if menu == 'ssh' %}class="active"{% endif %} href="">{% trans "SSH keys" %}</a></p-->
+ {% use_ssh as use_ssh %}
+ {% if use_ssh %}
+ <p><a {% if menu == 'ssh' %}class="active"{% endif %} href="{% url 'ssh_keys' %}">{% trans "SSH keys" %}</a></p>
+ {% endif %}
{% endblock %}
<p><a href="{% url 'cas_logout' %}">{% trans "Logout" %}</a></p>
</aside>
INSTALLED_APPS = (
'accounts',
+ 'services',
+ 'ssh_keys',
'cas_provider',
'django_gravatar',
}
-form label, form input, form button {
+label, input, button, textarea {
display: block;
width: 100%;
}
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')),
]
--- /dev/null
+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)
+
+
--- /dev/null
+from django.apps import AppConfig
+
+
+class ServicesConfig(AppConfig):
+ name = 'services'
--- /dev/null
+# 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'),
+ ),
+ ]
--- /dev/null
+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)
--- /dev/null
+{% for user in service.all_users %}# {{ user }}
+{% for key in user.sshkey_set.all %}{{ key.key }}
+{% endfor %}
+{% endfor %}
--- /dev/null
+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()
--- /dev/null
+from django.test import TestCase
+
+# Create your tests here.
--- /dev/null
+from django.urls import path
+from . import views
+
+
+urlpatterns = [
+ path('<int:pk>/ssh/authorized_keys', views.SshAuthorizedKeysView.as_view()),
+]
--- /dev/null
+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
+
--- /dev/null
+from django.contrib import admin
+from .models import SSHKey
+
+
+admin.site.register(SSHKey)
--- /dev/null
+from django.apps import AppConfig
+
+
+class SshKeysConfig(AppConfig):
+ name = 'ssh_keys'
--- /dev/null
+from django import forms
+from .models import SSHKey
+
+
+class SSHKeyForm(forms.ModelForm):
+ model = SSHKey
--- /dev/null
+# 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)),
+ ],
+ ),
+ ]
--- /dev/null
+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']
--- /dev/null
+{% extends "account/base.html" %}
+
+
+{% block menu %}
+ {% with menu='ssh' %}
+ {{ block.super }}
+ {% endwith %}
+{% endblock %}
--- /dev/null
+{% extends 'ssh_keys/base.html' %}
+{% load i18n %}
+
+
+{% block accounts-content %}
+<h1>{% trans "Add SSH key" %}</h1>
+
+<form method='post'>
+ {% csrf_token %}
+ {{ form.as_p }}
+ <button type='submit'>
+ {% trans "Add" %}
+ </button>
+</form>
+{% endblock %}
--- /dev/null
+{% extends "ssh_keys/base.html" %}
+{% load i18n %}
+
+
+{% block accounts-content %}
+<h1>{% trans "Confirm deleting an SSH key" %}</h1>
+
+<p>Are you sure you want to delete this key?</p>
+
+<form method="post">
+ {% csrf_token %}
+ <p>
+ <code class="key">{{ object.key }}</code><br>
+ {% trans "Added at" %} {{ object.created_at }}.
+ </p>
+ <button>
+ {% trans "Delete" %}
+ </button>
+</form>
+{% endblock %}
--- /dev/null
+{% extends "account/base.html" %}
+{% load i18n %}
+
+
+{% block menu %}
+ {% with menu="ssh" %}
+ {{ block.super }}
+ {% endwith %}
+{% endblock %}
+
+
+{% block accounts-content %}
+ <h1>{% trans "SSH keys" %}</h1>
+
+ {% for key in object_list %}
+<p>
+ <code style='word-wrap:break-word'>{{ key.key }}</code><br>
+ {% trans "Added at" %} {{ key.created_at }}<br>
+ <a href="{% url 'ssh_keys_delete' key.id %}">
+ {% trans "Delete" %}
+ </a>
+</p>
+<hr>
+ {% endfor %}
+
+ <a href="{% url 'ssh_keys_add' %}">
+ {% trans "Add" %}
+ </a>
+
+{% endblock %}
--- /dev/null
+from django.urls import path
+from . import views
+
+
+urlpatterns = [
+ path('', views.SSHKeysView.as_view(), name='ssh_keys'),
+ path('<int:pk>/delete/', views.DeleteSSHKeyView.as_view(), name='ssh_keys_delete'),
+ path('add/', views.AddSSHKeyView.as_view(), name='ssh_keys_add'),
+
+]
--- /dev/null
+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)
+