Commit 3260ceaa authored by ibuler's avatar ibuler

[Update] Merge with dev

parents e9705889 b5aa69db
......@@ -5,3 +5,5 @@ data/*
tmp/*
django.db
celerybeat.pid
### Vagrant ###
.vagrant/
\ No newline at end of file
......@@ -34,3 +34,5 @@ data/static
docs/_build/
xpack
logs/*
### Vagrant ###
.vagrant/
\ No newline at end of file
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure("2") do |config|
# The most common configuration options are documented and commented below.
# For a complete reference, please see the online documentation at
# https://docs.vagrantup.com.
# Every Vagrant development environment requires a box. You can search for
# boxes at https://vagrantcloud.com/search.
config.vm.box_check_update = false
config.vm.box = "centos/7"
config.vm.hostname = "jumpserver"
config.vm.network "private_network", ip: "172.17.8.101"
config.vm.provider "virtualbox" do |vb|
vb.memory = "4096"
vb.cpus = 2
vb.name = "jumpserver"
end
config.vm.synced_folder ".", "/vagrant", type: "rsync",
rsync__verbose: true,
rsync__exclude: ['.git*', 'node_modules*','*.log','*.box','Vagrantfile']
config.vm.provision "shell", inline: <<-SHELL
## 设置yum的阿里云源
sudo curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
sudo sed -i -e '/mirrors.cloud.aliyuncs.com/d' -e '/mirrors.aliyuncs.com/d' /etc/yum.repos.d/CentOS-Base.repo
sudo curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
sudo yum makecache
## 安装依赖包
sudo yum install -y python36 python36-devel python36-pip \
libtiff-devel libjpeg-devel libzip-devel freetype-devel \
lcms2-devel libwebp-devel tcl-devel tk-devel sshpass \
openldap-devel mariadb-devel mysql-devel libffi-devel \
openssh-clients telnet openldap-clients gcc
## 配置pip阿里云源
mkdir /home/vagrant/.pip
cat << EOF | sudo tee -a /home/vagrant/.pip/pip.conf
[global]
timeout = 6000
index-url = https://mirrors.aliyun.com/pypi/simple/
[install]
use-mirrors = true
mirrors = https://mirrors.aliyun.com/pypi/simple/
trusted-host=mirrors.aliyun.com
EOF
python3.6 -m venv /home/vagrant/venv
source /home/vagrant/venv/bin/activate
echo 'source /home/vagrant/venv/bin/activate' >> /home/vagrant/.bash_profile
SHELL
end
......@@ -82,7 +82,7 @@ class AssetUserViewSet(IDInCacheFilterMixin, BulkModelViewSet):
manager = AssetUserManager()
if system_user_id:
system_user = get_object_or_404(SystemUser, id=system_user_id)
assets = system_user.assets.all()
assets = system_user.get_all_assets()
username = system_user.username
elif admin_user_id:
admin_user = get_object_or_404(AdminUser, id=admin_user_id)
......
......@@ -12,7 +12,6 @@ from django.core.cache import cache
from django.db import models
from django.utils.translation import ugettext_lazy as _
from .user import AdminUser, SystemUser
from .utils import Connectivity
from orgs.mixins import OrgModelMixin, OrgManager
......@@ -320,6 +319,7 @@ class Asset(ProtocolsMixin, NodesRelationMixin, OrgModelMixin):
@classmethod
def generate_fake(cls, count=100):
from .user import AdminUser, SystemUser
from random import seed, choice
from django.db import IntegrityError
from .node import Node
......
......@@ -4,12 +4,15 @@
import logging
from functools import reduce
from django.db import models
from django.db.models import Q
from django.utils.translation import ugettext_lazy as _
from django.core.validators import MinValueValidator, MaxValueValidator
from common.utils import get_signer
from .base import AssetUser
from .asset import Asset
__all__ = ['AdminUser', 'SystemUser']
......@@ -144,6 +147,19 @@ class SystemUser(AssetUser):
return False, matched_cmd
return True, None
def get_all_assets(self):
args = [Q(systemuser=self)]
pattern = set()
nodes_keys = self.nodes.all().values_list('key', flat=True)
for key in nodes_keys:
pattern.add(r'^{0}$|^{0}:'.format(key))
pattern = '|'.join(list(pattern))
if pattern:
args.append(Q(nodes__key__regex=pattern))
args = reduce(lambda x, y: x | y, args)
assets = Asset.objects.filter(args).distinct()
return assets
class Meta:
ordering = ['name']
unique_together = [('name', 'org_id')]
......
......@@ -57,16 +57,16 @@ def on_system_user_update(sender, instance=None, created=True, **kwargs):
push_system_user_to_assets.delay(instance, assets)
@receiver(m2m_changed, sender=SystemUser.nodes.through)
def on_system_user_nodes_change(sender, instance=None, **kwargs):
if instance and kwargs["action"] == "post_add":
logger.info("System user `{}` nodes update signal received".format(instance))
assets = set()
nodes = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
for node in nodes:
assets.update(set(node.get_all_assets()))
instance.assets.add(*tuple(assets))
# @receiver(m2m_changed, sender=SystemUser.nodes.through)
# def on_system_user_nodes_change(sender, instance=None, **kwargs):
# if instance and kwargs["action"] == "post_add":
# logger.info("System user `{}` nodes update signal received".format(instance))
# assets = set()
# nodes = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
# for node in nodes:
# assets.update(set(node.get_all_assets()))
# instance.assets.add(*tuple(assets))
#
@receiver(m2m_changed, sender=SystemUser.assets.through)
def on_system_user_assets_change(sender, instance=None, **kwargs):
......
......@@ -347,7 +347,7 @@ def test_system_user_connectivity_util(system_user, assets, task_name):
@shared_task
def test_system_user_connectivity_manual(system_user):
task_name = _("Test system user connectivity: {}").format(system_user)
assets = system_user.get_related_assets()
assets = system_user.get_all_assets()
return test_system_user_connectivity_util(system_user, assets, task_name)
......@@ -367,7 +367,7 @@ def test_system_user_connectivity_period():
system_users = SystemUser.objects.all()
for system_user in system_users:
task_name = _("Test system user connectivity period: {}").format(system_user)
assets = system_user.get_related_assets()
assets = system_user.get_all_assets()
test_system_user_connectivity_util(system_user, assets, task_name)
......@@ -513,7 +513,7 @@ def push_system_user_util(system_user, assets, task_name):
@shared_task
def push_system_user_to_assets_manual(system_user):
assets = system_user.get_related_assets()
assets = system_user.get_all_assets()
task_name = _("Push system users to assets: {}").format(system_user.name)
return push_system_user_util(system_user, assets, task_name=task_name)
......
......@@ -68,10 +68,12 @@ var treeUrl = '{% url 'api-assets:node-children-tree' %}?assets=0';
// "otherMenu": "",
// "showAssets": false,
// }
var inited = false;
function initNodeTree(options) {
if (options.showAssets) {
treeUrl = setUrlParam(treeUrl, 'assets', '1')
}
var asyncTreeUrl = setUrlParam(treeUrl, 'refresh', '0');
var setting = {
view: {
dblClickExpand: false,
......@@ -84,7 +86,7 @@ function initNodeTree(options) {
},
async: {
enable: true,
url: treeUrl,
url: asyncTreeUrl,
autoParam: ["id=key", "name=n", "level=lv"],
type: 'get'
},
......@@ -109,16 +111,20 @@ function initNodeTree(options) {
beforeAsync: options.beforeAsync || defaultCallback("Before async")
}
};
$.get(treeUrl, function(data, status){
zNodes = data;
zTree = $.fn.zTree.init($("#nodeTree"), setting, zNodes);
$.get(treeUrl, function (data, status) {
zTree = $.fn.zTree.init($("#nodeTree"), setting, data);
rootNodeAddDom(zTree, function () {
treeUrl = setUrlParam(treeUrl, 'refresh', '1');
initTree();
treeUrl = setUrlParam(treeUrl, 'refresh', '0')
initNodeTree(options);
treeUrl = setUrlParam(treeUrl, 'refresh', '0');
});
inited = true;
});
if (inited) {
return
}
if (options.showMenu) {
showMenu = true;
rMenu = $("#rMenu");
......
......@@ -8,6 +8,7 @@ from django_auth_ldap.backend import _LDAPUser, LDAPBackend
from django_auth_ldap.config import _LDAPConfig, LDAPSearch, LDAPSearchUnion
from users.utils import construct_user_email
from common.const import LDAP_AD_ACCOUNT_DISABLE
logger = _LDAPConfig.get_logger()
......@@ -17,6 +18,15 @@ class LDAPAuthorizationBackend(LDAPBackend):
Override this class to override _LDAPUser to LDAPUser
"""
@staticmethod
def user_can_authenticate(user):
"""
Reject users with is_active=False. Custom user models that don't have
that attribute are allowed.
"""
is_valid = getattr(user, 'is_valid', None)
return is_valid or is_valid is None
def authenticate(self, request=None, username=None, password=None, **kwargs):
logger.info('Authentication LDAP backend')
if not username:
......@@ -25,34 +35,29 @@ class LDAPAuthorizationBackend(LDAPBackend):
ldap_user = LDAPUser(self, username=username.strip(), request=request)
user = self.authenticate_ldap_user(ldap_user, password)
logger.info('Authenticate user: {}'.format(user))
return user
return user if self.user_can_authenticate(user) else None
def get_user(self, user_id):
user = None
try:
user = self.get_user_model().objects.get(pk=user_id)
LDAPUser(self, user=user) # This sets user.ldap_user
except ObjectDoesNotExist:
pass
return user
def get_group_permissions(self, user, obj=None):
if not hasattr(user, 'ldap_user') and self.settings.AUTHORIZE_ALL_USERS:
LDAPUser(self, user=user) # This sets user.ldap_user
if hasattr(user, 'ldap_user'):
permissions = user.ldap_user.get_group_permissions()
else:
permissions = set()
return permissions
def populate_user(self, username):
ldap_user = LDAPUser(self, username=username)
user = ldap_user.populate_user()
return user
......@@ -91,13 +96,19 @@ class LDAPUser(_LDAPUser):
for field, attr in self.settings.USER_ATTR_MAP.items():
try:
value = self.attrs[attr][0]
if attr.lower() == 'useraccountcontrol' \
and field == 'is_active' and value:
value = int(value) & LDAP_AD_ACCOUNT_DISABLE \
!= LDAP_AD_ACCOUNT_DISABLE
except LookupError:
logger.warning("{} does not have a value for the attribute {}".format(self.dn, attr))
else:
if not hasattr(self._user, field):
continue
if isinstance(getattr(self._user, field), bool):
value = value.lower() in ['true', '1']
if isinstance(value, str):
value = value.lower()
value = value in ['true', '1', True]
setattr(self._user, field, value)
email = getattr(self._user, 'email', '')
......
......@@ -26,8 +26,8 @@ class BaseOpenIDAuthorizationBackend(object):
Reject users with is_active=False. Custom user models that don't have
that attribute are allowed.
"""
is_active = getattr(user, 'is_active', None)
return is_active or is_active is None
is_valid = getattr(user, 'is_valid', None)
return is_valid or is_valid is None
def get_user(self, user_id):
try:
......
from rest_framework.request import Request
from django.http.request import QueryDict
from django.conf import settings
from django.dispatch import receiver
......@@ -52,14 +53,15 @@ def on_ldap_create_user(sender, user, ldap_user, **kwargs):
def generate_data(username, request):
if not request.user.is_anonymous and request.user.is_app:
user_agent = request.META.get('HTTP_USER_AGENT', '')
if isinstance(request, Request):
login_ip = request.data.get('remote_addr', None)
login_type = request.data.get('login_type', '')
user_agent = request.data.get('HTTP_USER_AGENT', '')
else:
login_ip = get_request_ip(request)
user_agent = request.META.get('HTTP_USER_AGENT', '')
login_type = 'W'
data = {
'username': username,
'ip': login_ip,
......
......@@ -8,3 +8,7 @@ update_success_msg = _("%(name)s was updated successfully")
FILE_END_GUARD = ">>> Content End <<<"
celery_task_pre_key = "CELERY_"
KEY_CACHE_RESOURCES_ID = "RESOURCES_ID_{}"
# AD User AccountDisable
# https://blog.csdn.net/bytxl/article/details/17763975
LDAP_AD_ACCOUNT_DISABLE = 2
......@@ -137,6 +137,16 @@ class PermissionsMixin(UserPassesTestMixin):
return True
class UserCanUpdatePassword:
def has_permission(self, request, view):
return request.user.can_update_password()
class UserCanUpdateSSHKey:
def has_permission(self, request, view):
return request.user.can_update_ssh_key()
class NeedMFAVerify(permissions.BasePermission):
def has_permission(self, request, view):
mfa_verify_time = request.session.get('MFA_VERIFY_TIME', 0)
......
......@@ -58,8 +58,8 @@ class JMSCSVRender(BaseRenderer):
template = request.query_params.get('template', 'export')
view = renderer_context['view']
if isinstance(data, dict) and data.get("count"):
data = data["results"]
if isinstance(data, dict):
data = data.get("results", [])
if template == 'import':
data = [data[0]] if data else data
......
This diff is collapsed.
......@@ -9,13 +9,15 @@ from rest_framework.generics import (
)
from rest_framework.pagination import LimitOffsetPagination
from common.permissions import IsValidUser, IsOrgAdminOrAppUser
from common.permissions import IsValidUser, IsOrgAdminOrAppUser, IsOrgAdmin
from common.tree import TreeNodeSerializer
from common.utils import get_logger
from ..utils import (
AssetPermissionUtil, ParserNode,
)
from .mixin import UserPermissionCacheMixin, GrantAssetsMixin, NodesWithUngroupMixin
from .mixin import (
UserPermissionCacheMixin, GrantAssetsMixin, NodesWithUngroupMixin
)
from .. import const
from ..hands import User, Asset, Node, SystemUser, NodeSerializer
from .. import serializers
......@@ -29,6 +31,7 @@ __all__ = [
'UserGrantedNodesWithAssetsApi', 'UserGrantedNodeAssetsApi',
'ValidateUserAssetPermissionApi', 'UserGrantedNodesAsTreeApi',
'UserGrantedNodesWithAssetsAsTreeApi', 'GetUserAssetPermissionActionsApi',
'RefreshAssetPermissionCacheApi'
]
......@@ -373,3 +376,12 @@ class GetUserAssetPermissionActionsApi(UserPermissionCacheMixin, RetrieveAPIView
actions = asset["system_users"].get(system_id, 0)
break
return {"actions": actions}
class RefreshAssetPermissionCacheApi(RetrieveAPIView):
permission_classes = (IsOrgAdmin,)
def retrieve(self, request, *args, **kwargs):
# expire all cache
AssetPermissionUtil.expire_all_cache()
return Response({'msg': True}, status=200)
......@@ -33,10 +33,14 @@
</div>
</div>
<div class="mail-box-header">
<div class="uc pull-left m-r-5">
<a class="btn btn-sm btn-primary btn-create-permission">
<div class="btn-group uc pull-left m-r-5">
<button class="btn btn-sm btn-primary btn-create-permission">
{% trans "Create permission" %}
</a>
</button>
<button data-toggle="dropdown" class="btn btn-primary btn-sm dropdown-toggle"><span class="caret"></span></button>
<ul class="dropdown-menu">
<li><a class="refresh-asset-permission-cache" href="#">{% trans 'Refresh permission cache' %}</a></li>
</ul>
</div>
<table class="table table-striped table-bordered table-hover" id="permission_list_table" style="width: 100%">
<thead>
......@@ -232,6 +236,14 @@ $(document).ready(function(){
.replace('{{ DEFAULT_PK }}', uid);
objectDelete($this, name, the_url);
})
.on('click', '.refresh-asset-permission-cache', function () {
var the_url = "{% url 'api-perms:refresh-asset-permission-cache' %}";
requestApi({
url: the_url,
method: 'GET',
success_message: "{% trans 'Refresh success' %}"
});
})
.on('click', '.btn-create-permission', function () {
var url = "{% url 'perms:asset-permission-create' %}";
var nodes = zTree.getSelectedNodes();
......
......@@ -57,6 +57,9 @@ asset_permission_urlpatterns = [
# 验证用户是否有某个资产和系统用户的权限
path('asset-permissions/user/validate/', api.ValidateUserAssetPermissionApi.as_view(), name='validate-user-asset-permission'),
path('asset-permissions/user/actions/', api.GetUserAssetPermissionActionsApi.as_view(), name='get-user-asset-permission-actions'),
# 刷新缓存
path('asset-permissions/user/cache/refresh/', api.RefreshAssetPermissionCacheApi.as_view(), name='refresh-asset-permission-cache'),
]
......
......@@ -414,15 +414,12 @@ class AssetPermissionCacheMixin:
cache.delete_pattern(key)
self.expire_cache_meta()
@classmethod
def expire_all_cache_meta(cls):
key = cls.CACHE_META_KEY_PREFIX + '*'
cache.delete_pattern(key)
@classmethod
def expire_all_cache(cls):
key = cls.CACHE_KEY_PREFIX + '*'
cache.delete_pattern(key)
meta_key = cls.CACHE_META_KEY_PREFIX + '*'
cache.delete_pattern(meta_key)
class AssetPermissionUtil(AssetPermissionCacheMixin):
......
......@@ -7,6 +7,7 @@ from django.utils.translation import ugettext_lazy as _
from users.models import User
from users.utils import construct_user_email
from common.utils import get_logger
from common.const import LDAP_AD_ACCOUNT_DISABLE
from .models import settings
......@@ -70,7 +71,12 @@ class LDAPUtil:
for attr, mapping in self.attr_map.items():
if not hasattr(entry, mapping):
continue
user_item[attr] = getattr(entry, mapping).value or ''
value = getattr(entry, mapping).value or ''
if mapping.lower() == 'useraccountcontrol' and attr == 'is_active'\
and value:
value = int(value) & LDAP_AD_ACCOUNT_DISABLE \
!= LDAP_AD_ACCOUNT_DISABLE
user_item[attr] = value
return user_item
def search_user_items(self):
......@@ -102,7 +108,9 @@ class LDAPUtil:
if not hasattr(user, field):
continue
if isinstance(getattr(user, field), bool):
value = value.lower() in ['true', 1]
if isinstance(value, str):
value = value.lower()
value = value in ['true', 1, True]
setattr(user, field, value)
user.save()
......
......@@ -157,7 +157,7 @@ UserProfileForm.verbose_name = _("Profile")
class UserMFAForm(forms.ModelForm):
mfa_description = _(
'Tip: when enabled, '
'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"!')
......
......@@ -54,6 +54,12 @@ class AuthMixin:
def can_update_password(self):
return self.is_local
def can_update_ssh_key(self):
return self.can_use_ssh_key_login()
def can_use_ssh_key_login(self):
return settings.TERMINAL_PUBLIC_KEY_AUTH
def check_otp(self, code):
from ..utils import check_otp_code
return check_otp_code(self.otp_secret_key, code)
......
......@@ -122,7 +122,7 @@ function initTree() {
$.fn.zTree.init($("#assetTree"), setting, data);
zTree = $.fn.zTree.getZTreeObj("assetTree");
rootNodeAddDom(zTree, function () {
treeUrl = treeUrl.replace('cache_policy=1', 'cache_policy=2');
treeUrl = setUrlParam(treeUrl, 'cache_policy', '2');
initTree();
});
});
......@@ -156,7 +156,7 @@ function loadLabels() {
}
$(document).ready(function () {
loadLabels()
{#loadLabels()#}
}).on('click', '.labels-menu li', function () {
var val = $(this).text();
$("#user_assets_table_filter input").val(val);
......
......@@ -73,14 +73,17 @@
<p id="noTerms" class="red-fonts" style="visibility: hidden; font-size: 10px; margin-top: 10px;">* {% trans 'Please choose the terms and conditions.' %}</p>
{% endif %}
{% if wizard.steps.current == '1' and not request.user.can_update_ssh_key %}
<b id="ssh_key_help_text">{% trans 'User auth from {}, ssh key login is not supported' %}</b>
{% else %}
{% bootstrap_form wizard.form %}
{% endif %}
{% if form.mfa_description %}
<b>{{ form.mfa_description }}</b>
{% endif %}
{% if form.pubkey_description %}
<span>或者:</span>
{% if form.pubkey_description and request.user.can_update_ssh_key %}
<a type="button" id="btn-reset-pubkey">{{ form.pubkey_description }}</a>
{% endif %}
......@@ -121,12 +124,18 @@
{% block custom_foot_js %}
<script>
$(document).on('click', ".fl_goto", function(){
$(document).ready(function(){
var origin_ssh_key_text = $("#ssh_key_help_text").text();
var new_ssh_key_text = origin_ssh_key_text.replace('{}', "{{ request.user.source_display }}");
$("#ssh_key_help_text").html(new_ssh_key_text)
})
.on('click', ".fl_goto", function(){
var $form = $('#fl_form');
$('<input />', {'name': 'wizard_goto_step', 'value': $(this).data('goto'), 'type': 'hidden'}).appendTo($form);
$form.submit();
return false;
}).on('click', '#fl_submit', function(){
})
.on('click', '#fl_submit', function(){
var isFinish = $('#fl_submit').html() === "{% trans 'Finish' %}";
var noChecked = !$('#acceptTerms').prop('checked');
if ( isFinish && noChecked){
......@@ -136,11 +145,12 @@
$('#fl_form').submit();
return false;
}
}).on('click', '#btn-reset-pubkey', function () {
})
.on('click', '#btn-reset-pubkey', function () {
var the_url = '{% url "users:user-pubkey-generate" %}';
window.open(the_url, "_blank");
$('#fl_form').submit();
})
})
</script>
{% endblock %}
......@@ -119,10 +119,12 @@
<td>{% trans 'Last login' %}:</td>
<td><b>{{ user_object.last_login|date:"Y-m-j H:i:s" }}</b></td>
</tr>
{% if user_object.can_update_password %}
<tr>
<td>{% trans 'Last password updated' %}:</td>
<td><b>{{ user_object.date_password_last_updated|date:"Y-m-j H:i:s" }}</b></td>
</tr>
{% endif %}
<tr>
<td>{% trans 'Comment' %}:</td>
<td><b>{{ user_object.comment }}</b></td>
......@@ -187,6 +189,7 @@
</td>
</tr>
{% endif %}
{% if user_object.can_update_ssh_key %}
<tr>
<td>{% trans 'Send reset ssh key mail' %}:</td>
<td>
......@@ -195,6 +198,7 @@
</span>
</td>
</tr>
{% endif %}
<tr style="{% if not unblock %}display:none{% endif %}">
<td>{% trans 'Unblock user' %}</td>
<td>
......
......@@ -34,7 +34,7 @@
<script>
var assetTableUrl = "{% url 'api-perms:user-assets' pk=object.id %}?cache_policy=1";
var selectUrl = '{% url "api-perms:user-node-assets" pk=object.id node_id=DEFAULT_PK %}?cache_policy=1&all=1';
var treeUrl = "{% url 'api-perms:user-nodes-as-tree' pk=object.id %}?&cache_policy=1";
var treeUrl = "{% url 'api-perms:user-nodes-as-tree' pk=object.id %}?cache_policy=1";
$(document).ready(function () {
initTree();
......
......@@ -39,12 +39,16 @@
<li>
<a href="{% url 'users:user-profile-update' %}" class="text-center">{% trans 'Profile' %} </a>
</li>
{% if request.user.can_update_password %}
<li class="active">
<a href="{% url 'users:user-password-update' %}" class="text-center">{% trans 'Password' %} </a>
</li>
{% endif %}
{% if request.user.can_update_ssh_key %}
<li>
<a href="{% url 'users:user-pubkey-update' %}" class="text-center">{% trans 'Public key' %} </a>
</li>
{% endif %}
</ul>
</div>
<div class="tab-content" style="background-color: #ffffff">
......
......@@ -64,6 +64,7 @@
<td>{{ user.is_active|yesno:"Yes,No,Unkown" }}</td>
</tr>
{% if user.can_update_ssh_key %}
<tr>
<td class="text-navy">{% trans 'Public key' %}</td>
<td>
......@@ -81,6 +82,7 @@
</table>
</td>
</tr>
{% endif %}
<tr>
<td class="text-navy">{% trans 'MFA certification' %}</td>
<td>
......@@ -108,10 +110,12 @@
<td class="text-navy">{% trans 'Last login' %}</td>
<td>{{ user.last_login|date:"Y-m-d H:i:s" }}</td>
</tr>
{% if user.can_update_password %}
<tr>
<td class="text-navy">{% trans 'Last password updated' %}</td>
<td>{{ user.date_password_last_updated|date:"Y-m-d H:i:s" }}</td>
</tr>
{% endif %}
<tr>
<td class="text-navy">{% trans 'Date expired' %}</td>
<td>{{ user.date_expired|date:"Y-m-d H:i:s" }}</td>
......@@ -189,6 +193,7 @@
</td>
</tr>
{% endif %}
{% if request.user.can_update_ssh_key %}
<tr>
<td>{% trans 'Update SSH public key' %}:</td>
<td>
......@@ -205,6 +210,7 @@
</span>
</td>
</tr>
{% endif %}
</tbody>
</table>
</div>
......
......@@ -36,12 +36,16 @@
<li class="active">
<a href="{% url 'users:user-profile-update' %}" class="text-center">{% trans 'Profile' %} </a>
</li>
{% if request.user.can_update_password %}
<li>
<a href="{% url 'users:user-password-update' %}" class="text-center">{% trans 'Password' %} </a>
</li>
{% endif %}
{% if request.user.can_update_ssh_key %}
<li>
<a href="{% url 'users:user-pubkey-update' %}" class="text-center">{% trans 'Public key' %} </a>
</li>
{% endif %}
</ul>
</div>
<div class="tab-content" style="background-color: #ffffff">
......
......@@ -36,12 +36,16 @@
<li>
<a href="{% url 'users:user-profile-update' %}" class="text-center">{% trans 'Profile' %} </a>
</li>
{% if request.user.can_update_password %}
<li>
<a href="{% url 'users:user-password-update' %}" class="text-center">{% trans 'Password' %} </a>
</li>
{% endif %}
{% if request.user.can_update_ssh_key %}
<li class="active">
<a href="{% url 'users:user-pubkey-update' %}" class="text-center">{% trans 'Public key' %} </a>
</li>
{% endif %}
</ul>
</div>
<div class="tab-content" style="background-color: #ffffff">
......
......@@ -23,7 +23,16 @@
</div>
</div>
{% endif %}
{% if object.can_update_ssh_key %}
{% bootstrap_field form.public_key layout="horizontal" %}
{% else %}
<div class="form-group">
<label class="col-sm-2 control-label">{% trans 'ssh public key' %}</label>
<div class="col-sm-8 controls" style="margin-top: 8px;" id="ssh_key_help_text">
{% trans 'User auth from {}, ssh key login is not supported' %}
</div>
</div>
{% endif %}
{% endblock %}
{% block custom_foot_js %}
......@@ -77,9 +86,13 @@ function passwordCheck() {
$(document).ready(function(){
passwordCheck();
var origin_text = $("#password_help_text").text();
var new_text = origin_text.replace('{}', "{{ object.source_display }}");
$("#password_help_text").html(new_text);
var origin_password_text = $("#password_help_text").text();
var new_password_text = origin_password_text.replace('{}', "{{ object.source_display }}");
$("#password_help_text").html(new_password_text);
var origin_ssh_key_text = $("#ssh_key_help_text").text();
var new_ssh_key_text = origin_ssh_key_text.replace('{}', "{{ object.source_display }}");
$("#ssh_key_help_text").html(new_ssh_key_text)
})
.on("submit", "form", function (evt) {
......
......@@ -2,40 +2,32 @@
from __future__ import unicode_literals
import json
import uuid
import csv
import codecs
import chardet
from io import StringIO
from django.contrib import messages
from django.contrib.auth import authenticate, login as auth_login
from django.contrib.auth import authenticate
from django.contrib.messages.views import SuccessMessageMixin
from django.core.cache import cache
from django.conf import settings
from django.http import HttpResponse, JsonResponse
from django.http import HttpResponse
from django.shortcuts import redirect
from django.urls import reverse_lazy, reverse
from django.utils import timezone
from django.utils.translation import ugettext as _
from django.utils.decorators import method_decorator
from django.views import View
from django.views.generic.base import TemplateView
from django.db import transaction
from django.views.generic.edit import (
CreateView, UpdateView, FormView
)
from django.views.generic.detail import DetailView
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth import logout as auth_logout
from common.const import (
create_success_msg, update_success_msg, KEY_CACHE_RESOURCES_ID
)
from common.mixins import JSONResponseMixin
from common.utils import get_logger, get_object_or_none, is_uuid, ssh_key_gen
from common.permissions import PermissionsMixin, IsOrgAdmin, IsValidUser
from common.utils import get_logger, ssh_key_gen
from common.permissions import (
PermissionsMixin, IsOrgAdmin, IsValidUser,
UserCanUpdatePassword, UserCanUpdateSSHKey,
)
from orgs.utils import current_org
from .. import forms
from ..models import User, UserGroup
......@@ -260,6 +252,7 @@ class UserPasswordUpdateView(PermissionsMixin, UpdateView):
model = User
form_class = forms.UserPasswordForm
success_url = reverse_lazy('users:user-profile')
permission_classes = [IsValidUser, UserCanUpdatePassword]
def get_object(self, queryset=None):
return self.request.user
......@@ -279,12 +272,6 @@ class UserPasswordUpdateView(PermissionsMixin, UpdateView):
return super().get_success_url()
def form_valid(self, form):
if not self.request.user.can_update_password():
error = _("User auth from {}, go there change password").format(
self.request.source_display
)
form.add_error("password", error)
return self.form_invalid(form)
password = form.cleaned_data.get('new_password')
is_ok = check_password_rules(password)
if not is_ok:
......@@ -300,7 +287,7 @@ class UserPublicKeyUpdateView(PermissionsMixin, UpdateView):
template_name = 'users/user_pubkey_update.html'
model = User
form_class = forms.UserPublicKeyForm
permission_classes = [IsValidUser]
permission_classes = [IsValidUser, UserCanUpdateSSHKey]
success_url = reverse_lazy('users:user-profile')
def get_object(self, queryset=None):
......
......@@ -81,7 +81,7 @@ def make_migrations():
def collect_static():
print("Collect static files")
os.chdir(os.path.join(BASE_DIR, 'apps'))
subprocess.call('python3 manage.py collectstatic --no-input', shell=True)
subprocess.call('python3 manage.py collectstatic --no-input -c &> /dev/null && echo "Collect static file done"', shell=True)
def prepare():
......
tiff-dev jpeg-dev zlib-dev freetype-dev lcms-dev libwebp-dev tcl-dev tk-dev python3-dev libressl-dev openldap-dev cyrus-sasl-dev krb5-dev sshpass postgresql-dev mariadb-dev sqlite-dev libffi-dev openssh-client
tiff-dev jpeg-dev zlib-dev freetype-dev lcms-dev libwebp-dev tcl-dev tk-dev python3-dev libressl-dev openldap-dev cyrus-sasl-dev krb5-dev sshpass postgresql-dev mariadb-dev sqlite-dev libffi-dev openssh-client gcc libc-dev linux-headers make autoconf
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