Compare commits

...

4 Commits

Author SHA1 Message Date
xxx
afc82856f6 fixed: 学员批量导入邮箱 2024-01-26 11:05:59 +08:00
xxx
e215fbb59c 移除身份证号查看权限 2024-01-22 10:29:49 +08:00
xxx
1e92f19923 禁用用户不同步 2023-12-26 15:34:56 +08:00
xxx
8d03678e71 LDAP用户分页读取 2023-12-26 14:57:15 +08:00
10 changed files with 141 additions and 53 deletions

View File

@ -0,0 +1,34 @@
/*
* Copyright (C) 2023 杭州白书科技有限公司
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package xyz.playedu.api.bus;
import org.springframework.stereotype.Component;
import xyz.playedu.common.constant.ConfigConstant;
import java.util.Map;
@Component
public class UserBus {
public String getUserDefaultAvatar(Map<String, String> configData) {
String memberDefaultAvatar = configData.get(ConfigConstant.MEMBER_DEFAULT_AVATAR);
if (memberDefaultAvatar == null || memberDefaultAvatar.trim().isEmpty()) {
return configData.get(ConfigConstant.SYSTEM_API_URL) + "/images/default_avatar.png";
}
return memberDefaultAvatar;
}
}

View File

@ -22,6 +22,7 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import xyz.playedu.api.bus.UserBus;
import xyz.playedu.common.annotation.Log;
import xyz.playedu.common.constant.BusinessTypeConstant;
import xyz.playedu.common.constant.ConfigConstant;
@ -45,6 +46,8 @@ public class SystemController {
@Autowired private CategoryService categoryService;
@Autowired private UserBus userBus;
@GetMapping("/config")
@Log(title = "其它-系统配置", businessType = BusinessTypeConstant.GET)
public JsonResponse config() {
@ -68,12 +71,7 @@ public class SystemController {
data.put(ConfigConstant.SYSTEM_H5_URL, configData.get(ConfigConstant.SYSTEM_H5_URL));
// 学员的默认头像
String memberDefaultAvatar = configData.get(ConfigConstant.MEMBER_DEFAULT_AVATAR);
if (memberDefaultAvatar == null || memberDefaultAvatar.trim().isEmpty()) {
data.put(ConfigConstant.MEMBER_DEFAULT_AVATAR, apiUrl + "/images/default_avatar.png");
} else {
data.put(ConfigConstant.MEMBER_DEFAULT_AVATAR, memberDefaultAvatar);
}
data.put(ConfigConstant.MEMBER_DEFAULT_AVATAR, userBus.getUserDefaultAvatar(configData));
// 内置的三个线上课封面
List<String> defaultCourseThumbs = new ArrayList<>();

View File

@ -28,6 +28,7 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import xyz.playedu.api.bus.UserBus;
import xyz.playedu.api.event.UserCourseHourRecordDestroyEvent;
import xyz.playedu.api.event.UserCourseRecordDestroyEvent;
import xyz.playedu.api.event.UserDestroyEvent;
@ -37,7 +38,6 @@ import xyz.playedu.common.annotation.BackendPermission;
import xyz.playedu.common.annotation.Log;
import xyz.playedu.common.constant.BPermissionConstant;
import xyz.playedu.common.constant.BusinessTypeConstant;
import xyz.playedu.common.constant.ConfigConstant;
import xyz.playedu.common.constant.SystemConstant;
import xyz.playedu.common.context.BCtx;
import xyz.playedu.common.domain.*;
@ -232,7 +232,7 @@ public class UserController {
@PostMapping("/store-batch")
@Transactional
@Log(title = "学员-批量导入", businessType = BusinessTypeConstant.INSERT)
public JsonResponse batchStore(@RequestBody @Validated UserImportRequest req) {
public JsonResponse batchStore(@RequestBody @Validated UserImportRequest req, UserBus userBus) {
List<UserImportRequest.UserItem> users = req.getUsers();
if (users.isEmpty()) {
return JsonResponse.error("数据为空");
@ -245,7 +245,7 @@ public class UserController {
Integer startLine = req.getStartLine();
// 默认的学员头像
String defaultAvatar = BCtx.getConfig().get(ConfigConstant.MEMBER_DEFAULT_AVATAR);
String defaultAvatar = userBus.getUserDefaultAvatar(BCtx.getConfig());
List<String[]> errorLines = new ArrayList<>();
errorLines.add(new String[] {"错误行", "错误信息"}); // 错误表-表头

View File

@ -33,6 +33,7 @@ import xyz.playedu.common.util.ldap.LdapTransformDepartment;
import xyz.playedu.common.util.ldap.LdapTransformUser;
import xyz.playedu.common.util.ldap.LdapUtil;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -162,7 +163,7 @@ public class LDAPBus {
}
}
public void userSync() throws NamingException {
public void userSync() throws NamingException, IOException {
LdapConfig ldapConfig = appConfigService.ldapConfig();
List<LdapTransformUser> userList =
@ -179,6 +180,9 @@ public class LDAPBus {
String defaultAvatar = appConfigService.defaultAvatar();
for (LdapTransformUser ldapTransformUser : userList) {
if (ldapTransformUser.isBan()) {
continue;
}
singleUserSync(ldapTransformUser, defaultAvatar);
}
}

View File

@ -56,6 +56,5 @@ public class BPermissionConstant {
public static final String DATA_USER_NAME = "data-user-name";
public static final String DATA_USER_EMAIL = "data-user-email";
public static final String DATA_USER_ID_CARD = "data-user-id-card";
public static final String DATA_ADMIN_EMAIL = "data-admin-email";
}

View File

@ -121,14 +121,6 @@ public class User implements Serializable {
getEmail());
}
@JsonGetter("id_card")
public String transformIdCard() {
return BackendBus.valueHidden(
BPermissionConstant.DATA_USER_ID_CARD,
BackendConstant.PRIVACY_FIELD_TYPE_ID_CARD,
getIdCard());
}
@Override
public boolean equals(Object that) {
if (this == that) {

View File

@ -32,4 +32,6 @@ public class LdapTransformUser {
private String email;
private String uid;
private boolean ban;
}

View File

@ -20,6 +20,7 @@ import lombok.extern.slf4j.Slf4j;
import xyz.playedu.common.exception.ServiceException;
import xyz.playedu.common.util.StringUtil;
import java.io.IOException;
import java.util.*;
import javax.naming.Context;
@ -29,8 +30,7 @@ import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
import javax.naming.ldap.*;
@Slf4j
public class LdapUtil {
@ -55,12 +55,21 @@ public class LdapUtil {
"sAMAccountName",
"displayName",
"uSNCreated", // AD域的唯一属性
"userAccountControl",
// 公用属性
"mail",
};
private static final String[] OU_RETURN_ATTRS = new String[] {"ou", "usncreated"};
// 514 - 禁用账户
// 546 - 禁用账户 不需密码
// 66050 - 禁用账户 密码未过期
// 66080 - 禁用账户 密码未过期且不需密码
// 66082 - 禁用账户 密码未过期且不需密码
private static final String[] DISABLE_USER_ACCOUNT_CONTROL =
new String[] {"514", "546", "66050", "66080", "66082"};
public static LdapContext initContext(String url, String adminUser, String adminPass)
throws NamingException {
Hashtable<String, String> context = new Hashtable<>();
@ -75,41 +84,75 @@ public class LdapUtil {
}
public static List<LdapTransformUser> users(
String url, String adminUser, String adminPass, String baseDN) throws NamingException {
String url, String adminUser, String adminPass, String baseDN)
throws NamingException, IOException {
LdapContext ldapContext = initContext(url, adminUser, adminPass);
int pageSize = 1000;
List<LdapTransformUser> users = new ArrayList<>();
SearchControls controls = new SearchControls();
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
controls.setReturningAttributes(USER_RETURN_ATTRS);
controls.setReturningObjFlag(true);
NamingEnumeration<SearchResult> result = null;
try {
result = ldapContext.search(baseDN, USER_OBJECT_CLASS, controls);
} catch (NamingException e) {
log.error("LDAP用户查询失败", e);
} finally {
closeContext(ldapContext);
byte[] cookie = null;
while (true) {
try {
if (cookie != null) {
ldapContext.setRequestControls(
new Control[] {
new PagedResultsControl(pageSize, cookie, false),
});
} else {
ldapContext.setRequestControls(
new Control[] {new PagedResultsControl(pageSize, false)});
}
NamingEnumeration<SearchResult> result =
ldapContext.search(baseDN, USER_OBJECT_CLASS, controls);
while (result.hasMoreElements()) {
SearchResult item = result.nextElement();
if (item != null) {
LdapTransformUser ldapTransformUser = parseTransformUser(item, baseDN);
if (ldapTransformUser != null) {
users.add(ldapTransformUser);
}
}
}
cookie = parseCookie(ldapContext.getResponseControls());
if (cookie == null || cookie.length == 0) {
break;
}
} catch (NamingException e) {
log.error("LDAP用户查询失败", e);
break;
}
}
if (result == null || !result.hasMoreElements()) {
closeContext(ldapContext);
if (users.isEmpty()) {
log.info("LDAP服务中没有用户");
return null;
}
List<LdapTransformUser> users = new ArrayList<>();
while (result.hasMoreElements()) {
SearchResult item = result.nextElement();
if (item == null) {
continue;
}
LdapTransformUser ldapTransformUser = parseTransformUser(item, baseDN);
users.add(ldapTransformUser);
}
return users;
}
private static byte[] parseCookie(Control[] controls) throws NamingException {
if (controls != null) {
for (Control control : controls) {
if (control instanceof PagedResultsResponseControl) {
return ((PagedResultsResponseControl) control).getCookie();
}
}
}
return null;
}
public static List<LdapTransformDepartment> departments(
String url, String adminUser, String adminPass, String baseDN) throws NamingException {
LdapContext ldapContext = initContext(url, adminUser, adminPass);
@ -253,6 +296,16 @@ public class LdapUtil {
LdapTransformUser ldapUser = new LdapTransformUser();
ldapUser.setDn(item.getName());
if (attributes.get("userAccountControl") != null) {
String userAccountControl = (String) attributes.get("userAccountControl").get();
for (String s : DISABLE_USER_ACCOUNT_CONTROL) {
if (s.equals(userAccountControl)) {
ldapUser.setBan(true);
break;
}
}
}
// name解析
String displayName = getAttribute(attributes, "displayName");
if (StringUtil.isEmpty(displayName)) {

View File

@ -275,15 +275,6 @@ public class AdminPermissionCheck implements CommandLineRunner {
setSlug(BPermissionConstant.DATA_USER_NAME);
}
},
new AdminPermission() {
{
setSort(20);
setName("身份证号");
setSlug(
BPermissionConstant
.DATA_USER_ID_CARD);
}
},
});
}
});

View File

@ -36,11 +36,12 @@ public class UpgradeCheck implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
upgrade_v1_beta7();
upgrade_v1_4();
upgrade_1_beta7();
upgrade_1_4();
upgrade_1_6();
}
private void upgrade_v1_4() {
private void upgrade_1_4() {
appConfigService.remove(
appConfigService
.query()
@ -54,7 +55,7 @@ public class UpgradeCheck implements CommandLineRunner {
}));
}
private void upgrade_v1_beta7() {
private void upgrade_1_beta7() {
appConfigService.update(
new AppConfig() {
{
@ -75,4 +76,18 @@ public class UpgradeCheck implements CommandLineRunner {
}
}));
}
private void upgrade_1_6() {
permissionService.remove(
permissionService
.query()
.getWrapper()
.in(
"slug",
new ArrayList<>() {
{
add("data-user-id-card");
}
}));
}
}