diff --git a/apps/assets/forms.py b/apps/assets/forms.py index 1d62eadcb..221420ce0 100644 --- a/apps/assets/forms.py +++ b/apps/assets/forms.py @@ -90,6 +90,7 @@ class AdminUserForm(forms.ModelForm): widget=forms.SelectMultiple( 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, 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) @@ -120,15 +121,15 @@ class AdminUserForm(forms.ModelForm): admin_user.password = password print(password) # Todo: Validate private key file, and generate public key + # Todo: Auto generate private key and public key if private_key_file: - print(private_key_file) admin_user.private_key = private_key_file.read() admin_user.save() return self.instance class Meta: model = AdminUser - fields = ['name', 'username', 'password', 'private_key_file', 'as_default', 'comment'] + fields = ['name', 'username', 'auto_generate_key', 'password', 'private_key_file', 'as_default', 'comment'] widgets = { 'name': forms.TextInput(attrs={'placeholder': _('Name')}), 'username': forms.TextInput(attrs={'placeholder': _('Username')}), @@ -138,3 +139,76 @@ class AdminUserForm(forms.ModelForm): 'username': '* required', } + +class SystemUserForm(forms.ModelForm): + # Admin user assets define, let user select, save it in form not in view + assets = forms.ModelMultipleChoiceField(queryset=Asset.objects.all(), + label=_('Asset'), + required=False, + widget=forms.SelectMultiple( + attrs={'class': 'select2', 'data-placeholder': _('Select assets')}) + ) + asset_groups = forms.ModelMultipleChoiceField(queryset=AssetGroup.objects.all(), + label=_('Asset group'), + required=False, + widget=forms.SelectMultiple( + attrs={'class': 'select2', + 'data-placeholder': _('Select asset groups')}) + ) + auto_generate_key = forms.BooleanField(required=True, initial=True) + # Form field name can not start with `_`, so redefine it, + 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) + # Need use upload private key file except paste private key content + private_key_file = forms.FileField(required=False) + + def __init__(self, *args, **kwargs): + # When update a admin user instance, initial it + if kwargs.get('instance'): + initial = kwargs.get('initial', {}) + initial['assets'] = kwargs['instance'].assets.all() + initial['asset_groups'] = kwargs['instance'].asset_groups.all() + super(SystemUserForm, self).__init__(*args, **kwargs) + + def _save_m2m(self): + # Save assets relation with admin user + super(SystemUserForm, self)._save_m2m() + assets = self.cleaned_data['assets'] + asset_groups = self.cleaned_data['asset_groups'] + self.instance.assets.clear() + self.instance.assets.add(*tuple(assets)) + self.instance.asset_groups.clear() + self.instance.asset_groups.add(*tuple(asset_groups)) + + def save(self, commit=True): + # Because we define custom field, so we need rewrite :method: `save` + system_user = super(SystemUserForm, self).save(commit=commit) + password = self.cleaned_data['password'] + private_key_file = self.cleaned_data['private_key_file'] + + if password: + system_user.password = password + print(password) + # Todo: Validate private key file, and generate public key + # Todo: Auto generate private key and public key + if private_key_file: + system_user.private_key = private_key_file.read() + system_user.save() + return self.instance + + class Meta: + model = SystemUser + fields = [ + 'name', 'username', 'protocol', 'auto_generate_key', 'password', 'private_key_file', 'as_default', + 'auto_push', 'auto_update', 'sudo', 'comment', 'shell', 'home', 'uid', + ] + widgets = { + 'name': forms.TextInput(attrs={'placeholder': _('Name')}), + 'username': forms.TextInput(attrs={'placeholder': _('Username')}), + } + help_texts = { + 'name': '* required', + 'username': '* required', + 'auth_push': 'Auto push system user to asset', + 'auth_update': 'Auto update system user ssh key', + } \ No newline at end of file diff --git a/apps/assets/models.py b/apps/assets/models.py index 213a49822..6701d65ce 100644 --- a/apps/assets/models.py +++ b/apps/assets/models.py @@ -135,28 +135,72 @@ class SystemUser(models.Model): ('telnet', 'telnet'), ) name = models.CharField(max_length=128, unique=True, verbose_name=_('Name')) - username = models.CharField(max_length=16, blank=True, verbose_name=_('Username')) - password = models.CharField(max_length=256, blank=True, verbose_name=_('Password')) - protocol = models.CharField(max_length=16, default='ssh', verbose_name=_('Protocol')) - private_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH private key')) - public_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH public key')) - is_default = models.BooleanField(default=True, verbose_name=_('As default')) + username = models.CharField(max_length=16, verbose_name=_('Username')) + _password = models.CharField(max_length=256, blank=True, verbose_name=_('Password')) + protocol = models.CharField(max_length=16, choices=PROTOCOL_CHOICES, default='ssh', verbose_name=_('Protocol')) + _private_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH private 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')) auto_push = models.BooleanField(default=True, verbose_name=_('Auto push')) auto_update = models.BooleanField(default=True, verbose_name=_('Auto update pass/key')) - sudo = models.TextField(max_length=4096, blank=True, verbose_name=_('Sudo')) - shell = models.CharField(max_length=64, blank=True, verbose_name=_('Shell')) + sudo = models.TextField(max_length=4096, default='/user/bin/whoami', verbose_name=_('Sudo')) + shell = models.CharField(max_length=64, default='/bin/bash', verbose_name=_('Shell')) home = models.CharField(max_length=64, blank=True, verbose_name=_('Home')) - uid = models.IntegerField(blank=True, verbose_name=_('Uid')) - date_created = models.DateTimeField(auto_now=True, null=True) + uid = models.IntegerField(null=True, blank=True, verbose_name=_('Uid')) + date_created = models.DateTimeField(auto_now=True) created_by = models.CharField(max_length=32, blank=True, verbose_name=_('Created by')) - comment = models.CharField(max_length=128, blank=True, verbose_name=_('Comment')) + comment = models.TextField(max_length=128, blank=True, verbose_name=_('Comment')) def __unicode__(self): return self.name + @property + def password(self): + return decrypt(self._password) + + @password.setter + def password(self, password_raw): + self._password = encrypt(password_raw) + + @property + def private_key(self): + return decrypt(self._private_key) + + @private_key.setter + def private_key(self, private_key_raw): + self._private_key = encrypt(private_key_raw) + + @property + def public_key(self): + return decrypt(self._public_key) + + @public_key.setter + def public_key(self, public_key_raw): + self._public_key = encrypt(public_key_raw) + class Meta: db_table = 'system_user' + @classmethod + def generate_fake(cls, count=100): + from random import seed + import forgery_py + from django.db import IntegrityError + + seed() + for i in range(count): + obj = cls(name=forgery_py.name.full_name(), + username=forgery_py.internet.user_name(), + password=forgery_py.lorem_ipsum.word(), + comment=forgery_py.lorem_ipsum.sentence(), + created_by='Fake') + try: + obj.save() + logger.debug('Generate fake asset group: %s' % obj.name) + except IntegrityError: + print('Error continue') + continue + class AssetGroup(models.Model): name = models.CharField(max_length=64, unique=True, verbose_name=_('Name')) @@ -206,7 +250,7 @@ class Asset(models.Model): password = models.CharField(max_length=256, null=True, blank=True, verbose_name=_("Admin password")) admin_user = models.ForeignKey(AdminUser, null=True, related_name='assets', on_delete=models.SET_NULL, verbose_name=_("Admin user")) - system_user = models.ManyToManyField(SystemUser, blank=True, verbose_name=_("System User")) + system_user = models.ManyToManyField(SystemUser, blank=True, related_name='assets', verbose_name=_("System User")) idc = models.ForeignKey(IDC, null=True, related_name='assets', on_delete=models.SET_NULL, verbose_name=_('IDC')) mac_address = models.CharField(max_length=20, null=True, blank=True, verbose_name=_("Mac address")) brand = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('Brand')) @@ -227,7 +271,7 @@ class Asset(models.Model): comment = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('Comment')) def __unicode__(self): - return '%(ip)s:%(port)d' % {'ip': self.ip, 'port': self.port} + return '%(ip)s:%(port)s' % {'ip': self.ip, 'port': self.port} def initial(self): pass diff --git a/apps/assets/templates/assets/admin_user_create_update.html b/apps/assets/templates/assets/admin_user_create_update.html index b340b7c3a..7738adec0 100644 --- a/apps/assets/templates/assets/admin_user_create_update.html +++ b/apps/assets/templates/assets/admin_user_create_update.html @@ -13,7 +13,7 @@