Commit b5fc76d6 authored by BaiJiangjie's avatar BaiJiangjie

[Update] 修改用户首次登录页

parent 5b52b907
This diff is collapsed.
...@@ -220,7 +220,6 @@ ...@@ -220,7 +220,6 @@
position: relative; position: relative;
display: block; display: block;
text-align: right; text-align: right;
width: 100%;
} }
.wizard.vertical > .actions .wizard.vertical > .actions
......
...@@ -6,9 +6,12 @@ ...@@ -6,9 +6,12 @@
{% blocktrans %} {% blocktrans %}
Your information was incomplete. Please click <a href="{{ first_login_url }}"> this link </a>to complete your information. Your information was incomplete. Please click <a href="{{ first_login_url }}"> this link </a>to complete your information.
{% endblocktrans %} {% endblocktrans %}
<button aria-hidden="true" data-dismiss="alert" class="close" type="button">×</button>
</div> </div>
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block update_public_key_message %} {% block update_public_key_message %}
{% if request.user.is_authenticated and not request.user.is_public_key_valid and not request.COOKIE.close_public_key_msg != '1' %} {% if request.user.is_authenticated and not request.user.is_public_key_valid and not request.COOKIE.close_public_key_msg != '1' %}
<div class="alert alert-danger help-message alert-dismissable"> <div class="alert alert-danger help-message alert-dismissable">
......
...@@ -36,7 +36,10 @@ class UserCreateUpdateForm(forms.ModelForm): ...@@ -36,7 +36,10 @@ class UserCreateUpdateForm(forms.ModelForm):
label=_('Password'), widget=forms.PasswordInput, label=_('Password'), widget=forms.PasswordInput,
max_length=128, strip=False, required=False, max_length=128, strip=False, required=False,
) )
role = forms.ChoiceField(choices=role_choices, required=True, initial=User.ROLE_USER, label=_("Role")) role = forms.ChoiceField(
choices=role_choices, required=True,
initial=User.ROLE_USER, label=_("Role")
)
public_key = forms.CharField( public_key = forms.CharField(
label=_('ssh public key'), max_length=5000, required=False, label=_('ssh public key'), max_length=5000, required=False,
widget=forms.Textarea(attrs={'placeholder': _('ssh-rsa AAAA...')}), widget=forms.Textarea(attrs={'placeholder': _('ssh-rsa AAAA...')}),
...@@ -110,6 +113,39 @@ class UserProfileForm(forms.ModelForm): ...@@ -110,6 +113,39 @@ class UserProfileForm(forms.ModelForm):
UserProfileForm.verbose_name = _("Profile") UserProfileForm.verbose_name = _("Profile")
class UserMFAForm(forms.ModelForm):
mfa_description = _(
'Tip: when enabled, '
'you will enter the MFA binding process the next time you log in. '
'you can also directly bind in '
'"personal information -> quick modification -> change MFA Settings"!')
class Meta:
model = User
fields = ['otp_level']
widgets = {'otp_level': forms.RadioSelect()}
help_texts = {
'otp_level': _('* Enable MFA authentication '
'to make the account more secure.'),
}
UserMFAForm.verbose_name = _("MFA")
class UserFirstLoginFinishForm(forms.Form):
finish_description = _(
'In order to protect you and your company, '
'please keep your account, '
'password and key sensitive information properly. '
'(for example: setting complex password, enabling MFA authentication)'
)
UserFirstLoginFinishForm.verbose_name = _("Finish")
class UserPasswordForm(forms.Form): class UserPasswordForm(forms.Form):
old_password = forms.CharField( old_password = forms.CharField(
max_length=128, widget=forms.PasswordInput, max_length=128, widget=forms.PasswordInput,
...@@ -152,6 +188,7 @@ class UserPasswordForm(forms.Form): ...@@ -152,6 +188,7 @@ class UserPasswordForm(forms.Form):
class UserPublicKeyForm(forms.Form): class UserPublicKeyForm(forms.Form):
pubkey_description = _('Automatically configure and download the SSH key')
public_key = forms.CharField( public_key = forms.CharField(
label=_('ssh public key'), max_length=5000, required=False, label=_('ssh public key'), max_length=5000, required=False,
widget=forms.Textarea(attrs={'placeholder': _('ssh-rsa AAAA...')}), widget=forms.Textarea(attrs={'placeholder': _('ssh-rsa AAAA...')}),
......
...@@ -36,23 +36,52 @@ class User(AbstractUser): ...@@ -36,23 +36,52 @@ class User(AbstractUser):
(2, _("Force enable")), (2, _("Force enable")),
) )
id = models.UUIDField(default=uuid.uuid4, primary_key=True) id = models.UUIDField(default=uuid.uuid4, primary_key=True)
username = models.CharField(max_length=128, unique=True, verbose_name=_('Username')) username = models.CharField(
max_length=128, unique=True, verbose_name=_('Username')
)
name = models.CharField(max_length=128, verbose_name=_('Name')) name = models.CharField(max_length=128, verbose_name=_('Name'))
email = models.EmailField(max_length=128, unique=True, verbose_name=_('Email')) email = models.EmailField(
groups = models.ManyToManyField('users.UserGroup', related_name='users', blank=True, verbose_name=_('User group')) max_length=128, unique=True, verbose_name=_('Email')
role = models.CharField(choices=ROLE_CHOICES, default='User', max_length=10, blank=True, verbose_name=_('Role')) )
avatar = models.ImageField(upload_to="avatar", null=True, verbose_name=_('Avatar')) groups = models.ManyToManyField(
wechat = models.CharField(max_length=128, blank=True, verbose_name=_('Wechat')) 'users.UserGroup', related_name='users',
phone = models.CharField(max_length=20, blank=True, null=True, verbose_name=_('Phone')) blank=True, verbose_name=_('User group')
otp_level = models.SmallIntegerField(default=0, choices=OTP_LEVEL_CHOICES, verbose_name=_('MFA')) )
role = models.CharField(
choices=ROLE_CHOICES, default='User', max_length=10,
blank=True, verbose_name=_('Role')
)
avatar = models.ImageField(
upload_to="avatar", null=True, verbose_name=_('Avatar')
)
wechat = models.CharField(
max_length=128, blank=True, verbose_name=_('Wechat')
)
phone = models.CharField(
max_length=20, blank=True, null=True, verbose_name=_('Phone')
)
otp_level = models.SmallIntegerField(
default=0, choices=OTP_LEVEL_CHOICES, verbose_name=_('MFA')
)
_otp_secret_key = models.CharField(max_length=128, blank=True, null=True) _otp_secret_key = models.CharField(max_length=128, blank=True, null=True)
# Todo: Auto generate key, let user download # Todo: Auto generate key, let user download
_private_key = models.CharField(max_length=5000, blank=True, verbose_name=_('Private key')) _private_key = models.CharField(
_public_key = models.CharField(max_length=5000, blank=True, verbose_name=_('Public key')) max_length=5000, blank=True, verbose_name=_('Private key')
comment = models.TextField(max_length=200, blank=True, verbose_name=_('Comment')) )
_public_key = models.CharField(
max_length=5000, blank=True, verbose_name=_('Public key')
)
comment = models.TextField(
max_length=200, blank=True, verbose_name=_('Comment')
)
is_first_login = models.BooleanField(default=True) is_first_login = models.BooleanField(default=True)
date_expired = models.DateTimeField(default=date_expired_default, blank=True, null=True, verbose_name=_('Date expired')) date_expired = models.DateTimeField(
created_by = models.CharField(max_length=30, default='', verbose_name=_('Created by')) default=date_expired_default, blank=True, null=True,
verbose_name=_('Date expired')
)
created_by = models.CharField(
max_length=30, default='', verbose_name=_('Created by')
)
def __str__(self): def __str__(self):
return '{0.name}({0.username})'.format(self) return '{0.name}({0.username})'.format(self)
...@@ -213,7 +242,9 @@ class User(AbstractUser): ...@@ -213,7 +242,9 @@ class User(AbstractUser):
return user_default return user_default
def generate_reset_token(self): def generate_reset_token(self):
return signer.sign_t({'reset': str(self.id), 'email': self.email}, expires_in=3600) return signer.sign_t(
{'reset': str(self.id), 'email': self.email}, expires_in=3600
)
@property @property
def otp_enabled(self): def otp_enabled(self):
......
...@@ -74,13 +74,11 @@ ...@@ -74,13 +74,11 @@
</article> </article>
<footer> <footer>
<div class="" style="margin-top: 100px;">
<div class="" style="margin-top: 100px;"> {% include '_copyright.html' %}
{% include '_copyright.html' %} </div>
</div> </footer>
</footer>
</body> </body>
</html> </html>
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
<link href="{% static 'css/plugins/steps/jquery.steps.css' %}" rel="stylesheet"> <link href="{% static 'css/plugins/steps/jquery.steps.css' %}" rel="stylesheet">
{% endblock %} {% endblock %}
{% block first_login_message %}{% endblock %} {% block first_login_message %}{% endblock %}
{% block content %} {% block content %}
<div class="wrapper wrapper-content animated fadeInRight"> <div class="wrapper wrapper-content animated fadeInRight">
<div class="row"> <div class="row">
...@@ -27,58 +28,116 @@ ...@@ -27,58 +28,116 @@
</div> </div>
<div class="ibox-content"> <div class="ibox-content">
<div class="wizard"> <div class="wizard">
<div class="steps clearfix"> <div class="steps clearfix">
<ul role="tablist"> <ul role="tablist">
{% for step in wizard.steps.all %} {% for step in wizard.steps.all %}
<li role="tab" class="{% ifequal step wizard.steps.first %}first{% endifequal %} {% ifequal step wizard.steps.current %}current{% else %}disabled{% endifequal %} {% ifequal step wizard.steps.last %}last{% endifequal %}" <li role="tab" class="{% ifequal step wizard.steps.first %}first{% endifequal %} {% ifequal step wizard.steps.current %}current{% else %}disabled{% endifequal %} {% ifequal step wizard.steps.last %}last{% endifequal %}"
aria-disabled="false" aria-selected="true"> aria-disabled="false" aria-selected="true">
<a href="javascript:void(0)"><span class="number">{% trans 'Step' %} {{ step }}</span></a> <a class="fl_goto" name="wizard_goto_step" data-goto="{{ step }}">
</li> <span class="number">
{% endfor >%} {% ifequal step '0' %}
</ul> 1. {% trans "Profile" %}
</div> {% endifequal %}
<div class="content clearfix"> {% ifequal step '1' %}
<form action="" method="post" class="form col-lg-8 p-m" id="fl_form"> 2. {% trans "Public key" %}
{% csrf_token %} {% endifequal %}
{{ wizard.management_form }} {% ifequal step '2' %}
{% if wizard.form.forms %} 3. {% trans "MFA" %}
{{ wizard.form.management_form }} {% endifequal %}
{% for form in wizard.form.forms %} {% ifequal step '3' %}
{% bootstrap_form form %} 4. {% trans "Finish" %}
{% endfor %} {% endifequal %}
{% else %} </span>
{% bootstrap_form wizard.form %} </a>
{% endif %} </li>
</form> {% endfor >%}
</div> </ul>
</div>
<div class="content clearfix" style="background-color: #eee; border-radius:5px;">
<div class="row">
<form action="" method="post" class="form col-lg-8 p-m" id="fl_form" style="padding-left: 40px;">
{% csrf_token %}
{{ wizard.management_form }}
{#{% if wizard.form.forms %}#}
{#{{ wizard.form.management_form }}#}
{#{% for form in wizard.form %}#}
{#{% bootstrap_form form %}#}
{#{% endfor %}#}
{#{% else %}#}
{#{% endif %}#}
{% if form.finish_description %}
<b>{{ form.finish_description }}</b>
<br>
<input type="checkbox" id="acceptTerms">
<label for="acceptTerms" style="margin-top:20px">{% trans "I agree with the terms and conditions." %}</label>
{% endif %}
{% bootstrap_form wizard.form %}
{% if form.mfa_description %}
<b>{{ form.mfa_description }}</b>
{% endif %}
{% if form.pubkey_description %}
<span>或者:</span>
<a type="button" id="btn-reset-pubkey">{{ form.pubkey_description }}</a>
{% endif %}
</form>
<div class="col-lg-4">
<div class="text-center">
<div style="margin-top: 20px">
<i class="fa fa-sign-in" style="font-size: 180px;color: #e5e5e5 "></i>
</div>
</div>
</div>
</div>
</div>
<div class="actions clearfix"> <div class="actions clearfix">
<ul> <ul>
{% if wizard.steps.prev %} {% if wizard.steps.prev %}
<li><a class="fl_goto" name="wizard_goto_step" data-goto="{{ wizard.steps.prev }}">{% trans "Previous" %}</a></li> <li><a class="fl_goto" name="wizard_goto_step" data-goto="{{ wizard.steps.prev }}">{% trans "Previous" %}</a></li>
{% endif %} {% endif %}
{% if wizard.steps.next %} {#{% if wizard.steps.next %}#}
<li><a class="fl_goto" name="wizard_goto_step" data-goto="{{ wizard.steps.next }}">{% trans "Next" %}</a></li> {#<li><a class="fl_goto" name="wizard_goto_step" data-goto="{{ wizard.steps.next }}">{% trans "Next" %}</a></li>#}
{% endif %} {#{% endif %}#}
<li><a id="fl_submit">{% trans "Submit" %}</a></li> {#<li><a id="fl_submit">{% trans "Submit" %}</a></li>#}
</ul> {#将原来的下一页-替换为提交;修复 每页都提交,最后才能成功问题#}
{% if wizard.steps.next %}
<li><a id="fl_submit" >{% trans "Next" %}</a></li>
{% else %}
<li><a id="fl_submit" style="width:66px;text-align: center;">{% trans "Finish" %}</a></li>
{% endif %}
</ul>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
$(document).on('click', ".fl_goto", function(){ $('#id_2-otp_level div').eq(2).css('display', 'none');
var $form = $('#fl_form');
$('<input />', {'name': 'wizard_goto_step', 'value': $(this).data('goto'), 'type': 'hidden'}).appendTo($form); $(document).on('click', ".fl_goto", function(){
$form.submit(); var $form = $('#fl_form');
return false; $('<input />', {'name': 'wizard_goto_step', 'value': $(this).data('goto'), 'type': 'hidden'}).appendTo($form);
}).on('click', '#fl_submit', function(){ $form.submit();
$('#fl_form').submit(); return false;
return false; }).on('click', '#fl_submit', function(){
$('#fl_form').submit();
return false;
}).on('click', '#btn-reset-pubkey', function () {
var the_url = '{% url "users:user-pubkey-generate" %}';
window.open(the_url, "_blank")
}) })
</script> </script>
{% endblock %} {% endblock %}
{% extends 'base.html' %} {% extends 'base.html' %}
{% load static %} {% load static %}
{% load i18n %} {% load i18n %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% block custom_head_css_js %} {% block custom_head_css_js %}
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
<link href="{% static 'css/plugins/steps/jquery.steps.css' %}" rel="stylesheet"> <link href="{% static 'css/plugins/steps/jquery.steps.css' %}" rel="stylesheet">
{% endblock %} {% endblock %}
{% block first_login_message %}{% endblock %} {% block first_login_message %}{% endblock %}
{% block content %} {% block content %}
<div class="wrapper wrapper-content animated fadeInRight"> <div class="wrapper wrapper-content animated fadeInRight">
<div class="row"> <div class="row">
...@@ -36,6 +37,8 @@ ...@@ -36,6 +37,8 @@
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
$(document).on('click', ".fl_goto", function(){ $(document).on('click', ".fl_goto", function(){
......
...@@ -27,7 +27,6 @@ ...@@ -27,7 +27,6 @@
$(function(){ $(function(){
$('.change-color li').eq(2).remove(); $('.change-color li').eq(2).remove();
$('.change-color li:eq(1) div').eq(1).html('解绑MFA') $('.change-color li:eq(1) div').eq(1).html('解绑MFA')
}) })
......
...@@ -166,8 +166,7 @@ class UserForgotPasswordSendmailSuccessView(TemplateView): ...@@ -166,8 +166,7 @@ class UserForgotPasswordSendmailSuccessView(TemplateView):
'redirect_url': reverse('users:login'), 'redirect_url': reverse('users:login'),
} }
kwargs.update(context) kwargs.update(context)
return super()\ return super().get_context_data(**kwargs)
.get_context_data(**kwargs)
class UserResetPasswordSuccessView(TemplateView): class UserResetPasswordSuccessView(TemplateView):
...@@ -214,7 +213,12 @@ class UserResetPasswordView(TemplateView): ...@@ -214,7 +213,12 @@ class UserResetPasswordView(TemplateView):
class UserFirstLoginView(LoginRequiredMixin, SessionWizardView): class UserFirstLoginView(LoginRequiredMixin, SessionWizardView):
template_name = 'users/first_login.html' template_name = 'users/first_login.html'
form_list = [forms.UserProfileForm, forms.UserPublicKeyForm] form_list = [
forms.UserProfileForm,
forms.UserPublicKeyForm,
forms.UserMFAForm,
forms.UserFirstLoginFinishForm
]
file_storage = default_storage file_storage = default_storage
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
...@@ -255,7 +259,6 @@ class UserFirstLoginView(LoginRequiredMixin, SessionWizardView): ...@@ -255,7 +259,6 @@ class UserFirstLoginView(LoginRequiredMixin, SessionWizardView):
def get_form(self, step=None, data=None, files=None): def get_form(self, step=None, data=None, files=None):
form = super().get_form(step, data, files) form = super().get_form(step, data, files)
form.instance = self.request.user form.instance = self.request.user
return form return form
...@@ -293,7 +296,9 @@ class LoginLogListView(AdminUserRequiredMixin, DatetimeSearchMixin, ListView): ...@@ -293,7 +296,9 @@ class LoginLogListView(AdminUserRequiredMixin, DatetimeSearchMixin, ListView):
'date_to': self.date_to, 'date_to': self.date_to,
'user': self.user, 'user': self.user,
'keyword': self.keyword, 'keyword': self.keyword,
'user_list': set(LoginLog.objects.all().values_list('username', flat=True)) 'user_list': set(
LoginLog.objects.all().values_list('username', flat=True)
)
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment