Add form validate

pull/530/head
ibuler 2016-11-07 00:39:26 +08:00
parent 072da114db
commit a75e1db970
8 changed files with 122 additions and 72 deletions

View File

@ -1,8 +1,9 @@
# coding:utf-8 # coding:utf-8
from django import forms from django import forms
from django.utils.translation import gettext_lazy as _
from .models import IDC, Asset, AssetGroup, AdminUser, SystemUser, Tag from .models import IDC, Asset, AssetGroup, AdminUser, SystemUser, Tag
from django.utils.translation import gettext_lazy as _ from common.utils import validate_ssh_private_key, ssh_pubkey_gen
# class AssetForm(forms.ModelForm): # class AssetForm(forms.ModelForm):
@ -141,7 +142,6 @@ class AdminUserForm(forms.ModelForm):
widget=forms.SelectMultiple( widget=forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Select assets')}) attrs={'class': 'select2', 'data-placeholder': _('Select assets')})
) )
auto_generate_key = forms.BooleanField(required=True, initial=True)
# Form field name can not start with `_`, so redefine it, # Form field name can not start with `_`, so redefine it,
password = forms.CharField(widget=forms.PasswordInput, max_length=100, min_length=8, strip=True, password = forms.CharField(widget=forms.PasswordInput, max_length=100, min_length=8, strip=True,
help_text=_('If also set private key, use that first'), required=False) help_text=_('If also set private key, use that first'), required=False)
@ -166,21 +166,36 @@ class AdminUserForm(forms.ModelForm):
# Because we define custom field, so we need rewrite :method: `save` # Because we define custom field, so we need rewrite :method: `save`
admin_user = super(AdminUserForm, self).save(commit=commit) admin_user = super(AdminUserForm, self).save(commit=commit)
password = self.cleaned_data['password'] password = self.cleaned_data['password']
private_key_file = self.cleaned_data['private_key_file'] private_key = self.cleaned_data['private_key_file']
public_key = ssh_pubkey_gen(private_key)
if password: if password:
admin_user.password = password admin_user.password = password
print(password) if private_key:
# Todo: Validate private key file, and generate public key admin_user.private_key = private_key
# Todo: Auto generate private key and public key admin_user.public_key = public_key
if private_key_file:
admin_user.private_key = private_key_file.read()
admin_user.save() admin_user.save()
return self.instance return admin_user
def clean_private_key_file(self):
private_key_file = self.cleaned_data['private_key_file']
if private_key_file:
private_key = private_key_file.read()
if not validate_ssh_private_key(private_key):
raise forms.ValidationError(_('Invalid private key'))
return private_key
return private_key_file
def clean(self):
password = self.cleaned_data['password']
private_key_file = self.cleaned_data.get('private_key_file', '')
if not (password or private_key_file):
raise forms.ValidationError(_('Password and private key file must be input one'))
class Meta: class Meta:
model = AdminUser model = AdminUser
fields = ['name', 'username', 'auto_generate_key', 'password', 'private_key_file', 'as_default', 'comment'] fields = ['name', 'username', 'password', 'private_key_file', 'comment']
widgets = { widgets = {
'name': forms.TextInput(attrs={'placeholder': _('Name')}), 'name': forms.TextInput(attrs={'placeholder': _('Name')}),
'username': forms.TextInput(attrs={'placeholder': _('Username')}), 'username': forms.TextInput(attrs={'placeholder': _('Username')}),

View File

@ -6,8 +6,9 @@ from django.db import models
from django.core import serializers from django.core import serializers
import logging import logging
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ValidationError
from common.utils import signer from common.utils import signer, validate_ssh_private_key
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -91,13 +92,21 @@ class AssetExtend(models.Model):
unique_together = ('key', 'value') unique_together = ('key', 'value')
def private_key_validator(value):
if not validate_ssh_private_key(value):
raise ValidationError(
_('%(value)s is not an even number'),
params={'value': value},
)
class AdminUser(models.Model): class AdminUser(models.Model):
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name')) name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
username = models.CharField(max_length=16, verbose_name=_('Username')) username = models.CharField(max_length=16, verbose_name=_('Username'))
_password = models.CharField(max_length=256, blank=True, verbose_name=_('Password')) _password = models.CharField(max_length=256, blank=True, null=True, verbose_name=_('Password'))
_private_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH private key')) _private_key = models.CharField(max_length=4096, blank=True, null=True, verbose_name=_('SSH private key'),
validators=[private_key_validator,])
_public_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH public key')) _public_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH public key'))
as_default = models.BooleanField(default=False, verbose_name=_('As default'))
comment = models.TextField(blank=True, verbose_name=_('Comment')) comment = models.TextField(blank=True, verbose_name=_('Comment'))
date_created = models.DateTimeField(auto_now_add=True, null=True) date_created = models.DateTimeField(auto_now_add=True, null=True)
created_by = models.CharField(max_length=32, null=True, verbose_name=_('Created by')) created_by = models.CharField(max_length=32, null=True, verbose_name=_('Created by'))

