mirror of https://github.com/jumpserver/jumpserver
Add form validate
parent
072da114db
commit
a75e1db970
|
@ -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')}),
|
||||||
|
|
|
@ -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'))
|
||||||
|
|
|
@ -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 }}
|
||||||
|
|
||||||
|
|
|
@ -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'} },
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue