Merge pull request #1 from nathanielksmith/master

support multiple pubkeys, misc
pull/16/head
Nathaniel Smith 2017-01-13 22:47:33 -08:00 committed by GitHub
commit 871474ab6d
6 changed files with 95 additions and 23 deletions

View File

@ -4,10 +4,11 @@ _Being an adminstrative and user-signup tool for [https://tilde.town]_.
## Features ## Features
(None of these are actually implemented yet) * User signup form (✓)
* with client-side key generation (only server-side key **validation** at this point)
* User signup form with client-side key generation * Guestbook (✓)
* User account management * Helpdesk (✓)
* User account management (admin only)
* Start/stop services * Start/stop services
* Cost reporting using AWS * Cost reporting using AWS
* Status monitoring * Status monitoring

View File

@ -1,6 +1,16 @@
from django.contrib import admin from django.contrib import admin
from .models import Townie from django.contrib.auth.models import User
from django.contrib.auth.models import Group
from .models import Townie, Pubkey
admin.site.unregister(User)
admin.site.unregister(Group)
class PubkeyInline(admin.TabularInline):
model = Pubkey
@admin.register(Townie) @admin.register(Townie)
class TownieAdmin(admin.ModelAdmin): class TownieAdmin(admin.ModelAdmin):
inlines = [PubkeyInline]
pass pass

View File

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.2 on 2017-01-13 21:26
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('users', '0006_townie_reasons'),
]
operations = [
migrations.AlterModelOptions(
name='townie',
options={'verbose_name': 'Townie', 'verbose_name_plural': 'Townies'},
),
migrations.RemoveField(
model_name='townie',
name='pubkey_type',
),
]

View File

@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.2 on 2017-01-13 21:28
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('users', '0007_auto_20170113_2126'),
]
operations = [
migrations.CreateModel(
name='Pubkey',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('key_type', models.CharField(choices=[('ssh-rsa', 'ssh-rsa'), ('ssh-dss', 'ssh-dss')], max_length=15)),
('key', models.TextField()),
],
),
migrations.RemoveField(
model_name='townie',
name='pubkey',
),
migrations.AddField(
model_name='pubkey',
name='townie',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.Townie'),
),
]

View File

@ -1,9 +1,10 @@
import re import re
from django.db.models import Model
from django.db.models.signals import post_save from django.db.models.signals import post_save
from django.dispatch import receiver from django.dispatch import receiver
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db.models import TextField, BooleanField, CharField from django.db.models import TextField, BooleanField, CharField, ForeignKey
SSH_TYPE_CHOICES = ( SSH_TYPE_CHOICES = (
@ -15,24 +16,13 @@ SSH_TYPE_CHOICES = (
class Townie(User): class Townie(User):
"""Both an almost normal Django User as well as an abstraction over a """Both an almost normal Django User as well as an abstraction over a
system user.""" system user."""
pubkey = TextField(blank=False, null=False) class Meta:
verbose_name = 'Townie'
verbose_name_plural = 'Townies'
shell = CharField(max_length=50, default="/bin/bash") shell = CharField(max_length=50, default="/bin/bash")
reviewed = BooleanField(default=False) reviewed = BooleanField(default=False)
reasons = TextField(blank=True, null=False, default='') reasons = TextField(blank=True, null=False, default='')
displayname = CharField(max_length=100, blank=False, null=False) displayname = CharField(max_length=100, blank=False, null=False)
pubkey_type = CharField(max_length=15,
blank=False,
null=False,
choices=SSH_TYPE_CHOICES)
@property
def home_path(self):
return "/home/{}".format(self.username)
def accept(self):
"""Sets self.pending to False. Indicates the user has been signed up
after review."""
self.pending = False
# TODO consider a generic ensure method that syncs this model with the # TODO consider a generic ensure method that syncs this model with the
# system. there will likely be things besides shell that we want to keep # system. there will likely be things besides shell that we want to keep
@ -42,6 +32,17 @@ class Townie(User):
is.""" is."""
raise NotImplementedError() raise NotImplementedError()
class Pubkey(Model):
key_type = CharField(max_length=15,
blank=False,
null=False,
choices=SSH_TYPE_CHOICES,
)
key = TextField(blank=False, null=False)
townie = ForeignKey(Townie)
@receiver(post_save, sender=Townie) @receiver(post_save, sender=Townie)
def sync_system_state(sender, instance, created, **kwargs): def sync_system_state(sender, instance, created, **kwargs):
if created: if created:

View File

@ -1,6 +1,7 @@
import re import re
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import transaction
from django.forms import Form, CharField, EmailField, Textarea, ChoiceField, BooleanField from django.forms import Form, CharField, EmailField, Textarea, ChoiceField, BooleanField
from django.http import HttpResponse from django.http import HttpResponse
from django.shortcuts import redirect from django.shortcuts import redirect
@ -8,21 +9,24 @@ from django.views.generic import TemplateView
from django.views.generic.edit import FormView from django.views.generic.edit import FormView
from .forms import TownieForm from .forms import TownieForm
from .models import Townie from .models import Townie, Pubkey
class SignupView(FormView): class SignupView(FormView):
form_class = TownieForm form_class = TownieForm
template_name = 'users/signup.html' template_name = 'users/signup.html'
@transaction.atomic
def form_valid(self, form): def form_valid(self, form):
del form.cleaned_data['captcha'] del form.cleaned_data['captcha']
del form.cleaned_data['aup'] del form.cleaned_data['aup']
pubkey = Pubkey(key=form.cleaned_data.pop('pubkey'),
key_type=form.cleaned_data.pop('pubkey_type'))
t = Townie(**form.cleaned_data) t = Townie(**form.cleaned_data)
t.set_unusable_password() t.set_unusable_password()
t.save() t.save()
pubkey.townie = t
pubkey.save()
return redirect('users:thanks') return redirect('users:thanks')