View File

@ -31,20 +31,8 @@
{% csrf_token %} {% csrf_token %}
{{ form.name|bootstrap_horizontal }} {{ form.name|bootstrap_horizontal }}
{{ form.username|bootstrap_horizontal }} {{ form.username|bootstrap_horizontal }}
<div class="form-group">
<label for="{{ form.auto_generate_key.id_for_label }}" class="col-sm-2 control-label">{% trans 'Auto generate key' %}</label>
<div class="col-sm-8">
{{ form.auto_generate_key}}
</div>
</div>
{{ form.password|bootstrap_horizontal }} {{ form.password|bootstrap_horizontal }}
{{ form.private_key_file|bootstrap_horizontal }} {{ form.private_key_file|bootstrap_horizontal }}
<div class="form-group">
<label for="{{ form.as_default.id_for_label }}" class="col-sm-2 control-label">{% trans 'As default' %}</label>
<div class="col-sm-8">
{{ form.as_default}}
</div>
</div>
{{ form.assets|bootstrap_horizontal }} {{ form.assets|bootstrap_horizontal }}
{{ form.comment|bootstrap_horizontal }} {{ form.comment|bootstrap_horizontal }}

View File

@ -1,22 +1,10 @@
{% extends '_base_list.html' %} {% extends '_base_list.html' %}
{% load i18n static %} {% load i18n static %}
{% block custom_head_css_js %} {% block table_search %}
{{ block.super }}
<style>
div.dataTables_wrapper div.dataTables_filter,
.dataTables_length {
float: right !important;
}
div.dataTables_wrapper div.dataTables_filter {
margin-left: 15px;
}
</style>
{% endblock %} {% endblock %}
{% block table_search %}{% endblock %}
{% block table_container %} {% block table_container %}
<div class="uc pull-left m-l-5 m-r-5"> <div class="uc pull-left m-l-5 m-r-5">
<a href="{% url "assets:idc-create" %}" class="btn btn-sm btn-primary"> {% trans "Create IDC" %} </a> <a href="{% url "assets:admin-user-create" %}" class="btn btn-sm btn-primary"> {% trans "Create admin user" %} </a>
</div> </div>
<table class="table table-striped table-bordered table-hover " id="admin_user_list_table" > <table class="table table-striped table-bordered table-hover " id="admin_user_list_table" >
<thead> <thead>
@ -29,7 +17,7 @@
<th class="text-center">{% trans 'Asset num' %}</th> <th class="text-center">{% trans 'Asset num' %}</th>
<th class="text-center">{% trans 'Lost connection' %}</th> <th class="text-center">{% trans 'Lost connection' %}</th>
<th class="text-center">{% trans 'Comment' %}</th> <th class="text-center">{% trans 'Comment' %}</th>
<th class="text-center"></th> <th class="text-center">{% trans 'Action' %}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -59,9 +47,10 @@ $(document).ready(function(){
{# }#} {# }#}
{# }},#} {# }},#}
{targets: 6, createdCell: function (td, cellData, rowData) { {targets: 6, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "assets:admin-user-update" pk=99991937 %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace('99991937', cellData); var script_btn = '<a href="{% url "assets:admin-user-update" pk=99991937 %}" class="btn btn-xs btn-primary">{% trans "Script" %}</a>'.replace('99991937', cellData);
var update_btn = '<a href="{% url "assets:admin-user-update" pk=99991937 %}" class="btn btn-xs m-l-xs btn-info">{% trans "Update" %}</a>'.replace('99991937', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_admin_user_delete" data-uid="99991937">{% trans "Delete" %}</a>'.replace('99991937', cellData); var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_admin_user_delete" data-uid="99991937">{% trans "Delete" %}</a>'.replace('99991937', cellData);
$(td).html(update_btn + del_btn) $(td).html(script_btn + update_btn + del_btn)
}}], }}],
ajax_url: '{% url "assets:api-admin-user-list" %}', ajax_url: '{% url "assets:api-admin-user-list" %}',
columns: [{data: function(){return ""}}, {data: "name" }, {data: "username" }, {data: "assets_amount" }, {data: function () {return 'lost'} }, columns: [{data: function(){return ""}}, {data: "name" }, {data: "username" }, {data: "assets_amount" }, {data: function () {return 'lost'} },

View File

@ -483,6 +483,9 @@ class AdminUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateVie
)) ))
return success_message return success_message
def form_invalid(self, form):
return super(AdminUserCreateView, self).form_invalid(form)
class AdminUserUpdateView(AdminUserRequiredMixin, UpdateView): class AdminUserUpdateView(AdminUserRequiredMixin, UpdateView):
model = AdminUser model = AdminUser

