Unverified 提交 f4c53cf3 authored 作者: fit2cloud-chenyw's avatar fit2cloud-chenyw 提交者: GitHub

Merge pull request #788 from dataease/pr@dev@feat_ldap

feat: 对接ldap基本功能
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>dataease-server</artifactId> <artifactId>dataease-server</artifactId>
<groupId>io.dataease</groupId> <groupId>io.dataease</groupId>
<version>1.2.0</version> <version>1.3.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
...@@ -316,7 +316,7 @@ ...@@ -316,7 +316,7 @@
<dependency> <dependency>
<groupId>io.dataease</groupId> <groupId>io.dataease</groupId>
<artifactId>dataease-plugin-interface</artifactId> <artifactId>dataease-plugin-interface</artifactId>
<version>1.2</version> <version>1.3</version>
</dependency> </dependency>
<dependency> <dependency>
...@@ -337,6 +337,11 @@ ...@@ -337,6 +337,11 @@
<version>5.7.4</version> <version>5.7.4</version>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-ldap</artifactId>
</dependency>
<!--<dependency> <!--<dependency>
<groupId>org.apache.poi</groupId> <groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId> <artifactId>poi-ooxml</artifactId>
......
...@@ -4,6 +4,7 @@ import com.github.xiaoymin.knife4j.annotations.ApiSupport; ...@@ -4,6 +4,7 @@ import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.dataease.auth.api.dto.CurrentUserDto; import io.dataease.auth.api.dto.CurrentUserDto;
import io.dataease.auth.api.dto.LoginDto; import io.dataease.auth.api.dto.LoginDto;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
...@@ -41,4 +42,8 @@ public interface AuthApi { ...@@ -41,4 +42,8 @@ public interface AuthApi {
@PostMapping("/validateName") @PostMapping("/validateName")
Boolean validateName(Map<String, String> nameDto); Boolean validateName(Map<String, String> nameDto);
@ApiOperation("是否开启ldap")
@PostMapping("/isOpenLdap")
boolean isOpenLdap();
} }
...@@ -13,4 +13,12 @@ public class LoginDto implements Serializable { ...@@ -13,4 +13,12 @@ public class LoginDto implements Serializable {
@ApiModelProperty(value = "密码", required = true) @ApiModelProperty(value = "密码", required = true)
private String password; private String password;
/**
* 0: 默认登录
* 1:ldap登录
* 2:单点登录
*/
@ApiModelProperty(value = "登录方式", required = true, allowableValues = "0, 1, 2")
private int loginType;
} }
...@@ -14,9 +14,13 @@ import io.dataease.commons.utils.BeanUtils; ...@@ -14,9 +14,13 @@ import io.dataease.commons.utils.BeanUtils;
import io.dataease.commons.utils.CodingUtil; import io.dataease.commons.utils.CodingUtil;
import io.dataease.commons.utils.LogUtil; import io.dataease.commons.utils.LogUtil;
import io.dataease.commons.utils.ServletUtils; import io.dataease.commons.utils.ServletUtils;
import io.dataease.exception.DataEaseException; import io.dataease.exception.DataEaseException;
import io.dataease.i18n.Translator; import io.dataease.i18n.Translator;
import io.dataease.plugins.config.SpringContextUtil;
import io.dataease.plugins.util.PluginUtils;
import io.dataease.plugins.xpack.ldap.dto.request.LdapValidateRequest;
import io.dataease.plugins.xpack.ldap.dto.response.ValidateResult;
import io.dataease.plugins.xpack.ldap.service.LdapXpackService;
import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils; import org.apache.shiro.SecurityUtils;
...@@ -35,10 +39,29 @@ public class AuthServer implements AuthApi { ...@@ -35,10 +39,29 @@ public class AuthServer implements AuthApi {
private AuthUserService authUserService; private AuthUserService authUserService;
@Override @Override
public Object login(@RequestBody LoginDto loginDto) throws Exception { public Object login(@RequestBody LoginDto loginDto) throws Exception {
String username = loginDto.getUsername(); String username = loginDto.getUsername();
String password = loginDto.getPassword(); String password = loginDto.getPassword();
String pwd = RsaUtil.decryptByPrivateKey(RsaProperties.privateKey, password);
// 增加ldap登录方式
Integer loginType = loginDto.getLoginType();
boolean isSupportLdap = authUserService.supportLdap();
if (loginType == 1 && isSupportLdap) {
LdapXpackService ldapXpackService = SpringContextUtil.getBean(LdapXpackService.class);
LdapValidateRequest request = LdapValidateRequest.builder().userName(username).password(pwd).build();
ValidateResult validateResult = ldapXpackService.login(request);
if (!validateResult.isSuccess()) {
DataEaseException.throwException(validateResult.getMsg());
}
username = validateResult.getUserName();
}
// 增加ldap登录方式
SysUserEntity user = authUserService.getUserByName(username); SysUserEntity user = authUserService.getUserByName(username);
if (ObjectUtils.isEmpty(user)) { if (ObjectUtils.isEmpty(user)) {
...@@ -48,14 +71,19 @@ public class AuthServer implements AuthApi { ...@@ -48,14 +71,19 @@ public class AuthServer implements AuthApi {
DataEaseException.throwException(Translator.get("i18n_id_or_pwd_error")); DataEaseException.throwException(Translator.get("i18n_id_or_pwd_error"));
} }
String realPwd = user.getPassword(); String realPwd = user.getPassword();
// 普通登录需要验证密码
if (loginType == 0 || !isSupportLdap) {
//私钥解密 //私钥解密
String pwd = RsaUtil.decryptByPrivateKey(RsaProperties.privateKey, password);
//md5加密 //md5加密
pwd = CodingUtil.md5(pwd); pwd = CodingUtil.md5(pwd);
if (!StringUtils.equals(pwd, realPwd)) { if (!StringUtils.equals(pwd, realPwd)) {
DataEaseException.throwException(Translator.get("i18n_id_or_pwd_error")); DataEaseException.throwException(Translator.get("i18n_id_or_pwd_error"));
} }
}
Map<String, Object> result = new HashMap<>(); Map<String, Object> result = new HashMap<>();
TokenInfo tokenInfo = TokenInfo.builder().userId(user.getUserId()).username(username).build(); TokenInfo tokenInfo = TokenInfo.builder().userId(user.getUserId()).username(username).build();
String token = JWTUtils.sign(tokenInfo, realPwd); String token = JWTUtils.sign(tokenInfo, realPwd);
...@@ -108,6 +136,14 @@ public class AuthServer implements AuthApi { ...@@ -108,6 +136,14 @@ public class AuthServer implements AuthApi {
return true; return true;
} }
@Override
public boolean isOpenLdap() {
Boolean licValid = PluginUtils.licValid();
if(!licValid) return false;
boolean open = authUserService.supportLdap();
return open;
}
/*@Override /*@Override
public Boolean isLogin() { public Boolean isLogin() {
return null; return null;
......
...@@ -21,6 +21,8 @@ public interface AuthUserService { ...@@ -21,6 +21,8 @@ public interface AuthUserService {
void clearCache(Long userId); void clearCache(Long userId);
boolean supportLdap();
} }
...@@ -8,6 +8,9 @@ import io.dataease.base.mapper.ext.AuthMapper; ...@@ -8,6 +8,9 @@ import io.dataease.base.mapper.ext.AuthMapper;
import io.dataease.auth.service.AuthUserService; import io.dataease.auth.service.AuthUserService;
import io.dataease.commons.constants.AuthConstants; import io.dataease.commons.constants.AuthConstants;
import io.dataease.commons.utils.LogUtil; import io.dataease.commons.utils.LogUtil;
import io.dataease.plugins.config.SpringContextUtil;
import io.dataease.plugins.xpack.ldap.service.LdapXpackService;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Cacheable;
...@@ -102,4 +105,10 @@ public class AuthUserServiceImpl implements AuthUserService { ...@@ -102,4 +105,10 @@ public class AuthUserServiceImpl implements AuthUserService {
LogUtil.info("正在清除用户缓存【{}】",userId); LogUtil.info("正在清除用户缓存【{}】",userId);
} }
@Override
public boolean supportLdap() {
LdapXpackService ldapXpackService = SpringContextUtil.getBean(LdapXpackService.class);
if(ObjectUtils.isEmpty(ldapXpackService)) return false;
return ldapXpackService.isOpen();
}
} }
...@@ -58,6 +58,7 @@ public class ShiroServiceImpl implements ShiroService { ...@@ -58,6 +58,7 @@ public class ShiroServiceImpl implements ShiroService {
filterChainDefinitionMap.put("/api/auth/login", ANON); filterChainDefinitionMap.put("/api/auth/login", ANON);
filterChainDefinitionMap.put("/api/auth/logout", ANON); filterChainDefinitionMap.put("/api/auth/logout", ANON);
filterChainDefinitionMap.put("/api/auth/validateName", ANON); filterChainDefinitionMap.put("/api/auth/validateName", ANON);
filterChainDefinitionMap.put("/api/auth/isOpenLdap", ANON);
filterChainDefinitionMap.put("/unauth", ANON); filterChainDefinitionMap.put("/unauth", ANON);
filterChainDefinitionMap.put("/display/**", ANON); filterChainDefinitionMap.put("/display/**", ANON);
filterChainDefinitionMap.put("/tokenExpired", ANON); filterChainDefinitionMap.put("/tokenExpired", ANON);
......
package io.dataease.base.domain; package io.dataease.base.domain;
import java.io.Serializable; import java.io.Serializable;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@Data @Data
public class SysUser implements Serializable { public class SysUser implements Serializable {
@ApiModelProperty(value = "用户ID" , allowEmptyValue = false, position = 0)
private Long userId; private Long userId;
@ApiModelProperty(value = "组织ID" , allowEmptyValue = false, position = 7)
private Long deptId; private Long deptId;
@ApiModelProperty(value = "账号" , required = true)
private String username; private String username;
@ApiModelProperty(value = "姓名" , required = true, position = 2)
private String nickName; private String nickName;
@ApiModelProperty(value = "性别" ,allowableValues = "男,女", allowEmptyValue = true, position = 5)
private String gender; private String gender;
@ApiModelProperty(value = "电话" , allowEmptyValue = true, position = 1)
private String phone; private String phone;
@ApiModelProperty(value = "邮箱" , required = true, position = 3)
private String email; private String email;
@ApiModelProperty(value = "密码" , required = true, position = 4)
private String password; private String password;
@ApiModelProperty(hidden = true)
private Boolean isAdmin; private Boolean isAdmin;
@ApiModelProperty(value = "状态" , allowableValues = "1,0", required = true, position = 6)
private Long enabled; private Long enabled;
@ApiModelProperty(hidden = true)
private String createBy; private String createBy;
@ApiModelProperty(hidden = true)
private String updateBy; private String updateBy;
@ApiModelProperty(hidden = true)
private Long pwdResetTime; private Long pwdResetTime;
@ApiModelProperty(hidden = true)
private Long createTime; private Long createTime;
@ApiModelProperty(hidden = true)
private Long updateTime; private Long updateTime;
@ApiModelProperty(hidden = true)
private String language; private String language;
private Integer from;
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
} }
\ No newline at end of file
...@@ -1153,6 +1153,66 @@ public class SysUserExample { ...@@ -1153,6 +1153,66 @@ public class SysUserExample {
addCriterion("`language` not between", value1, value2, "language"); addCriterion("`language` not between", value1, value2, "language");
return (Criteria) this; return (Criteria) this;
} }
public Criteria andFromIsNull() {
addCriterion("`from` is null");
return (Criteria) this;
}
public Criteria andFromIsNotNull() {
addCriterion("`from` is not null");
return (Criteria) this;
}
public Criteria andFromEqualTo(Integer value) {
addCriterion("`from` =", value, "from");
return (Criteria) this;
}
public Criteria andFromNotEqualTo(Integer value) {
addCriterion("`from` <>", value, "from");
return (Criteria) this;
}
public Criteria andFromGreaterThan(Integer value) {
addCriterion("`from` >", value, "from");
return (Criteria) this;
}
public Criteria andFromGreaterThanOrEqualTo(Integer value) {
addCriterion("`from` >=", value, "from");
return (Criteria) this;
}
public Criteria andFromLessThan(Integer value) {
addCriterion("`from` <", value, "from");
return (Criteria) this;
}
public Criteria andFromLessThanOrEqualTo(Integer value) {
addCriterion("`from` <=", value, "from");
return (Criteria) this;
}
public Criteria andFromIn(List<Integer> values) {
addCriterion("`from` in", values, "from");
return (Criteria) this;
}
public Criteria andFromNotIn(List<Integer> values) {
addCriterion("`from` not in", values, "from");
return (Criteria) this;
}
public Criteria andFromBetween(Integer value1, Integer value2) {
addCriterion("`from` between", value1, value2, "from");
return (Criteria) this;
}
public Criteria andFromNotBetween(Integer value1, Integer value2) {
addCriterion("`from` not between", value1, value2, "from");
return (Criteria) this;
}
} }
public static class Criteria extends GeneratedCriteria { public static class Criteria extends GeneratedCriteria {
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
<result column="create_time" jdbcType="BIGINT" property="createTime" /> <result column="create_time" jdbcType="BIGINT" property="createTime" />
<result column="update_time" jdbcType="BIGINT" property="updateTime" /> <result column="update_time" jdbcType="BIGINT" property="updateTime" />
<result column="language" jdbcType="VARCHAR" property="language" /> <result column="language" jdbcType="VARCHAR" property="language" />
<result column="from" jdbcType="INTEGER" property="from" />
</resultMap> </resultMap>
<sql id="Example_Where_Clause"> <sql id="Example_Where_Clause">
<where> <where>
...@@ -79,7 +80,8 @@ ...@@ -79,7 +80,8 @@
</sql> </sql>
<sql id="Base_Column_List"> <sql id="Base_Column_List">
user_id, dept_id, username, nick_name, gender, phone, email, `password`, is_admin, user_id, dept_id, username, nick_name, gender, phone, email, `password`, is_admin,
enabled, create_by, update_by, pwd_reset_time, create_time, update_time, `language` enabled, create_by, update_by, pwd_reset_time, create_time, update_time, `language`,
`from`
</sql> </sql>
<select id="selectByExample" parameterType="io.dataease.base.domain.SysUserExample" resultMap="BaseResultMap"> <select id="selectByExample" parameterType="io.dataease.base.domain.SysUserExample" resultMap="BaseResultMap">
select select
...@@ -117,13 +119,13 @@ ...@@ -117,13 +119,13 @@
email, `password`, is_admin, email, `password`, is_admin,
enabled, create_by, update_by, enabled, create_by, update_by,
pwd_reset_time, create_time, update_time, pwd_reset_time, create_time, update_time,
`language`) `language`, `from`)
values (#{userId,jdbcType=BIGINT}, #{deptId,jdbcType=BIGINT}, #{username,jdbcType=VARCHAR}, values (#{userId,jdbcType=BIGINT}, #{deptId,jdbcType=BIGINT}, #{username,jdbcType=VARCHAR},
#{nickName,jdbcType=VARCHAR}, #{gender,jdbcType=VARCHAR}, #{phone,jdbcType=VARCHAR}, #{nickName,jdbcType=VARCHAR}, #{gender,jdbcType=VARCHAR}, #{phone,jdbcType=VARCHAR},
#{email,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}, #{isAdmin,jdbcType=BIT}, #{email,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}, #{isAdmin,jdbcType=BIT},
#{enabled,jdbcType=BIGINT}, #{createBy,jdbcType=VARCHAR}, #{updateBy,jdbcType=VARCHAR}, #{enabled,jdbcType=BIGINT}, #{createBy,jdbcType=VARCHAR}, #{updateBy,jdbcType=VARCHAR},
#{pwdResetTime,jdbcType=BIGINT}, #{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT}, #{pwdResetTime,jdbcType=BIGINT}, #{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT},
#{language,jdbcType=VARCHAR}) #{language,jdbcType=VARCHAR}, #{from,jdbcType=INTEGER})
</insert> </insert>
<insert id="insertSelective" parameterType="io.dataease.base.domain.SysUser"> <insert id="insertSelective" parameterType="io.dataease.base.domain.SysUser">
insert into sys_user insert into sys_user
...@@ -176,6 +178,9 @@ ...@@ -176,6 +178,9 @@
<if test="language != null"> <if test="language != null">
`language`, `language`,
</if> </if>
<if test="from != null">
`from`,
</if>
</trim> </trim>
<trim prefix="values (" suffix=")" suffixOverrides=","> <trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="userId != null"> <if test="userId != null">
...@@ -226,6 +231,9 @@ ...@@ -226,6 +231,9 @@
<if test="language != null"> <if test="language != null">
#{language,jdbcType=VARCHAR}, #{language,jdbcType=VARCHAR},
</if> </if>
<if test="from != null">
#{from,jdbcType=INTEGER},
</if>
</trim> </trim>
</insert> </insert>
<select id="countByExample" parameterType="io.dataease.base.domain.SysUserExample" resultType="java.lang.Long"> <select id="countByExample" parameterType="io.dataease.base.domain.SysUserExample" resultType="java.lang.Long">
...@@ -285,6 +293,9 @@ ...@@ -285,6 +293,9 @@
<if test="record.language != null"> <if test="record.language != null">
`language` = #{record.language,jdbcType=VARCHAR}, `language` = #{record.language,jdbcType=VARCHAR},
</if> </if>
<if test="record.from != null">
`from` = #{record.from,jdbcType=INTEGER},
</if>
</set> </set>
<if test="_parameter != null"> <if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" /> <include refid="Update_By_Example_Where_Clause" />
...@@ -307,7 +318,8 @@ ...@@ -307,7 +318,8 @@
pwd_reset_time = #{record.pwdResetTime,jdbcType=BIGINT}, pwd_reset_time = #{record.pwdResetTime,jdbcType=BIGINT},
create_time = #{record.createTime,jdbcType=BIGINT}, create_time = #{record.createTime,jdbcType=BIGINT},
update_time = #{record.updateTime,jdbcType=BIGINT}, update_time = #{record.updateTime,jdbcType=BIGINT},
`language` = #{record.language,jdbcType=VARCHAR} `language` = #{record.language,jdbcType=VARCHAR},
`from` = #{record.from,jdbcType=INTEGER}
<if test="_parameter != null"> <if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" /> <include refid="Update_By_Example_Where_Clause" />
</if> </if>
...@@ -360,6 +372,9 @@ ...@@ -360,6 +372,9 @@
<if test="language != null"> <if test="language != null">
`language` = #{language,jdbcType=VARCHAR}, `language` = #{language,jdbcType=VARCHAR},
</if> </if>
<if test="from != null">
`from` = #{from,jdbcType=INTEGER},
</if>
</set> </set>
where user_id = #{userId,jdbcType=BIGINT} where user_id = #{userId,jdbcType=BIGINT}
</update> </update>
...@@ -379,7 +394,8 @@ ...@@ -379,7 +394,8 @@
pwd_reset_time = #{pwdResetTime,jdbcType=BIGINT}, pwd_reset_time = #{pwdResetTime,jdbcType=BIGINT},
create_time = #{createTime,jdbcType=BIGINT}, create_time = #{createTime,jdbcType=BIGINT},
update_time = #{updateTime,jdbcType=BIGINT}, update_time = #{updateTime,jdbcType=BIGINT},
`language` = #{language,jdbcType=VARCHAR} `language` = #{language,jdbcType=VARCHAR},
`from` = #{from,jdbcType=INTEGER}
where user_id = #{userId,jdbcType=BIGINT} where user_id = #{userId,jdbcType=BIGINT}
</update> </update>
</mapper> </mapper>
\ No newline at end of file
...@@ -7,4 +7,6 @@ import java.util.List; ...@@ -7,4 +7,6 @@ import java.util.List;
public interface ExtSysUserMapper { public interface ExtSysUserMapper {
List<SysUserGridResponse> query(GridExample example); List<SysUserGridResponse> query(GridExample example);
List<String> ldapUserNames(Integer from);
} }
...@@ -62,4 +62,8 @@ ...@@ -62,4 +62,8 @@
left join sys_role r on r.role_id = sur.role_id left join sys_role r on r.role_id = sur.role_id
where sur.user_id = #{user_id} where sur.user_id = #{user_id}
</select> </select>
<select id="ldapUserNames" resultType="java.lang.String" parameterType="java.lang.Integer">
select username from sys_user u where u.from = #{from}
</select>
</mapper> </mapper>
...@@ -10,6 +10,7 @@ import io.dataease.commons.utils.AuthUtils; ...@@ -10,6 +10,7 @@ import io.dataease.commons.utils.AuthUtils;
import io.dataease.commons.utils.PageUtils; import io.dataease.commons.utils.PageUtils;
import io.dataease.commons.utils.Pager; import io.dataease.commons.utils.Pager;
import io.dataease.controller.sys.base.BaseGridRequest; import io.dataease.controller.sys.base.BaseGridRequest;
import io.dataease.controller.sys.request.LdapAddRequest;
import io.dataease.controller.sys.request.SysUserCreateRequest; import io.dataease.controller.sys.request.SysUserCreateRequest;
import io.dataease.controller.sys.request.SysUserPwdRequest; import io.dataease.controller.sys.request.SysUserPwdRequest;
import io.dataease.controller.sys.request.SysUserStateRequest; import io.dataease.controller.sys.request.SysUserStateRequest;
...@@ -127,4 +128,18 @@ public class SysUserController { ...@@ -127,4 +128,18 @@ public class SysUserController {
Pager<List<SysRole>> listPager = PageUtils.setPageInfo(page, sysRoleService.query(request)); Pager<List<SysRole>> listPager = PageUtils.setPageInfo(page, sysRoleService.query(request));
return listPager; return listPager;
} }
@ApiOperation("同步用户")
@PostMapping("/sync")
public void importLdap(@RequestBody LdapAddRequest request) {
sysUserService.saveLdapUsers(request);
}
@ApiOperation("已同步用户")
@PostMapping("/existLdapUsers")
public List<String> getExistLdapUsers() {
return sysUserService.ldapUserNames();
}
} }
package io.dataease.controller.sys.request;
import io.dataease.plugins.common.entity.XpackLdapUserEntity;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Data
public class LdapAddRequest implements Serializable {
private Long deptId;
private List<Long> roleIds;
private Long enabled;
private List<XpackLdapUserEntity> users;
}
package io.dataease.plugins.server;
import io.dataease.plugins.common.entity.XpackLdapUserEntity;
import io.dataease.plugins.config.SpringContextUtil;
import io.dataease.plugins.xpack.display.dto.response.SysSettingDto;
import io.dataease.plugins.xpack.ldap.dto.response.LdapInfo;
import io.dataease.plugins.xpack.ldap.service.LdapXpackService;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RequestMapping("/api/ldap")
@RestController
public class XLdapServer {
@GetMapping("/info")
public LdapInfo getLdapInfo() {
LdapXpackService ldapXpackService = SpringContextUtil.getBean(LdapXpackService.class);
return ldapXpackService.info();
}
@PostMapping("/save")
public void save(@RequestBody List<SysSettingDto> settings) {
LdapXpackService ldapXpackService = SpringContextUtil.getBean(LdapXpackService.class);
ldapXpackService.save(settings);
}
@PostMapping("/testConn")
public void testConn() {
LdapXpackService ldapXpackService = SpringContextUtil.getBean(LdapXpackService.class);
ldapXpackService.testConn();
}
@PostMapping("/users")
public List<XpackLdapUserEntity> users() {
LdapXpackService ldapXpackService = SpringContextUtil.getBean(LdapXpackService.class);
return ldapXpackService.users();
}
}
...@@ -46,6 +46,16 @@ public class PluginUtils { ...@@ -46,6 +46,16 @@ public class PluginUtils {
return f2CLicenseResponse; return f2CLicenseResponse;
} }
public static Boolean licValid() {
try{
F2CLicenseResponse f2CLicenseResponse = PluginUtils.currentLic();
if (f2CLicenseResponse.getStatus() != F2CLicenseResponse.Status.valid) return false;
}catch (Exception e) {
return false;
}
return true;
}
......
...@@ -14,12 +14,14 @@ import io.dataease.commons.utils.AuthUtils; ...@@ -14,12 +14,14 @@ import io.dataease.commons.utils.AuthUtils;
import io.dataease.commons.utils.BeanUtils; import io.dataease.commons.utils.BeanUtils;
import io.dataease.commons.utils.CodingUtil; import io.dataease.commons.utils.CodingUtil;
import io.dataease.controller.sys.base.BaseGridRequest; import io.dataease.controller.sys.base.BaseGridRequest;
import io.dataease.controller.sys.request.LdapAddRequest;
import io.dataease.controller.sys.request.SysUserCreateRequest; import io.dataease.controller.sys.request.SysUserCreateRequest;
import io.dataease.controller.sys.request.SysUserPwdRequest; import io.dataease.controller.sys.request.SysUserPwdRequest;
import io.dataease.controller.sys.request.SysUserStateRequest; import io.dataease.controller.sys.request.SysUserStateRequest;
import io.dataease.controller.sys.response.SysUserGridResponse; import io.dataease.controller.sys.response.SysUserGridResponse;
import io.dataease.controller.sys.response.SysUserRole; import io.dataease.controller.sys.response.SysUserRole;
import io.dataease.i18n.Translator; import io.dataease.i18n.Translator;
import io.dataease.plugins.common.entity.XpackLdapUserEntity;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
...@@ -85,6 +87,7 @@ public class SysUserService { ...@@ -85,6 +87,7 @@ public class SysUserService {
user.setCreateTime(now); user.setCreateTime(now);
user.setUpdateTime(now); user.setUpdateTime(now);
user.setIsAdmin(false); user.setIsAdmin(false);
user.setFrom(0);
if (ObjectUtils.isEmpty(user.getPassword()) || StringUtils.equals(user.getPassword(), DEFAULT_PWD)) { if (ObjectUtils.isEmpty(user.getPassword()) || StringUtils.equals(user.getPassword(), DEFAULT_PWD)) {
user.setPassword(CodingUtil.md5(DEFAULT_PWD)); user.setPassword(CodingUtil.md5(DEFAULT_PWD));
} else { } else {
...@@ -99,6 +102,39 @@ public class SysUserService { ...@@ -99,6 +102,39 @@ public class SysUserService {
return insert; return insert;
} }
@Transactional
public void saveLdapUsers(LdapAddRequest request) {
long now = System.currentTimeMillis();
List<XpackLdapUserEntity> users = request.getUsers();
List<SysUser> sysUsers = users.stream().map(user -> {
SysUser sysUser = BeanUtils.copyBean(new SysUser(), user);
sysUser.setUsername(user.getUserName());
sysUser.setDeptId(request.getDeptId());
sysUser.setPassword(CodingUtil.md5(DEFAULT_PWD));
sysUser.setCreateTime(now);
sysUser.setUpdateTime(now);
sysUser.setEnabled(request.getEnabled());
sysUser.setFrom(1);
return sysUser;
}).collect(Collectors.toList());
sysUsers.forEach(sysUser -> {
sysUserMapper.insert(sysUser);
SysUser dbUser = findOne(sysUser);
if (null != dbUser && null != dbUser.getUserId()) {
saveUserRoles( dbUser.getUserId(), request.getRoleIds());
}
});
}
public List<String> ldapUserNames() {
List<String> usernames = extSysUserMapper.ldapUserNames(1);
return usernames;
}
/** /**
* 修改用户密码清楚缓存 * 修改用户密码清楚缓存
* *
......
...@@ -64,7 +64,9 @@ ...@@ -64,7 +64,9 @@
<!--要生成的数据库表 --> <!--要生成的数据库表 -->
<table tableName="chart_view"/> <table tableName="chart_view"/>
<!-- <table tableName="sys_dict_item"/>--> <!-- <table tableName="sys_dict_item"/>-->
<!-- <table tableName="dataset_table_field"/>--> <!-- <table tableName="dataset_table_field"/>-->
<!-- <table tableName="v_chart">--> <!-- <table tableName="v_chart">-->
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
<parent> <parent>
<artifactId>dataease-server</artifactId> <artifactId>dataease-server</artifactId>
<groupId>io.dataease</groupId> <groupId>io.dataease</groupId>
<version>1.2.0</version> <version>1.3.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
......
...@@ -98,4 +98,29 @@ export function roleGrid(pageIndex, pageSize, data) { ...@@ -98,4 +98,29 @@ export function roleGrid(pageIndex, pageSize, data) {
}) })
} }
export default { editPassword, delUser, editUser, addUser, userLists, editStatus, persionInfo, updatePerson, updatePersonPwd, allRoles, roleGrid } export function ldapUsers(data) {
return request({
url: '/api/ldap/users',
method: 'post',
loading: true
})
}
export function saveLdapUser(data) {
return request({
url: '/api/user/sync',
method: 'post',
loading: true,
data
})
}
export function existLdapUsers() {
return request({
url: '/api/user/existLdapUsers',
method: 'post',
loading: false
})
}
export default { editPassword, delUser, editUser, addUser, userLists, editStatus, persionInfo, updatePerson, updatePersonPwd, allRoles, roleGrid, ldapUsers, saveLdapUser, existLdapUsers }
...@@ -50,3 +50,10 @@ export function languageApi(language) { ...@@ -50,3 +50,10 @@ export function languageApi(language) {
method: 'post' method: 'post'
}) })
} }
export function ldapStatus() {
return request({
url: '/api/auth/isOpenLdap',
method: 'post'
})
}
...@@ -482,6 +482,7 @@ export default { ...@@ -482,6 +482,7 @@ export default {
input_password: '请输入密码', input_password: '请输入密码',
input_phone: '请输入电话号码', input_phone: '请输入电话号码',
input_roles: '请选择角色', input_roles: '请选择角色',
select_users: '请选择用户',
special_characters_are_not_supported: '不支持特殊字符', special_characters_are_not_supported: '不支持特殊字符',
mobile_number_format_is_incorrect: '手机号码格式不正确', mobile_number_format_is_incorrect: '手机号码格式不正确',
email_format_is_incorrect: '邮箱格式不正确', email_format_is_incorrect: '邮箱格式不正确',
...@@ -496,7 +497,38 @@ export default { ...@@ -496,7 +497,38 @@ export default {
new_passwd: '新密码', new_passwd: '新密码',
confirm_passwd: '确认密码', confirm_passwd: '确认密码',
change_password: '修改密码', change_password: '修改密码',
search_by_name: '按姓名搜索' search_by_name: '按姓名搜索',
import_ldap: '导入LDAP用户'
},
ldap: {
url: 'LDAP地址',
dn: '绑定DN',
password: '密码',
ou: '用户OU',
filter: '用户过滤器',
mapping: 'LDAP属性映射',
open: '启用LDAP认证',
input_url: '请输入LDAP地址',
input_dn: '请输入DN',
input_password: '请输入密码',
input_ou: '请输入用户OU',
input_filter: '请输入用户过滤器',
input_mapping: '请输入LDAP属性映射',
input_username: '请输入用户名',
input_url_placeholder: '请输入LDAP地址 (如 ldap://localhost:389)',
input_ou_placeholder: '输入用户OU (使用|分隔各OU)',
input_filter_placeholder: '输入过滤器 [可能的选项是cn或uid或sAMAccountName={0}, 如:(uid={0})]',
input_mapping_placeholder: '如:{"username":"uid","name":"sn","email":"mail"}, username映射的选项可能是cn或uid或sAMAccountName',
test_connect: '测试连接',
test_login: '测试登录',
edit: '编辑',
login_success: '登录成功',
url_cannot_be_empty: 'LDAP 地址不能为空',
dn_cannot_be_empty: 'LDAP DN不能为空',
ou_cannot_be_empty: 'LDAP OU不能为空',
filter_cannot_be_empty: 'LDAP 用户过滤器不能为空',
mapping_cannot_be_empty: 'LDAP 用户属性映射不能为空',
password_cannot_be_empty: 'LDAP 密码不能为空'
}, },
role: { role: {
menu_authorization: '菜单授权', menu_authorization: '菜单授权',
......
...@@ -71,9 +71,9 @@ const mutations = { ...@@ -71,9 +71,9 @@ const mutations = {
const actions = { const actions = {
// user login // user login
login({ commit }, userInfo) { login({ commit }, userInfo) {
const { username, password } = userInfo const { username, password, loginType } = userInfo
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
login({ username: username.trim(), password: password }).then(response => { login({ username: username.trim(), password: password, loginType: loginType }).then(response => {
const { data } = response const { data } = response
commit('SET_TOKEN', data.token) commit('SET_TOKEN', data.token)
commit('SET_LOGIN_MSG', null) commit('SET_LOGIN_MSG', null)
......
...@@ -15,6 +15,13 @@ ...@@ -15,6 +15,13 @@
{{ $t('login.welcome') + (uiInfo && uiInfo['ui.title'] && uiInfo['ui.title'].paramValue || ' DataEase') }} {{ $t('login.welcome') + (uiInfo && uiInfo['ui.title'] && uiInfo['ui.title'].paramValue || ' DataEase') }}
</div> </div>
<div class="login-form"> <div class="login-form">
<el-form-item>
<el-radio-group v-model="loginForm.loginType">
<el-radio v-if="openLdap" :label="0" size="mini">普通登录</el-radio>
<el-radio v-if="openLdap" :label="1" size="mini">LDAP</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item prop="username"> <el-form-item prop="username">
<el-input v-model="loginForm.username" placeholder="ID" autofocus /> <el-input v-model="loginForm.username" placeholder="ID" autofocus />
</el-form-item> </el-form-item>
...@@ -55,7 +62,7 @@ ...@@ -55,7 +62,7 @@
<script> <script>
import { encrypt } from '@/utils/rsaEncrypt' import { encrypt } from '@/utils/rsaEncrypt'
// import { validateUserName } from '@/api/user' import { ldapStatus } from '@/api/user'
import { getSysUI } from '@/utils/auth' import { getSysUI } from '@/utils/auth'
export default { export default {
name: 'Login', name: 'Login',
...@@ -86,6 +93,7 @@ export default { ...@@ -86,6 +93,7 @@ export default {
// } // }
return { return {
loginForm: { loginForm: {
loginType: 0,
username: '', username: '',
password: '' password: ''
}, },
...@@ -99,7 +107,8 @@ export default { ...@@ -99,7 +107,8 @@ export default {
uiInfo: null, uiInfo: null,
loginImageUrl: null, loginImageUrl: null,
loginLogoUrl: null, loginLogoUrl: null,
axiosFinished: false axiosFinished: false,
openLdap: true
} }
}, },
computed: { computed: {
...@@ -115,6 +124,11 @@ export default { ...@@ -115,6 +124,11 @@ export default {
immediate: true immediate: true
} }
}, },
beforeCreate() {
ldapStatus().then(res => {
this.openLdap = res.success && res.data
})
},
created() { created() {
this.$store.dispatch('user/getUI').then(() => { this.$store.dispatch('user/getUI').then(() => {
// const uiLists = this.$store.state.user.uiInfo // const uiLists = this.$store.state.user.uiInfo
...@@ -141,7 +155,8 @@ export default { ...@@ -141,7 +155,8 @@ export default {
this.loading = true this.loading = true
const user = { const user = {
username: this.loginForm.username, username: this.loginForm.username,
password: this.loginForm.password password: this.loginForm.password,
loginType: this.loginForm.loginType
} }
user.password = encrypt(user.password) user.password = encrypt(user.password)
this.$store.dispatch('user/login', user).then(() => { this.$store.dispatch('user/login', user).then(() => {
......
<template>
<router-view />
</template>
<script>
export default ({
data() {
return {
}
},
created() {
this.$store.dispatch('app/toggleSideBarHide', false)
},
method: {
}
})
</script>
<template>
<h2>this is sso page</h2>
</template>
<script>
export default ({
data() {
return {
}
},
created() {
this.$store.dispatch('app/toggleSideBarHide', false)
},
method: {
}
})
</script>
<template>
<h2>
this is display settings page
</h2>
</template>
<script>
export default {
}
</script>
<template>
<layout-content :header="$t('user.import_ldap') " back-name="system-user">
<el-form ref="importUserForm" :model="form" :rules="rule" size="small" label-width="auto" label-position="right">
<el-form-item :label="$t('commons.user')" prop="userIds">
<el-select
ref="userSelect"
v-model="form.userIds"
filterable
style="width: 100%"
multiple
:placeholder="$t('commons.please_select')"
@change="changeUser"
>
<el-option
v-for="item in users"
:key="item.userName"
:disabled="item.disabled"
:label="item.nickName"
:value="item.userName"
>
<span>{{ item.nickName + (item.disabled ? '(已存在)':'') }}</span>
<!-- <span><el-checkbox v-model="item.checked">{{ item.nickName }}</el-checkbox></span> -->
</el-option>
</el-select>
</el-form-item>
<el-form-item :label="$t('commons.organization')" prop="deptId">
<treeselect
ref="deptTreeSelect"
v-model="form.deptId"
:options="depts"
:load-options="loadDepts"
:auto-load-root-options="false"
:placeholder="$t('user.choose_org')"
@open="filterData"
/>
</el-form-item>
<el-form-item :label="$t('commons.role')" prop="roleIds">
<el-select
ref="roleSelect"
v-model="form.roleIds"
filterable
style="width: 100%"
multiple
:placeholder="$t('commons.please_select')"
@remove-tag="deleteTag"
@change="changeRole"
>
<el-option
v-for="item in roles"
:key="item.name"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item :label="$t('commons.status')" prop="enabled">
<el-radio-group v-model="form.enabled" style="width: 140px">
<el-radio :label="1">{{ $t('commons.enable') }}</el-radio>
<el-radio :label="0">{{ $t('commons.disable') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="save">{{ $t('commons.confirm') }}</el-button>
<el-button @click="cancel">{{ $t('commons.cancel') }}</el-button>
</el-form-item>
</el-form>
</layout-content>
</template>
<script>
import LayoutContent from '@/components/business/LayoutContent'
import { getDeptTree, treeByDeptId } from '@/api/system/dept'
import { allRoles, ldapUsers, saveLdapUser, existLdapUsers } from '@/api/system/user'
export default {
components: { LayoutContent },
data() {
return {
form: {
roles: [{
id: ''
}]
},
rule: {
userIds: [{ required: true, message: this.$t('user.select_users'), trigger: 'change' }],
roleIds: [{ required: true, message: this.$t('user.input_roles'), trigger: 'change' }],
deptId: [],
enable: []
},
defaultForm: { deptId: null, enabled: 1, roleIds: [2] },
depts: null,
roles: [],
roleDatas: [],
userRoles: [],
users: [],
exitsUsers: []
}
},
created() {
this.initRoles()
this.remoteMethod()
this.getExistUsers()
this.create()
},
mounted() {
this.bindKey()
},
destroyed() {
this.unBindKey()
},
methods: {
entryKey(event) {
const keyCode = event.keyCode
if (keyCode === 13) {
this.save()
}
},
bindKey() {
document.addEventListener('keypress', this.entryKey)
},
unBindKey() {
document.removeEventListener('keypress', this.entryKey)
},
create() {
this.depts = null
this.form = Object.assign({}, this.defaultForm)
},
initRoles() {
allRoles().then(res => {
this.roles = res.data
})
},
// 获取弹窗内部门数据
loadDepts({ action, parentNode, callback }) {
if (action === 'LOAD_ROOT_OPTIONS' && !this.form.deptId) {
const _self = this
treeByDeptId(0).then(res => {
const results = res.data.map(node => {
if (node.hasChildren && !node.children) {
node.children = null
}
return node
})
_self.depts = results
callback()
})
}
if (action === 'LOAD_CHILDREN_OPTIONS') {
const _self = this
getDeptTree(parentNode.id).then(res => {
parentNode.children = res.data.map(function(obj) {
return _self.normalizer(obj)
})
callback()
})
}
},
normalizer(node) {
if (node.hasChildren) {
node.children = null
}
return {
id: node.deptId,
label: node.name,
children: node.children
}
},
deleteTag(value) {
this.userRoles.forEach(function(data, index) {
if (data.id === value) {
this.userRoles.splice(index, value)
}
}.bind(this))
},
changeRole(value) {
this.userRoles = []
value.forEach(function(data, index) {
const role = { id: data }
this.userRoles.push(role)
}.bind(this))
},
cancel() {
this.$refs.importUserForm.resetFields()
},
save() {
this.$refs.importUserForm.validate(valid => {
if (valid) {
const checkedUsers = this.users.filter(user => user.checked)
const param = {
users: checkedUsers,
deptId: this.form.deptId,
roleIds: this.form.roleIds,
enabled: this.form.enabled
}
const method = saveLdapUser
method(param).then(res => {
this.$success(this.$t('commons.save_success'))
this.backToList()
})
} else {
return false
}
})
},
backToList() {
this.$router.push({ name: 'system-user' })
},
filterData(instanceId) {
this.$refs.roleSelect && this.$refs.roleSelect.blur && this.$refs.roleSelect.blur()
if (!this.depts) {
return
}
const results = this.depts.map(node => {
if (node.hasChildren) {
node.children = null
}
return node
})
this.depts = results
},
remoteMethod() {
this.users = []
existLdapUsers().then(resout => {
this.exitsUsers = resout.data
ldapUsers().then(res => {
if (res && res.data) {
this.users = res.data.map(item => {
if (this.exitsUsers.some(existUser => existUser === item.userName)) {
item.disabled = true
}
return item
})
}
})
})
},
changeUser(values) {
this.users.forEach(user => {
user.checked = false
if (values.includes(user.userName)) {
user.checked = true
}
})
},
getExistUsers() {
/* existLdapUsers().then(res => {
this.exitsUsers = res.data
}) */
}
}
}
</script>
...@@ -12,11 +12,19 @@ ...@@ -12,11 +12,19 @@
> >
<template #toolbar> <template #toolbar>
<el-button v-permission="['user:add']" icon="el-icon-circle-plus-outline" @click="create">{{ $t('user.create') }}</el-button> <el-button v-permission="['user:add']" icon="el-icon-circle-plus-outline" @click="create">{{ $t('user.create') }}</el-button>
<!-- <el-button v-permission="['user:import']" icon="el-icon-download" @click="importLdap">{{ $t('user.import_ldap') }}</el-button> -->
<el-button v-if="openLdap" v-permission="['user:import']" icon="el-icon-download" @click="importLdap">{{ $t('user.import_ldap') }}</el-button>
</template> </template>
<el-table-column prop="username" label="ID" /> <el-table-column prop="username" label="ID" />
<el-table-column :show-overflow-tooltip="true" prop="nickName" sortable="custom" :label="$t('commons.nick_name')" /> <el-table-column :show-overflow-tooltip="true" prop="nickName" sortable="custom" :label="$t('commons.nick_name')" />
<el-table-column prop="gender" :label="$t('commons.gender')" width="60" /> <!-- <el-table-column prop="gender" :label="$t('commons.gender')" width="60" /> -->
<el-table-column prop="from" :label="$t('user.source')" width="80">
<template slot-scope="scope">
<div>{{ scope.row.from === 0 ? 'LOCAL' : 'LDAP' }}</div>
</template>
</el-table-column>
<el-table-column :show-overflow-tooltip="true" prop="email" :label="$t('commons.email')" /> <el-table-column :show-overflow-tooltip="true" prop="email" :label="$t('commons.email')" />
<el-table-column :show-overflow-tooltip="true" prop="dept" sortable="custom" :label="$t('commons.organization')"> <el-table-column :show-overflow-tooltip="true" prop="dept" sortable="custom" :label="$t('commons.organization')">
...@@ -166,7 +174,7 @@ import { PHONE_REGEX } from '@/utils/validate' ...@@ -166,7 +174,7 @@ import { PHONE_REGEX } from '@/utils/validate'
import { LOAD_CHILDREN_OPTIONS, LOAD_ROOT_OPTIONS } from '@riophae/vue-treeselect' import { LOAD_CHILDREN_OPTIONS, LOAD_ROOT_OPTIONS } from '@riophae/vue-treeselect'
import Treeselect from '@riophae/vue-treeselect' import Treeselect from '@riophae/vue-treeselect'
import '@riophae/vue-treeselect/dist/vue-treeselect.css' import '@riophae/vue-treeselect/dist/vue-treeselect.css'
import { ldapStatus } from '@/api/user'
import { userLists, addUser, editUser, delUser, editPassword, editStatus, allRoles } from '@/api/system/user' import { userLists, addUser, editUser, delUser, editPassword, editStatus, allRoles } from '@/api/system/user'
import { getDeptTree, treeByDeptId } from '@/api/system/dept' import { getDeptTree, treeByDeptId } from '@/api/system/dept'
...@@ -297,14 +305,19 @@ export default { ...@@ -297,14 +305,19 @@ export default {
editPwd: ['user:editPwd'] editPwd: ['user:editPwd']
}, },
orderConditions: [], orderConditions: [],
last_condition: null last_condition: null,
openLdap: false
} }
}, },
mounted() { mounted() {
this.allRoles() this.allRoles()
this.search() this.search()
}, },
beforeCreate() {
ldapStatus().then(res => {
this.openLdap = res.success && res.data
})
},
methods: { methods: {
sortChange({ column, prop, order }) { sortChange({ column, prop, order }) {
this.orderConditions = [] this.orderConditions = []
...@@ -479,6 +492,9 @@ export default { ...@@ -479,6 +492,9 @@ export default {
}, },
btnDisabled(row) { btnDisabled(row) {
return row.userId === 1 return row.userId === 1
},
importLdap() {
this.$router.push({ name: 'system-user-import' })
} }
} }
} }
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>io.dataease</groupId> <groupId>io.dataease</groupId>
<artifactId>dataease-server</artifactId> <artifactId>dataease-server</artifactId>
<version>1.2.0</version> <version>1.3.0</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<parent> <parent>
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论