Commit 3260ceaa authored by ibuler's avatar ibuler

[Update] Merge with dev

parents e9705889 b5aa69db
...@@ -4,4 +4,6 @@ data/* ...@@ -4,4 +4,6 @@ data/*
.github .github
tmp/* tmp/*
django.db django.db
celerybeat.pid celerybeat.pid
\ No newline at end of file ### Vagrant ###
.vagrant/
\ No newline at end of file
...@@ -34,3 +34,5 @@ data/static ...@@ -34,3 +34,5 @@ data/static
docs/_build/ docs/_build/
xpack xpack
logs/* 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): ...@@ -82,7 +82,7 @@ class AssetUserViewSet(IDInCacheFilterMixin, BulkModelViewSet):
manager = AssetUserManager() manager = AssetUserManager()
if system_user_id: if system_user_id:
system_user = get_object_or_404(SystemUser, id=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 username = system_user.username
elif admin_user_id: elif admin_user_id:
admin_user = get_object_or_404(AdminUser, id=admin_user_id) admin_user = get_object_or_404(AdminUser, id=admin_user_id)
......
...@@ -12,7 +12,6 @@ from django.core.cache import cache ...@@ -12,7 +12,6 @@ from django.core.cache import cache
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from .user import AdminUser, SystemUser
from .utils import Connectivity from .utils import Connectivity
from orgs.mixins import OrgModelMixin, OrgManager from orgs.mixins import OrgModelMixin, OrgManager
...@@ -320,6 +319,7 @@ class Asset(ProtocolsMixin, NodesRelationMixin, OrgModelMixin): ...@@ -320,6 +319,7 @@ class Asset(ProtocolsMixin, NodesRelationMixin, OrgModelMixin):
@classmethod @classmethod
def generate_fake(cls, count=100): def generate_fake(cls, count=100):
from .user import AdminUser, SystemUser
from random import seed, choice from random import seed, choice
from django.db import IntegrityError from django.db import IntegrityError
from .node import Node from .node import Node
......
...@@ -4,12 +4,15 @@ ...@@ -4,12 +4,15 @@
import logging import logging
from functools import reduce
from django.db import models from django.db import models
from django.db.models import Q
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.core.validators import MinValueValidator, MaxValueValidator from django.core.validators import MinValueValidator, MaxValueValidator
from common.utils import get_signer from common.utils import get_signer
from .base import AssetUser from .base import AssetUser
from .asset import Asset
__all__ = ['AdminUser', 'SystemUser'] __all__ = ['AdminUser', 'SystemUser']
...@@ -144,6 +147,19 @@ class SystemUser(AssetUser): ...@@ -144,6 +147,19 @@ class SystemUser(AssetUser):
return False, matched_cmd return False, matched_cmd
return True, None 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: class Meta:
ordering = ['name'] ordering = ['name']
unique_together = [('name', 'org_id')] unique_together = [('name', 'org_id')]
......
...@@ -57,16 +57,16 @@ def on_system_user_update(sender, instance=None, created=True, **kwargs): ...@@ -57,16 +57,16 @@ def on_system_user_update(sender, instance=None, created=True, **kwargs):
push_system_user_to_assets.delay(instance, assets) push_system_user_to_assets.delay(instance, assets)
@receiver(m2m_changed, sender=SystemUser.nodes.through) # @receiver(m2m_changed, sender=SystemUser.nodes.through)
def on_system_user_nodes_change(sender, instance=None, **kwargs): # def on_system_user_nodes_change(sender, instance=None, **kwargs):
if instance and kwargs["action"] == "post_add": # if instance and kwargs["action"] == "post_add":
logger.info("System user `{}` nodes update signal received".format(instance)) # logger.info("System user `{}` nodes update signal received".format(instance))
assets = set() # assets = set()
nodes = kwargs['model'].objects.filter(pk__in=kwargs['pk_set']) # nodes = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
for node in nodes: # for node in nodes:
assets.update(set(node.get_all_assets())) # assets.update(set(node.get_all_assets()))
instance.assets.add(*tuple(assets)) # instance.assets.add(*tuple(assets))
#
@receiver(m2m_changed, sender=SystemUser.assets.through) @receiver(m2m_changed, sender=SystemUser.assets.through)
def on_system_user_assets_change(sender, instance=None, **kwargs): 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): ...@@ -347,7 +347,7 @@ def test_system_user_connectivity_util(system_user, assets, task_name):
@shared_task @shared_task
def test_system_user_connectivity_manual(system_user): def test_system_user_connectivity_manual(system_user):
task_name = _("Test system user connectivity: {}").format(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) return test_system_user_connectivity_util(system_user, assets, task_name)
...@@ -367,7 +367,7 @@ def test_system_user_connectivity_period(): ...@@ -367,7 +367,7 @@ def test_system_user_connectivity_period():
system_users = SystemUser.objects.all() system_users = SystemUser.objects.all()
for system_user in system_users: for system_user in system_users:
task_name = _("Test system user connectivity period: {}").format(system_user) 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) test_system_user_connectivity_util(system_user, assets, task_name)
...@@ -513,7 +513,7 @@ def push_system_user_util(system_user, assets, task_name): ...@@ -513,7 +513,7 @@ def push_system_user_util(system_user, assets, task_name):
@shared_task @shared_task
def push_system_user_to_assets_manual(system_user): 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) task_name = _("Push system users to assets: {}").format(system_user.name)
return push_system_user_util(system_user, assets, task_name=task_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'; ...@@ -68,10 +68,12 @@ var treeUrl = '{% url 'api-assets:node-children-tree' %}?assets=0';
// "otherMenu": "", // "otherMenu": "",
// "showAssets": false, // "showAssets": false,
// } // }
var inited = false;
function initNodeTree(options) { function initNodeTree(options) {
if (options.showAssets) { if (options.showAssets) {
treeUrl = setUrlParam(treeUrl, 'assets', '1') treeUrl = setUrlParam(treeUrl, 'assets', '1')
} }
var asyncTreeUrl = setUrlParam(treeUrl, 'refresh', '0');
var setting = { var setting = {
view: { view: {
dblClickExpand: false, dblClickExpand: false,
...@@ -84,7 +86,7 @@ function initNodeTree(options) { ...@@ -84,7 +86,7 @@ function initNodeTree(options) {
}, },
async: { async: {
enable: true, enable: true,
url: treeUrl, url: asyncTreeUrl,
autoParam: ["id=key", "name=n", "level=lv"], autoParam: ["id=key", "name=n", "level=lv"],
type: 'get' type: 'get'
}, },
...@@ -109,16 +111,20 @@ function initNodeTree(options) { ...@@ -109,16 +111,20 @@ function initNodeTree(options) {
beforeAsync: options.beforeAsync || defaultCallback("Before async") beforeAsync: options.beforeAsync || defaultCallback("Before async")
} }
}; };
$.get(treeUrl, function(data, status){ $.get(treeUrl, function (data, status) {
zNodes = data; zTree = $.fn.zTree.init($("#nodeTree"), setting, data);
zTree = $.fn.zTree.init($("#nodeTree"), setting, zNodes);
rootNodeAddDom(zTree, function () { rootNodeAddDom(zTree, function () {
treeUrl = setUrlParam(treeUrl, 'refresh', '1'); treeUrl = setUrlParam(treeUrl, 'refresh', '1');
initTree(); initNodeTree(options);
treeUrl = setUrlParam(treeUrl, 'refresh', '0') treeUrl = setUrlParam(treeUrl, 'refresh', '0');
}); });
inited = true;
}); });
if (inited) {
return
}
if (options.showMenu) { if (options.showMenu) {
showMenu = true; showMenu = true;
rMenu = $("#rMenu"); rMenu = $("#rMenu");
......
...@@ -8,6 +8,7 @@ from django_auth_ldap.backend import _LDAPUser, LDAPBackend ...@@ -8,6 +8,7 @@ from django_auth_ldap.backend import _LDAPUser, LDAPBackend
from django_auth_ldap.config import _LDAPConfig, LDAPSearch, LDAPSearchUnion from django_auth_ldap.config import _LDAPConfig, LDAPSearch, LDAPSearchUnion
from users.utils import construct_user_email from users.utils import construct_user_email
from common.const import LDAP_AD_ACCOUNT_DISABLE
logger = _LDAPConfig.get_logger() logger = _LDAPConfig.get_logger()
...@@ -17,6 +18,15 @@ class LDAPAuthorizationBackend(LDAPBackend): ...@@ -17,6 +18,15 @@ class LDAPAuthorizationBackend(LDAPBackend):
Override this class to override _LDAPUser to LDAPUser 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): def authenticate(self, request=None, username=None, password=None, **kwargs):
logger.info('Authentication LDAP backend') logger.info('Authentication LDAP backend')
if not username: if not username:
...@@ -25,34 +35,29 @@ class LDAPAuthorizationBackend(LDAPBackend): ...@@ -25,34 +35,29 @@ class LDAPAuthorizationBackend(LDAPBackend):
ldap_user = LDAPUser(self, username=username.strip(), request=request) ldap_user = LDAPUser(self, username=username.strip(), request=request)
user = self.authenticate_ldap_user(ldap_user, password) user = self.authenticate_ldap_user(ldap_user, password)
logger.info('Authenticate user: {}'.format(user)) logger.info('Authenticate user: {}'.format(user))
return user return user if self.user_can_authenticate(user) else None
def get_user(self, user_id): def get_user(self, user_id):
user = None user = None
try: try:
user = self.get_user_model().objects.get(pk=user_id) user = self.get_user_model().objects.get(pk=user_id)
LDAPUser(self, user=user) # This sets user.ldap_user LDAPUser(self, user=user) # This sets user.ldap_user
except ObjectDoesNotExist: except ObjectDoesNotExist:
pass pass
return user return user
def get_group_permissions(self, user, obj=None): def get_group_permissions(self, user, obj=None):
if not hasattr(user, 'ldap_user') and self.settings.AUTHORIZE_ALL_USERS: if not hasattr(user, 'ldap_user') and self.settings.AUTHORIZE_ALL_USERS:
LDAPUser(self, user=user) # This sets user.ldap_user LDAPUser(self, user=user) # This sets user.ldap_user
if hasattr(user, 'ldap_user'): if hasattr(user, 'ldap_user'):
permissions = user.ldap_user.get_group_permissions() permissions = user.ldap_user.get_group_permissions()
else: else:
permissions = set() permissions = set()
return permissions return permissions
def populate_user(self, username): def populate_user(self, username):
ldap_user = LDAPUser(self, username=username) ldap_user = LDAPUser(self, username=username)
user = ldap_user.populate_user() user = ldap_user.populate_user()
return user return user
...@@ -91,13 +96,19 @@ class LDAPUser(_LDAPUser): ...@@ -91,13 +96,19 @@ class LDAPUser(_LDAPUser):
for field, attr in self.settings.USER_ATTR_MAP.items(): for field, attr in self.settings.USER_ATTR_MAP.items():
try: try:
value = self.attrs[attr][0] 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: except LookupError:
logger.warning("{} does not have a value for the attribute {}".format(self.dn, attr)) logger.warning("{} does not have a value for the attribute {}".format(self.dn, attr))
else: else:
if not hasattr(self._user, field): if not hasattr(self._user, field):
continue continue
if isinstance(getattr(self._user, field), bool): 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) setattr(self._user, field, value)
email = getattr(self._user, 'email', '') email = getattr(self._user, 'email', '')
......
...@@ -26,8 +26,8 @@ class BaseOpenIDAuthorizationBackend(object): ...@@ -26,8 +26,8 @@ class BaseOpenIDAuthorizationBackend(object):
Reject users with is_active=False. Custom user models that don't have Reject users with is_active=False. Custom user models that don't have
that attribute are allowed. that attribute are allowed.
""" """
is_active = getattr(user, 'is_active', None) is_valid = getattr(user, 'is_valid', None)
return is_active or is_active is None return is_valid or is_valid is None
def get_user(self, user_id): def get_user(self, user_id):
try: try:
......
from rest_framework.request import Request
from django.http.request import QueryDict from django.http.request import QueryDict
from django.conf import settings from django.conf import settings
from django.dispatch import receiver from django.dispatch import receiver
...@@ -52,14 +53,15 @@ def on_ldap_create_user(sender, user, ldap_user, **kwargs): ...@@ -52,14 +53,15 @@ def on_ldap_create_user(sender, user, ldap_user, **kwargs):
def generate_data(username, request): 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_ip = request.data.get('remote_addr', None)
login_type = request.data.get('login_type', '') login_type = request.data.get('login_type', '')
user_agent = request.data.get('HTTP_USER_AGENT', '')
else: else:
login_ip = get_request_ip(request) login_ip = get_request_ip(request)
user_agent = request.META.get('HTTP_USER_AGENT', '')
login_type = 'W' login_type = 'W'
data = { data = {
'username': username, 'username': username,
'ip': login_ip, 'ip': login_ip,
......
...@@ -8,3 +8,7 @@ update_success_msg = _("%(name)s was updated successfully") ...@@ -8,3 +8,7 @@ update_success_msg = _("%(name)s was updated successfully")
FILE_END_GUARD = ">>> Content End <<<" FILE_END_GUARD = ">>> Content End <<<"
celery_task_pre_key = "CELERY_" celery_task_pre_key = "CELERY_"
KEY_CACHE_RESOURCES_ID = "RESOURCES_ID_{}" 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): ...@@ -137,6 +137,16 @@ class PermissionsMixin(UserPassesTestMixin):
return True 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): class NeedMFAVerify(permissions.BasePermission):
def has_permission(self, request, view): def has_permission(self, request, view):
mfa_verify_time = request.session.get('MFA_VERIFY_TIME', 0) mfa_verify_time = request.session.get('MFA_VERIFY_TIME', 0)
......
...@@ -58,8 +58,8 @@ class JMSCSVRender(BaseRenderer): ...@@ -58,8 +58,8 @@ class JMSCSVRender(BaseRenderer):
template = request.query_params.get('template', 'export') template = request.query_params.get('template', 'export')
view = renderer_context['view'] view = renderer_context['view']
if isinstance(data, dict) and data.get("count"): if isinstance(data, dict):
data = data["results"] data = data.get("results", [])
if template == 'import': if template == 'import':
data = [data[0]] if data else data data = [data[0]] if data else data
......
This diff is collapsed.
...@@ -9,13 +9,15 @@ from rest_framework.generics import ( ...@@ -9,13 +9,15 @@ from rest_framework.generics import (
) )
from rest_framework.pagination import LimitOffsetPagination 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.tree import TreeNodeSerializer
from common.utils import get_logger from common.utils import get_logger
from ..utils import ( from ..utils import (
AssetPermissionUtil, ParserNode, AssetPermissionUtil, ParserNode,
) )
from .mixin import UserPermissionCacheMixin, GrantAssetsMixin, NodesWithUngroupMixin from .mixin import (
UserPermissionCacheMixin, GrantAssetsMixin, NodesWithUngroupMixin
)
from .. import const from .. import const
from ..hands import User, Asset, Node, SystemUser, NodeSerializer from ..hands import User, Asset, Node, SystemUser, NodeSerializer
from .. import serializers from .. import serializers
...@@ -29,6 +31,7 @@ __all__ = [ ...@@ -29,6 +31,7 @@ __all__ = [
'UserGrantedNodesWithAssetsApi', 'UserGrantedNodeAssetsApi', 'UserGrantedNodesWithAssetsApi', 'UserGrantedNodeAssetsApi',
'ValidateUserAssetPermissionApi', 'UserGrantedNodesAsTreeApi', 'ValidateUserAssetPermissionApi', 'UserGrantedNodesAsTreeApi',
'UserGrantedNodesWithAssetsAsTreeApi', 'GetUserAssetPermissionActionsApi', 'UserGrantedNodesWithAssetsAsTreeApi', 'GetUserAssetPermissionActionsApi',
'RefreshAssetPermissionCacheApi'
] ]
...@@ -373,3 +376,12 @@ class GetUserAssetPermissionActionsApi(UserPermissionCacheMixin, RetrieveAPIView ...@@ -373,3 +376,12 @@ class GetUserAssetPermissionActionsApi(UserPermissionCacheMixin, RetrieveAPIView
actions = asset["system_users"].get(system_id, 0) actions = asset["system_users"].get(system_id, 0)
break break
return {"actions": actions} 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 @@ ...@@ -33,10 +33,14 @@
</div> </div>
</div> </div>
<div class="mail-box-header"> <div class="mail-box-header">
<div class="uc pull-left m-r-5"> <div class="btn-group uc pull-left m-r-5">
<a class="btn btn-sm btn-primary btn-create-permission"> <button class="btn btn-sm btn-primary btn-create-permission">
{% trans "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> </div>
<table class="table table-striped table-bordered table-hover" id="permission_list_table" style="width: 100%"> <table class="table table-striped table-bordered table-hover" id="permission_list_table" style="width: 100%">
<thead> <thead>
...@@ -232,6 +236,14 @@ $(document).ready(function(){ ...@@ -232,6 +236,14 @@ $(document).ready(function(){
.replace('{{ DEFAULT_PK }}', uid); .replace('{{ DEFAULT_PK }}', uid);
objectDelete($this, name, the_url); 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 () { .on('click', '.btn-create-permission', function () {
var url = "{% url 'perms:asset-permission-create' %}"; var url = "{% url 'perms:asset-permission-create' %}";
var nodes = zTree.getSelectedNodes(); var nodes = zTree.getSelectedNodes();
......
...@@ -57,6 +57,9 @@ asset_permission_urlpatterns = [ ...@@ -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/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/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: ...@@ -414,15 +414,12 @@ class AssetPermissionCacheMixin:
cache.delete_pattern(key) cache.delete_pattern(key)
self.expire_cache_meta() self.expire_cache_meta()
@classmethod
def expire_all_cache_meta(cls):
key = cls.CACHE_META_KEY_PREFIX + '*'
cache.delete_pattern(key)
@classmethod @classmethod
def expire_all_cache(cls): def expire_all_cache(cls):
key = cls.CACHE_KEY_PREFIX + '*' key = cls.CACHE_KEY_PREFIX + '*'
cache.delete_pattern(key) cache.delete_pattern(key)
meta_key = cls.CACHE_META_KEY_PREFIX + '*'
cache.delete_pattern(meta_key)
class AssetPermissionUtil(AssetPermissionCacheMixin): class AssetPermissionUtil(AssetPermissionCacheMixin):
......
...@@ -7,6 +7,7 @@ from django.utils.translation import ugettext_lazy as _ ...@@ -7,6 +7,7 @@ from django.utils.translation import ugettext_lazy as _
from users.models import User from users.models import User
from users.utils import construct_user_email from users.utils import construct_user_email
from common.utils import get_logger from common.utils import get_logger
from common.const import LDAP_AD_ACCOUNT_DISABLE
from .models import settings from .models import settings
...@@ -70,7 +71,12 @@ class LDAPUtil: ...@@ -70,7 +71,12 @@ class LDAPUtil:
for attr, mapping in self.attr_map.items(): for attr, mapping in self.attr_map.items():
if not hasattr(entry, mapping): if not hasattr(entry, mapping):
continue 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 return user_item
def search_user_items(self): def search_user_items(self):
...@@ -102,7 +108,9 @@ class LDAPUtil: ...@@ -102,7 +108,9 @@ class LDAPUtil:
if not hasattr(user, field): if not hasattr(user, field):
continue continue
if isinstance(getattr(user, field), bool): 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) setattr(user, field, value)
user.save() user.save()
......
...@@ -157,7 +157,7 @@ UserProfileForm.verbose_name = _("Profile") ...@@ -157,7 +157,7 @@ UserProfileForm.verbose_name = _("Profile")
class UserMFAForm(forms.ModelForm): class UserMFAForm(forms.ModelForm):
mfa_description = _( mfa_description = _(
'Tip: when enabled, ' 'When enabled, '
'you will enter the MFA binding process the next time you log in. ' 'you will enter the MFA binding process the next time you log in. '
'you can also directly bind in ' 'you can also directly bind in '
'"personal information -> quick modification -> change MFA Settings"!') '"personal information -> quick modification -> change MFA Settings"!')
......
...@@ -54,6 +54,12 @@ class AuthMixin: ...@@ -54,6 +54,12 @@ class AuthMixin:
def can_update_password(self): def can_update_password(self):
return self.is_local 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): def check_otp(self, code):
from ..utils import check_otp_code from ..utils import check_otp_code
return check_otp_code(self.otp_secret_key, code) return check_otp_code(self.otp_secret_key, code)
......
...@@ -122,7 +122,7 @@ function initTree() { ...@@ -122,7 +122,7 @@ function initTree() {
$.fn.zTree.init($("#assetTree"), setting, data); $.fn.zTree.init($("#assetTree"), setting, data);
zTree = $.fn.zTree.getZTreeObj("assetTree"); zTree = $.fn.zTree.getZTreeObj("assetTree");
rootNodeAddDom(zTree, function () { rootNodeAddDom(zTree, function () {
treeUrl = treeUrl.replace('cache_policy=1', 'cache_policy=2'); treeUrl = setUrlParam(treeUrl, 'cache_policy', '2');
initTree(); initTree();
}); });
}); });
...@@ -156,7 +156,7 @@ function loadLabels() { ...@@ -156,7 +156,7 @@ function loadLabels() {
} }
$(document).ready(function () { $(document).ready(function () {
loadLabels() {#loadLabels()#}
}).on('click', '.labels-menu li', function () { }).on('click', '.labels-menu li', function () {
var val = $(this).text(); var val = $(this).text();
$("#user_assets_table_filter input").val(val); $("#user_assets_table_filter input").val(val);
......
...@@ -73,14 +73,17 @@ ...@@ -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> <p id="noTerms" class="red-fonts" style="visibility: hidden; font-size: 10px; margin-top: 10px;">* {% trans 'Please choose the terms and conditions.' %}</p>
{% endif %} {% endif %}
{% bootstrap_form wizard.form %} {% 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 %} {% if form.mfa_description %}
<b>{{ form.mfa_description }}</b> <b>{{ form.mfa_description }}</b>
{% endif %} {% endif %}
{% if form.pubkey_description %} {% if form.pubkey_description and request.user.can_update_ssh_key %}
<span>或者:</span>
<a type="button" id="btn-reset-pubkey">{{ form.pubkey_description }}</a> <a type="button" id="btn-reset-pubkey">{{ form.pubkey_description }}</a>
{% endif %} {% endif %}
...@@ -121,26 +124,33 @@ ...@@ -121,26 +124,33 @@
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
$(document).on('click', ".fl_goto", function(){ $(document).ready(function(){
var $form = $('#fl_form'); var origin_ssh_key_text = $("#ssh_key_help_text").text();
$('<input />', {'name': 'wizard_goto_step', 'value': $(this).data('goto'), 'type': 'hidden'}).appendTo($form); var new_ssh_key_text = origin_ssh_key_text.replace('{}', "{{ request.user.source_display }}");
$form.submit(); $("#ssh_key_help_text").html(new_ssh_key_text)
return false; })
}).on('click', '#fl_submit', function(){ .on('click', ".fl_goto", function(){
var isFinish = $('#fl_submit').html() === "{% trans 'Finish' %}"; var $form = $('#fl_form');
var noChecked = !$('#acceptTerms').prop('checked'); $('<input />', {'name': 'wizard_goto_step', 'value': $(this).data('goto'), 'type': 'hidden'}).appendTo($form);
if ( isFinish && noChecked){ $form.submit();
$('#noTerms').css('visibility', 'visible'); return false;
} })
else{ .on('click', '#fl_submit', function(){
$('#fl_form').submit(); var isFinish = $('#fl_submit').html() === "{% trans 'Finish' %}";
return false; var noChecked = !$('#acceptTerms').prop('checked');
} if ( isFinish && noChecked){
}).on('click', '#btn-reset-pubkey', function () { $('#noTerms').css('visibility', 'visible');
var the_url = '{% url "users:user-pubkey-generate" %}'; }
window.open(the_url, "_blank"); else{
$('#fl_form').submit(); $('#fl_form').submit();
}) return false;
}
})
.on('click', '#btn-reset-pubkey', function () {
var the_url = '{% url "users:user-pubkey-generate" %}';
window.open(the_url, "_blank");
$('#fl_form').submit();
})
</script> </script>
{% endblock %} {% endblock %}
...@@ -119,10 +119,12 @@ ...@@ -119,10 +119,12 @@
<td>{% trans 'Last login' %}:</td> <td>{% trans 'Last login' %}:</td>
<td><b>{{ user_object.last_login|date:"Y-m-j H:i:s" }}</b></td> <td><b>{{ user_object.last_login|date:"Y-m-j H:i:s" }}</b></td>
</tr> </tr>
{% if user_object.can_update_password %}
<tr> <tr>
<td>{% trans 'Last password updated' %}:</td> <td>{% trans 'Last password updated' %}:</td>
<td><b>{{ user_object.date_password_last_updated|date:"Y-m-j H:i:s" }}</b></td> <td><b>{{ user_object.date_password_last_updated|date:"Y-m-j H:i:s" }}</b></td>
</tr> </tr>
{% endif %}
<tr> <tr>
<td>{% trans 'Comment' %}:</td> <td>{% trans 'Comment' %}:</td>
<td><b>{{ user_object.comment }}</b></td> <td><b>{{ user_object.comment }}</b></td>
...@@ -187,6 +189,7 @@ ...@@ -187,6 +189,7 @@
</td> </td>
</tr> </tr>
{% endif %} {% endif %}
{% if user_object.can_update_ssh_key %}
<tr> <tr>
<td>{% trans 'Send reset ssh key mail' %}:</td> <td>{% trans 'Send reset ssh key mail' %}:</td>
<td> <td>
...@@ -195,6 +198,7 @@ ...@@ -195,6 +198,7 @@
</span> </span>
</td> </td>
</tr> </tr>
{% endif %}
<tr style="{% if not unblock %}display:none{% endif %}"> <tr style="{% if not unblock %}display:none{% endif %}">
<td>{% trans 'Unblock user' %}</td> <td>{% trans 'Unblock user' %}</td>
<td> <td>
......
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
<script> <script>
var assetTableUrl = "{% url 'api-perms:user-assets' pk=object.id %}?cache_policy=1"; 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 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 () { $(document).ready(function () {
initTree(); initTree();
......
...@@ -39,12 +39,16 @@ ...@@ -39,12 +39,16 @@
<li> <li>
<a href="{% url 'users:user-profile-update' %}" class="text-center">{% trans 'Profile' %} </a> <a href="{% url 'users:user-profile-update' %}" class="text-center">{% trans 'Profile' %} </a>
</li> </li>
{% if request.user.can_update_password %}
<li class="active"> <li class="active">
<a href="{% url 'users:user-password-update' %}" class="text-center">{% trans 'Password' %} </a> <a href="{% url 'users:user-password-update' %}" class="text-center">{% trans 'Password' %} </a>
</li> </li>
{% endif %}
{% if request.user.can_update_ssh_key %}
<li> <li>
<a href="{% url 'users:user-pubkey-update' %}" class="text-center">{% trans 'Public key' %} </a> <a href="{% url 'users:user-pubkey-update' %}" class="text-center">{% trans 'Public key' %} </a>
</li> </li>
{% endif %}
</ul> </ul>
</div> </div>
<div class="tab-content" style="background-color: #ffffff"> <div class="tab-content" style="background-color: #ffffff">
......
...@@ -64,6 +64,7 @@ ...@@ -64,6 +64,7 @@
<td>{{ user.is_active|yesno:"Yes,No,Unkown" }}</td> <td>{{ user.is_active|yesno:"Yes,No,Unkown" }}</td>
</tr> </tr>
{% if user.can_update_ssh_key %}
<tr> <tr>
<td class="text-navy">{% trans 'Public key' %}</td> <td class="text-navy">{% trans 'Public key' %}</td>
<td> <td>
...@@ -81,6 +82,7 @@ ...@@ -81,6 +82,7 @@
</table> </table>
</td> </td>
</tr> </tr>
{% endif %}
<tr> <tr>
<td class="text-navy">{% trans 'MFA certification' %}</td> <td class="text-navy">{% trans 'MFA certification' %}</td>
<td> <td>
...@@ -108,10 +110,12 @@ ...@@ -108,10 +110,12 @@
<td class="text-navy">{% trans 'Last login' %}</td> <td class="text-navy">{% trans 'Last login' %}</td>
<td>{{ user.last_login|date:"Y-m-d H:i:s" }}</td> <td>{{ user.last_login|date:"Y-m-d H:i:s" }}</td>
</tr> </tr>
{% if user.can_update_password %}
<tr> <tr>
<td class="text-navy">{% trans 'Last password updated' %}</td> <td class="text-navy">{% trans 'Last password updated' %}</td>
<td>{{ user.date_password_last_updated|date:"Y-m-d H:i:s" }}</td> <td>{{ user.date_password_last_updated|date:"Y-m-d H:i:s" }}</td>
</tr> </tr>
{% endif %}
<tr> <tr>
<td class="text-navy">{% trans 'Date expired' %}</td> <td class="text-navy">{% trans 'Date expired' %}</td>
<td>{{ user.date_expired|date:"Y-m-d H:i:s" }}</td> <td>{{ user.date_expired|date:"Y-m-d H:i:s" }}</td>
...@@ -189,6 +193,7 @@ ...@@ -189,6 +193,7 @@
</td> </td>
</tr> </tr>
{% endif %} {% endif %}
{% if request.user.can_update_ssh_key %}
<tr> <tr>
<td>{% trans 'Update SSH public key' %}:</td> <td>{% trans 'Update SSH public key' %}:</td>
<td> <td>
...@@ -205,6 +210,7 @@ ...@@ -205,6 +210,7 @@
</span> </span>
</td> </td>
</tr> </tr>
{% endif %}
</tbody> </tbody>
</table> </table>
</div> </div>
......
...@@ -36,12 +36,16 @@ ...@@ -36,12 +36,16 @@
<li class="active"> <li class="active">
<a href="{% url 'users:user-profile-update' %}" class="text-center">{% trans 'Profile' %} </a> <a href="{% url 'users:user-profile-update' %}" class="text-center">{% trans 'Profile' %} </a>
</li> </li>
{% if request.user.can_update_password %}
<li> <li>
<a href="{% url 'users:user-password-update' %}" class="text-center">{% trans 'Password' %} </a> <a href="{% url 'users:user-password-update' %}" class="text-center">{% trans 'Password' %} </a>
</li> </li>
{% endif %}
{% if request.user.can_update_ssh_key %}
<li> <li>
<a href="{% url 'users:user-pubkey-update' %}" class="text-center">{% trans 'Public key' %} </a> <a href="{% url 'users:user-pubkey-update' %}" class="text-center">{% trans 'Public key' %} </a>
</li> </li>
{% endif %}
</ul> </ul>
</div> </div>
<div class="tab-content" style="background-color: #ffffff"> <div class="tab-content" style="background-color: #ffffff">
......
...@@ -36,12 +36,16 @@ ...@@ -36,12 +36,16 @@
<li> <li>
<a href="{% url 'users:user-profile-update' %}" class="text-center">{% trans 'Profile' %} </a> <a href="{% url 'users:user-profile-update' %}" class="text-center">{% trans 'Profile' %} </a>
</li> </li>
{% if request.user.can_update_password %}
<li> <li>
<a href="{% url 'users:user-password-update' %}" class="text-center">{% trans 'Password' %} </a> <a href="{% url 'users:user-password-update' %}" class="text-center">{% trans 'Password' %} </a>
</li> </li>
{% endif %}
{% if request.user.can_update_ssh_key %}
<li class="active"> <li class="active">
<a href="{% url 'users:user-pubkey-update' %}" class="text-center">{% trans 'Public key' %} </a> <a href="{% url 'users:user-pubkey-update' %}" class="text-center">{% trans 'Public key' %} </a>
</li> </li>
{% endif %}
</ul> </ul>
</div> </div>
<div class="tab-content" style="background-color: #ffffff"> <div class="tab-content" style="background-color: #ffffff">
......
...@@ -23,7 +23,16 @@ ...@@ -23,7 +23,16 @@
</div> </div>
</div> </div>
{% endif %} {% endif %}
{% if object.can_update_ssh_key %}
{% bootstrap_field form.public_key layout="horizontal" %} {% 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 %} {% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
...@@ -77,9 +86,13 @@ function passwordCheck() { ...@@ -77,9 +86,13 @@ function passwordCheck() {
$(document).ready(function(){ $(document).ready(function(){
passwordCheck(); passwordCheck();
var origin_text = $("#password_help_text").text(); var origin_password_text = $("#password_help_text").text();
var new_text = origin_text.replace('{}', "{{ object.source_display }}"); var new_password_text = origin_password_text.replace('{}', "{{ object.source_display }}");
$("#password_help_text").html(new_text); $("#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) { .on("submit", "form", function (evt) {
......
...@@ -2,40 +2,32 @@ ...@@ -2,40 +2,32 @@
from __future__ import unicode_literals 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 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.contrib.messages.views import SuccessMessageMixin
from django.core.cache import cache from django.core.cache import cache
from django.conf import settings from django.conf import settings
from django.http import HttpResponse, JsonResponse from django.http import HttpResponse
from django.shortcuts import redirect from django.shortcuts import redirect
from django.urls import reverse_lazy, reverse from django.urls import reverse_lazy, reverse
from django.utils import timezone
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.utils.decorators import method_decorator
from django.views import View from django.views import View
from django.views.generic.base import TemplateView from django.views.generic.base import TemplateView
from django.db import transaction
from django.views.generic.edit import ( from django.views.generic.edit import (
CreateView, UpdateView, FormView CreateView, UpdateView, FormView
) )
from django.views.generic.detail import DetailView 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 django.contrib.auth import logout as auth_logout
from common.const import ( from common.const import (
create_success_msg, update_success_msg, KEY_CACHE_RESOURCES_ID create_success_msg, update_success_msg, KEY_CACHE_RESOURCES_ID
) )
from common.mixins import JSONResponseMixin from common.utils import get_logger, ssh_key_gen
from common.utils import get_logger, get_object_or_none, is_uuid, ssh_key_gen from common.permissions import (
from common.permissions import PermissionsMixin, IsOrgAdmin, IsValidUser PermissionsMixin, IsOrgAdmin, IsValidUser,
UserCanUpdatePassword, UserCanUpdateSSHKey,
)
from orgs.utils import current_org from orgs.utils import current_org
from .. import forms from .. import forms
from ..models import User, UserGroup from ..models import User, UserGroup
...@@ -260,6 +252,7 @@ class UserPasswordUpdateView(PermissionsMixin, UpdateView): ...@@ -260,6 +252,7 @@ class UserPasswordUpdateView(PermissionsMixin, UpdateView):
model = User model = User
form_class = forms.UserPasswordForm form_class = forms.UserPasswordForm
success_url = reverse_lazy('users:user-profile') success_url = reverse_lazy('users:user-profile')
permission_classes = [IsValidUser, UserCanUpdatePassword]
def get_object(self, queryset=None): def get_object(self, queryset=None):
return self.request.user return self.request.user
...@@ -279,12 +272,6 @@ class UserPasswordUpdateView(PermissionsMixin, UpdateView): ...@@ -279,12 +272,6 @@ class UserPasswordUpdateView(PermissionsMixin, UpdateView):
return super().get_success_url() return super().get_success_url()
def form_valid(self, form): 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') password = form.cleaned_data.get('new_password')
is_ok = check_password_rules(password) is_ok = check_password_rules(password)
if not is_ok: if not is_ok:
...@@ -300,7 +287,7 @@ class UserPublicKeyUpdateView(PermissionsMixin, UpdateView): ...@@ -300,7 +287,7 @@ class UserPublicKeyUpdateView(PermissionsMixin, UpdateView):
template_name = 'users/user_pubkey_update.html' template_name = 'users/user_pubkey_update.html'
model = User model = User
form_class = forms.UserPublicKeyForm form_class = forms.UserPublicKeyForm
permission_classes = [IsValidUser] permission_classes = [IsValidUser, UserCanUpdateSSHKey]
success_url = reverse_lazy('users:user-profile') success_url = reverse_lazy('users:user-profile')
def get_object(self, queryset=None): def get_object(self, queryset=None):
......
...@@ -81,7 +81,7 @@ def make_migrations(): ...@@ -81,7 +81,7 @@ def make_migrations():
def collect_static(): def collect_static():
print("Collect static files") print("Collect static files")
os.chdir(os.path.join(BASE_DIR, 'apps')) 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(): 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