View File

@ -3,11 +3,14 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from six import string_types from six import string_types
import os
from itertools import chain from itertools import chain
import string import string
import logging import logging
import datetime import datetime
import paramiko
import paramiko
from itsdangerous import TimedJSONWebSignatureSerializer, JSONWebSignatureSerializer, \ from itsdangerous import TimedJSONWebSignatureSerializer, JSONWebSignatureSerializer, \
BadSignature, SignatureExpired BadSignature, SignatureExpired
from django.shortcuts import reverse as dj_reverse from django.shortcuts import reverse as dj_reverse
@ -15,6 +18,11 @@ from django.conf import settings
from django.core import signing from django.core import signing
from django.utils import timezone from django.utils import timezone
try:
import cStringIO as StringIO
except ImportError:
import StringIO
SECRET_KEY = settings.SECRET_KEY SECRET_KEY = settings.SECRET_KEY
@ -162,4 +170,70 @@ def timesince(dt, since='', default="just now"):
return default return default
def ssh_key_string_to_obj(text):
key_f = StringIO.StringIO(text)
key = None
try:
key = paramiko.RSAKey.from_private_key(key_f)
except paramiko.SSHException:
pass
try:
key = paramiko.DSSKey.from_private_key(key_f)
except paramiko.SSHException:
pass
return key
def ssh_pubkey_gen(private_key=None, username='jumpserver', hostname='localhost'):
if isinstance(private_key, string_types):
private_key = ssh_key_string_to_obj(private_key)
if not isinstance(private_key, (paramiko.RSAKey, paramiko.DSSKey)):
raise IOError('Invalid private key')
public_key = "%(key_type)s %(key_content)s %(username)s@%(hostname)s" % {
'key_type': private_key.get_name(),
'key_content': private_key.get_base64(),
'username': username,
'hostname': hostname,
}
return public_key
def ssh_key_gen(length=2048, type='rsa', password=None, username='jumpserver', hostname=None):
"""Generate user ssh private and public key
Use paramiko RSAKey generate it.
:return private key str and public key str
"""
if hostname is None:
hostname = os.uname()[1]
f = StringIO.StringIO()
try:
if type == 'rsa':
private_key_obj = paramiko.RSAKey.generate(length)
elif type == 'dsa':
private_key_obj = paramiko.DSSKey.generate(length)
else:
raise IOError('SSH private key must be `rsa` or `dsa`')
private_key_obj.write_private_key(f, password=password)
private_key = f.getvalue()
public_key = ssh_pubkey_gen(private_key_obj, username=username, hostname=hostname)
return private_key, public_key
except IOError:
raise IOError('These is error when generate ssh key.')
def validate_ssh_private_key(text):
key = ssh_key_string_to_obj(text)
if key is None:
return False
else:
return True
signer = Signer() signer = Signer()

View File

@ -34,36 +34,7 @@ class AdminUserRequiredMixin(UserPassesTestMixin):
return self.request.user.is_staff return self.request.user.is_staff
def ssh_key_gen(length=2048, password=None, username='root', hostname=None):
"""Generate user ssh private and public key
Use paramiko RSAKey generate it.
"""
if hostname is None:
hostname = os.uname()[1]
f = StringIO.StringIO()
try:
logger.debug(_('Begin to generate ssh private key ...'))
private_key_obj = RSAKey.generate(length)
private_key_obj.write_private_key(f, password=password)
private_key = f.getvalue()
public_key = "%(key_type)s %(key_content)s %(username)s@%(hostname)s" % {
'key_type': private_key_obj.get_name(),
'key_content': private_key_obj.get_base64(),
'username': username,
'hostname': hostname,
}
logger.debug(_('Finish to generate ssh private key ...'))
return private_key, public_key
except IOError:
raise IOError(_('These is error when generate ssh key.'))
def user_add_success_next(user): def user_add_success_next(user):

View File

@ -11,3 +11,4 @@ django-simple-captcha==0.5.2
django-formtools==1.0 django-formtools==1.0
sshpubkeys==2.2.0 sshpubkeys==2.2.0
djangorestframework-bulk==0.2.1 djangorestframework-bulk==0.2.1
paramiko==2.0.2