diff --git a/backend/src/main/java/io/dataease/auth/entity/AccountLockStatus.java b/backend/src/main/java/io/dataease/auth/entity/AccountLockStatus.java new file mode 100644 index 0000000000..fbd62da94a --- /dev/null +++ b/backend/src/main/java/io/dataease/auth/entity/AccountLockStatus.java @@ -0,0 +1,13 @@ +package io.dataease.auth.entity; + +import lombok.Data; + +@Data +public class AccountLockStatus { + + private Boolean locked = false; + + private String username; + + private Long unlockTime; +} diff --git a/backend/src/main/java/io/dataease/auth/server/AuthServer.java b/backend/src/main/java/io/dataease/auth/server/AuthServer.java index 046015a14f..b367411469 100644 --- a/backend/src/main/java/io/dataease/auth/server/AuthServer.java +++ b/backend/src/main/java/io/dataease/auth/server/AuthServer.java @@ -5,6 +5,7 @@ import io.dataease.auth.api.dto.CurrentRoleDto; import io.dataease.auth.api.dto.CurrentUserDto; import io.dataease.auth.api.dto.LoginDto; import io.dataease.auth.config.RsaProperties; +import io.dataease.auth.entity.AccountLockStatus; import io.dataease.auth.entity.SysUserEntity; import io.dataease.auth.entity.TokenInfo; import io.dataease.auth.service.AuthUserService; @@ -67,10 +68,18 @@ public class AuthServer implements AuthApi { Integer loginType = loginDto.getLoginType(); boolean isSupportLdap = authUserService.supportLdap(); if (loginType == 1 && isSupportLdap) { + AccountLockStatus accountLockStatus = authUserService.lockStatus(username, 1); + if (accountLockStatus.getLocked()) { + String msg = Translator.get("I18N_ACCOUNT_LOCKED"); + msg = String.format(msg, username); + DataEaseException.throwException(msg); + } LdapXpackService ldapXpackService = SpringContextUtil.getBean(LdapXpackService.class); LdapValidateRequest request = LdapValidateRequest.builder().userName(username).password(pwd).build(); ValidateResult validateResult = ldapXpackService.login(request); + if (!validateResult.isSuccess()) { + authUserService.recordLoginFail(username, 1); DataEaseException.throwException(validateResult.getMsg()); } XpackLdapUserEntity ldapUserEntity = validateResult.getData(); @@ -96,20 +105,29 @@ public class AuthServer implements AuthApi { username = validateResult.getData().getUsername(); } // 增加ldap登录方式 + AccountLockStatus accountLockStatus = authUserService.lockStatus(username, 0); + if (accountLockStatus.getLocked()) { + String msg = Translator.get("I18N_ACCOUNT_LOCKED"); + msg = String.format(msg, username); + DataEaseException.throwException(msg); + } SysUserEntity user = authUserService.getUserByName(username); if (ObjectUtils.isEmpty(user)) { - DataEaseException.throwException(Translator.get("i18n_id_or_pwd_error")); + authUserService.recordLoginFail(username, 0); + DataEaseException.throwException(Translator.get("i18n_user_do_not_exist")); } // 验证登录类型是否与用户类型相同 if (!sysUserService.validateLoginType(user.getFrom(), loginType)) { - DataEaseException.throwException(Translator.get("i18n_id_or_pwd_error")); + authUserService.recordLoginFail(username, 0); + DataEaseException.throwException(Translator.get("i18n_login_type_error")); } if (user.getEnabled() == 0) { - DataEaseException.throwException(Translator.get("i18n_id_or_pwd_error")); + authUserService.recordLoginFail(username, 0); + DataEaseException.throwException(Translator.get("i18n_user_is_disable")); } String realPwd = user.getPassword(); @@ -121,6 +139,7 @@ public class AuthServer implements AuthApi { pwd = CodingUtil.md5(pwd); if (!StringUtils.equals(pwd, realPwd)) { + authUserService.recordLoginFail(username, 0); DataEaseException.throwException(Translator.get("i18n_id_or_pwd_error")); } } diff --git a/backend/src/main/java/io/dataease/auth/service/AuthUserService.java b/backend/src/main/java/io/dataease/auth/service/AuthUserService.java index d6a885b960..01eb0d0f72 100644 --- a/backend/src/main/java/io/dataease/auth/service/AuthUserService.java +++ b/backend/src/main/java/io/dataease/auth/service/AuthUserService.java @@ -1,6 +1,7 @@ package io.dataease.auth.service; import io.dataease.auth.api.dto.CurrentRoleDto; +import io.dataease.auth.entity.AccountLockStatus; import io.dataease.auth.entity.SysUserEntity; import java.util.List; @@ -38,9 +39,19 @@ public interface AuthUserService { Boolean supportLark(); + Boolean supportLoginLimit(); + Boolean pluginLoaded(); void checkAdmin(String uname, String pwd); + void recordLoginFail(String username, Integer logintype); + + void unlockAccount(String username, Integer logintype); + + AccountLockStatus lockStatus(String username, Integer logintype); + + void clearAllLock(); + } diff --git a/backend/src/main/java/io/dataease/auth/service/impl/AuthUserServiceImpl.java b/backend/src/main/java/io/dataease/auth/service/impl/AuthUserServiceImpl.java index 825dd7ff03..949a2465ca 100644 --- a/backend/src/main/java/io/dataease/auth/service/impl/AuthUserServiceImpl.java +++ b/backend/src/main/java/io/dataease/auth/service/impl/AuthUserServiceImpl.java @@ -1,6 +1,7 @@ package io.dataease.auth.service.impl; import io.dataease.auth.api.dto.CurrentRoleDto; +import io.dataease.auth.entity.AccountLockStatus; import io.dataease.auth.entity.SysUserEntity; import io.dataease.commons.utils.CodingUtil; import io.dataease.exception.DataEaseException; @@ -9,7 +10,10 @@ import io.dataease.auth.service.AuthUserService; import io.dataease.commons.constants.AuthConstants; import io.dataease.commons.utils.LogUtil; import io.dataease.i18n.Translator; +import io.dataease.plugins.common.base.domain.SysLoginLimit; +import io.dataease.plugins.common.base.domain.SysLoginLimitExample; import io.dataease.plugins.common.base.domain.SysUser; +import io.dataease.plugins.common.base.mapper.SysLoginLimitMapper; import io.dataease.plugins.common.base.mapper.SysUserMapper; import io.dataease.plugins.common.service.PluginCommonService; import io.dataease.plugins.config.SpringContextUtil; @@ -18,9 +22,12 @@ import io.dataease.plugins.xpack.cas.service.CasXpackService; import io.dataease.plugins.xpack.dingtalk.service.DingtalkXpackService; import io.dataease.plugins.xpack.lark.service.LarkXpackService; import io.dataease.plugins.xpack.ldap.service.LdapXpackService; +import io.dataease.plugins.xpack.loginlimit.dto.response.LoginLimitInfo; +import io.dataease.plugins.xpack.loginlimit.service.LoginLimitXpackService; import io.dataease.plugins.xpack.oidc.service.OidcXpackService; import io.dataease.plugins.xpack.wecom.service.WecomXpackService; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.cache.annotation.CacheEvict; @@ -46,6 +53,9 @@ public class AuthUserServiceImpl implements AuthUserService { @Resource private DynamicMenuServiceImpl dynamicMenuService; + @Resource + private SysLoginLimitMapper sysLoginLimitMapper; + /** * 此处需被F2CRealm登录认证调用 也就是说每次请求都会被调用 所以最好加上缓存 * @@ -196,6 +206,15 @@ public class AuthUserServiceImpl implements AuthUserService { return larkXpackService.isOpen(); } + @Override + public Boolean supportLoginLimit() { + Map beansOfType = SpringContextUtil.getApplicationContext().getBeansOfType((LoginLimitXpackService.class)); + if (beansOfType.keySet().size() == 0) return false; + LoginLimitXpackService loginLimitXpackService = SpringContextUtil.getBean(LoginLimitXpackService.class); + if (ObjectUtils.isEmpty(loginLimitXpackService)) return false; + return loginLimitXpackService.isOpen(); + } + @Override public Boolean pluginLoaded() { Map beansOfType = SpringContextUtil.getApplicationContext().getBeansOfType((PluginCommonService.class)); @@ -221,4 +240,61 @@ public class AuthUserServiceImpl implements AuthUserService { DataEaseException.throwException(Translator.get("i18n_id_or_pwd_error")); } } + + @Override + public void recordLoginFail(String username, Integer logintype) { + if (!supportLoginLimit()) return; + long now = System.currentTimeMillis(); + SysLoginLimit sysLoginLimit = new SysLoginLimit(); + sysLoginLimit.setUsername(username); + sysLoginLimit.setLoginType(logintype); + sysLoginLimit.setRecordTime(now); + sysLoginLimitMapper.insert(sysLoginLimit); + } + + @Override + public void unlockAccount(String username, Integer logintype) { + SysLoginLimitExample example = new SysLoginLimitExample(); + example.createCriteria().andUsernameEqualTo(username).andLoginTypeEqualTo(logintype); + sysLoginLimitMapper.deleteByExample(example); + } + + @Override + public AccountLockStatus lockStatus(String username, Integer logintype) { + AccountLockStatus accountLockStatus = new AccountLockStatus(); + accountLockStatus.setUsername(username); + if (!supportLoginLimit()) return accountLockStatus; + + LoginLimitXpackService loginLimitXpackService = SpringContextUtil.getBean(LoginLimitXpackService.class); + LoginLimitInfo info = loginLimitXpackService.info(); + Integer limitTimes = info.getLimitTimes(); + Integer relieveTimes = info.getRelieveTimes(); + + long now = System.currentTimeMillis(); + + long longRelieveTimes = Long.parseLong(relieveTimes.toString()); + long dividingPointTime = now - (longRelieveTimes * 60L * 1000L); + SysLoginLimitExample example = new SysLoginLimitExample(); + example.createCriteria().andUsernameEqualTo(username).andLoginTypeEqualTo(logintype).andRecordTimeGreaterThan(dividingPointTime); + List sysLoginLimits = sysLoginLimitMapper.selectByExample(example); + if (CollectionUtils.isNotEmpty(sysLoginLimits)) { + boolean needLock = sysLoginLimits.size() >= limitTimes; + accountLockStatus.setLocked(needLock); + if (needLock) { + long unlockTime = now + (longRelieveTimes * 60L * 1000L); + accountLockStatus.setUnlockTime(unlockTime); + } + + } + example.clear(); + example.createCriteria().andUsernameEqualTo(username).andLoginTypeEqualTo(logintype).andRecordTimeLessThanOrEqualTo(dividingPointTime); + sysLoginLimitMapper.deleteByExample(example); + return accountLockStatus; + } + + @Override + public void clearAllLock() { + SysLoginLimitExample example = new SysLoginLimitExample(); + sysLoginLimitMapper.deleteByExample(example); + } } diff --git a/backend/src/main/java/io/dataease/commons/constants/ParamConstants.java b/backend/src/main/java/io/dataease/commons/constants/ParamConstants.java index 6eef4b8d2c..858d0fa71f 100644 --- a/backend/src/main/java/io/dataease/commons/constants/ParamConstants.java +++ b/backend/src/main/java/io/dataease/commons/constants/ParamConstants.java @@ -117,6 +117,12 @@ public interface ParamConstants { OPEN_MARKET_PAGE("ui.openMarketPage"), TEMPLATE_MARKET_ULR("basic.templateMarketUlr"), + + LOGIN_LIMIT_LIMITTIMES("loginlimit.limitTimes"), + + LOGIN_LIMIT_RELIEVETIMES("loginlimit.relieveTimes"), + + LOGIN_LIMIT_OPEN("loginlimit.open"), TEMPLATE_ACCESS_KEY("basic.templateAccessKey"); private String value; diff --git a/backend/src/main/java/io/dataease/controller/datasource/DatasourceController.java b/backend/src/main/java/io/dataease/controller/datasource/DatasourceController.java index a8b5335cd0..5932e7bd14 100644 --- a/backend/src/main/java/io/dataease/controller/datasource/DatasourceController.java +++ b/backend/src/main/java/io/dataease/controller/datasource/DatasourceController.java @@ -3,22 +3,23 @@ package io.dataease.controller.datasource; import com.github.xiaoymin.knife4j.annotations.ApiSupport; import io.dataease.auth.annotation.DeLog; import io.dataease.auth.annotation.DePermission; -import io.dataease.commons.constants.SysLogConstants; -import io.dataease.commons.utils.DeLogUtils; -import io.dataease.dto.SysLogDTO; -import io.dataease.plugins.common.base.domain.Datasource; import io.dataease.commons.constants.DePermissionType; import io.dataease.commons.constants.ResourceAuthLevel; +import io.dataease.commons.constants.SysLogConstants; import io.dataease.commons.utils.AuthUtils; +import io.dataease.commons.utils.DeLogUtils; import io.dataease.controller.ResultHolder; import io.dataease.controller.datasource.request.UpdataDsRequest; import io.dataease.controller.request.DatasourceUnionRequest; import io.dataease.controller.request.datasource.ApiDefinition; -import io.dataease.dto.datasource.DBTableDTO; -import io.dataease.service.datasource.DatasourceService; import io.dataease.dto.DatasourceDTO; +import io.dataease.dto.SysLogDTO; +import io.dataease.dto.datasource.DBTableDTO; +import io.dataease.plugins.common.base.domain.Datasource; +import io.dataease.service.datasource.DatasourceService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.springframework.web.bind.annotation.*; @@ -43,19 +44,19 @@ public class DatasourceController { @ApiOperation("新增数据源") @PostMapping("/add") @DeLog( - operatetype = SysLogConstants.OPERATE_TYPE.CREATE, - sourcetype = SysLogConstants.SOURCE_TYPE.DATASOURCE, - positionIndex = 0,positionKey = "type", - value = "id" + operatetype = SysLogConstants.OPERATE_TYPE.CREATE, + sourcetype = SysLogConstants.SOURCE_TYPE.DATASOURCE, + positionIndex = 0, positionKey = "type", + value = "id" ) - public Datasource addDatasource(@RequestBody Datasource datasource) throws Exception{ + public Datasource addDatasource(@RequestBody Datasource datasource) throws Exception { return datasourceService.addDatasource(datasource); } @RequiresPermissions("datasource:read") @ApiOperation("数据源类型") @GetMapping("/types") - public Collection types() throws Exception{ + public Collection types() throws Exception { return datasourceService.types(); } @@ -81,6 +82,16 @@ public class DatasourceController { return datasourceService.getDatasourceList(request); } + @ApiOperation("查询数据源详情") + @PostMapping("/get/{id}") + public DatasourceDTO getDatasource(@PathVariable String id) throws Exception { + DatasourceUnionRequest request = new DatasourceUnionRequest(); + request.setUserId(String.valueOf(AuthUtils.getUser().getUserId())); + request.setId(id); + List datasourceList = datasourceService.getDatasourceList(request); + return CollectionUtils.isNotEmpty(datasourceList) ? datasourceList.get(0) : null; + } + @ApiOperation("查询当前用户数据源") @GetMapping("/list/{type}") public List getDatasourceListByType(@PathVariable String type) throws Exception { @@ -105,12 +116,12 @@ public class DatasourceController { @ApiOperation("更新数据源") @PostMapping("/update") @DeLog( - operatetype = SysLogConstants.OPERATE_TYPE.MODIFY, - sourcetype = SysLogConstants.SOURCE_TYPE.DATASOURCE, - positionIndex = 0,positionKey = "type", - value = "id" + operatetype = SysLogConstants.OPERATE_TYPE.MODIFY, + sourcetype = SysLogConstants.SOURCE_TYPE.DATASOURCE, + positionIndex = 0, positionKey = "type", + value = "id" ) - public void updateDatasource(@RequestBody UpdataDsRequest dsRequest) throws Exception{ + public void updateDatasource(@RequestBody UpdataDsRequest dsRequest) throws Exception { datasourceService.updateDatasource(dsRequest); } @@ -124,7 +135,7 @@ public class DatasourceController { @ApiIgnore @PostMapping("/getSchema") public List getSchema(@RequestBody Datasource datasource) throws Exception { - return datasourceService.getSchema(datasource); + return datasourceService.getSchema(datasource); } @ApiIgnore diff --git a/backend/src/main/java/io/dataease/controller/sys/SysUserController.java b/backend/src/main/java/io/dataease/controller/sys/SysUserController.java index 098834c39e..a03fad7d85 100644 --- a/backend/src/main/java/io/dataease/controller/sys/SysUserController.java +++ b/backend/src/main/java/io/dataease/controller/sys/SysUserController.java @@ -5,6 +5,8 @@ import com.github.pagehelper.PageHelper; import com.github.xiaoymin.knife4j.annotations.ApiSupport; import io.dataease.auth.annotation.DeLog; import io.dataease.auth.api.dto.CurrentUserDto; +import io.dataease.auth.entity.AccountLockStatus; +import io.dataease.auth.service.AuthUserService; import io.dataease.commons.constants.SysLogConstants; import io.dataease.commons.utils.BeanUtils; import io.dataease.controller.sys.request.KeyGridRequest; @@ -21,6 +23,7 @@ import io.dataease.controller.sys.request.SysUserPwdRequest; import io.dataease.controller.sys.request.SysUserStateRequest; import io.dataease.controller.sys.response.RoleUserItem; import io.dataease.controller.sys.response.SysUserGridResponse; +import io.dataease.plugins.common.base.domain.SysUser; import io.dataease.service.sys.SysRoleService; import io.dataease.service.sys.SysUserService; import io.swagger.annotations.Api; @@ -51,6 +54,9 @@ public class SysUserController { @Resource private SysRoleService sysRoleService; + @Resource + private AuthUserService authUserService; + @ApiOperation("查询用户") @RequiresPermissions("user:read") @PostMapping("/userGrid/{goPage}/{pageSize}") @@ -62,7 +68,12 @@ public class SysUserController { public Pager> userGrid(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody KeyGridRequest request) { Page page = PageHelper.startPage(goPage, pageSize, true); - return PageUtils.setPageInfo(page, sysUserService.query(request)); + List users = sysUserService.query(request); + users.forEach(user -> { + AccountLockStatus accountLockStatus = authUserService.lockStatus(user.getUsername(), user.getFrom()); + user.setLocked(accountLockStatus.getLocked()); + }); + return PageUtils.setPageInfo(page, users); } @ApiIgnore @@ -207,4 +218,12 @@ public class SysUserController { }).collect(Collectors.toList()); } + @PostMapping("/unlock/{username}") + public void unlock(@PathVariable("username") String username) { + SysUser sysUser = new SysUser(); + sysUser.setUsername(username); + SysUser one = sysUserService.findOne(sysUser); + authUserService.unlockAccount(username, one.getFrom()); + } + } diff --git a/backend/src/main/java/io/dataease/controller/sys/response/BasicInfo.java b/backend/src/main/java/io/dataease/controller/sys/response/BasicInfo.java index 7038a03406..40e49a008c 100644 --- a/backend/src/main/java/io/dataease/controller/sys/response/BasicInfo.java +++ b/backend/src/main/java/io/dataease/controller/sys/response/BasicInfo.java @@ -2,11 +2,12 @@ package io.dataease.controller.sys.response; import java.io.Serializable; +import io.dataease.plugins.xpack.loginlimit.dto.response.LoginLimitInfo; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @Data -public class BasicInfo implements Serializable { +public class BasicInfo extends LoginLimitInfo implements Serializable { @ApiModelProperty("请求超时时间") private String frontTimeOut; diff --git a/backend/src/main/java/io/dataease/controller/sys/response/SysUserGridResponse.java b/backend/src/main/java/io/dataease/controller/sys/response/SysUserGridResponse.java index a38a57d0f8..976069c2ef 100644 --- a/backend/src/main/java/io/dataease/controller/sys/response/SysUserGridResponse.java +++ b/backend/src/main/java/io/dataease/controller/sys/response/SysUserGridResponse.java @@ -16,5 +16,7 @@ public class SysUserGridResponse extends SysUser { private SysUserDept dept; @ApiModelProperty("角色ID集合") private List roleIds; + @ApiModelProperty("锁定") + private Boolean locked; } diff --git a/backend/src/main/java/io/dataease/ext/ExtDataSourceMapper.xml b/backend/src/main/java/io/dataease/ext/ExtDataSourceMapper.xml index 95b8f716f5..e7b7e1ca56 100644 --- a/backend/src/main/java/io/dataease/ext/ExtDataSourceMapper.xml +++ b/backend/src/main/java/io/dataease/ext/ExtDataSourceMapper.xml @@ -10,7 +10,7 @@ - select datasource.*, authInfo.privileges as `privileges` from (select GET_V_AUTH_MODEL_ID_P_USE (#{userId}, 'link') cids) t,datasource @@ -91,46 +91,43 @@ on datasource.id = authInfo.auth_source FIND_IN_SET(datasource.id,cids) - - id = #{id,jdbcType=VARCHAR} - - - `name` like concat('%',#{name,jdbcType=VARCHAR},'%') - - - `desc` = #{desc,jdbcType=VARCHAR} - - - `type` = #{type,jdbcType=VARCHAR} - - - create_time = #{createTime,jdbcType=BIGINT} - - - update_time = #{updateTime,jdbcType=BIGINT} - - - configuration = #{configuration,jdbcType=LONGVARCHAR} - + + and id = #{id,jdbcType=VARCHAR} + + + and `name` like concat('%',#{name,jdbcType=VARCHAR},'%') + + + and `desc` = #{desc,jdbcType=VARCHAR} + + + and `type` = #{type,jdbcType=VARCHAR} + + + and create_time = #{createTime,jdbcType=BIGINT} + + + and update_time = #{updateTime,jdbcType=BIGINT} + + + and configuration = #{configuration,jdbcType=LONGVARCHAR} + order by ${sort} - + SELECT DISTINCT datasource.id, + datasource.`name`, + datasource.DESC, + datasource.type + FROM chart_view + INNER JOIN panel_view ON chart_view.id = panel_view.chart_view_id + INNER JOIN dataset_table ON chart_view.table_id = dataset_table.id + INNER JOIN datasource ON dataset_table.data_source_id = datasource.id + WHERE panel_view.panel_id = #{panelId} diff --git a/backend/src/main/java/io/dataease/service/dataset/DataSetTableService.java b/backend/src/main/java/io/dataease/service/dataset/DataSetTableService.java index 690b746ae3..7835eada20 100644 --- a/backend/src/main/java/io/dataease/service/dataset/DataSetTableService.java +++ b/backend/src/main/java/io/dataease/service/dataset/DataSetTableService.java @@ -1111,6 +1111,7 @@ public class DataSetTableService { QueryProvider qp = ProviderFactory.getQueryProvider(ds.getType()); String sqlAsTable = qp.createSQLPreview(sql, null); datasourceRequest.setQuery(sqlAsTable); + datasourceRequest.setTable(dataTableInfo.getTable()); Map result = datasourceProvider.fetchResultAndField(datasourceRequest); List data = result.get("dataList"); List fields = result.get("fieldList"); diff --git a/backend/src/main/java/io/dataease/service/system/SystemParameterService.java b/backend/src/main/java/io/dataease/service/system/SystemParameterService.java index 0b70c4ccd1..e3ff651273 100644 --- a/backend/src/main/java/io/dataease/service/system/SystemParameterService.java +++ b/backend/src/main/java/io/dataease/service/system/SystemParameterService.java @@ -15,6 +15,7 @@ import io.dataease.plugins.config.SpringContextUtil; import io.dataease.plugins.xpack.cas.dto.CasSaveResult; import io.dataease.plugins.xpack.cas.service.CasXpackService; import io.dataease.plugins.xpack.display.service.DisplayXpackService; +import io.dataease.plugins.xpack.loginlimit.service.LoginLimitXpackService; import io.dataease.service.FileService; import io.dataease.service.datasource.DatasourceService; import org.apache.commons.collections.CollectionUtils; @@ -58,10 +59,14 @@ public class SystemParameterService { List paramList = this.getParamList("basic"); List homePageList = this.getParamList("ui.openHomePage"); List marketPageList = this.getParamList("ui.openMarketPage"); + List loginLimitList = this.getParamList("loginlimit"); paramList.addAll(homePageList); paramList.addAll(marketPageList); + paramList.addAll(loginLimitList); BasicInfo result = new BasicInfo(); result.setOpenHomePage("true"); + Map beansOfType = SpringContextUtil.getApplicationContext().getBeansOfType((LoginLimitXpackService.class)); + Boolean loginLimitPluginLoaded = beansOfType.keySet().size() > 0; if (!CollectionUtils.isEmpty(paramList)) { for (SystemParameter param : paramList) { if (StringUtils.equals(param.getParamKey(), ParamConstants.BASIC.FRONT_TIME_OUT.getValue())) { @@ -94,6 +99,27 @@ public class SystemParameterService { if (StringUtils.equals(param.getParamKey(), ParamConstants.BASIC.DS_CHECK_INTERVAL_TYPE.getValue())) { result.setDsCheckIntervalType(param.getParamValue()); } + + + if (loginLimitPluginLoaded) { + if (StringUtils.equals(param.getParamKey(), ParamConstants.BASIC.LOGIN_LIMIT_LIMITTIMES.getValue())) { + String paramValue = param.getParamValue(); + if (StringUtils.isNotBlank(paramValue)) { + result.setLimitTimes(Integer.parseInt(paramValue)); + } + } + if (StringUtils.equals(param.getParamKey(), ParamConstants.BASIC.LOGIN_LIMIT_RELIEVETIMES.getValue())) { + String paramValue = param.getParamValue(); + if (StringUtils.isNotBlank(paramValue)) { + result.setRelieveTimes(Integer.parseInt(paramValue)); + } + } + if (StringUtils.equals(param.getParamKey(), ParamConstants.BASIC.LOGIN_LIMIT_OPEN.getValue())) { + boolean open = StringUtils.equals("true", param.getParamValue()); + result.setOpen(open ? "true" : "false"); + } + } + } } return result; diff --git a/backend/src/main/resources/db/migration/V40__1.15.sql b/backend/src/main/resources/db/migration/V40__1.15.sql index 826007787b..0dbc5b1707 100644 --- a/backend/src/main/resources/db/migration/V40__1.15.sql +++ b/backend/src/main/resources/db/migration/V40__1.15.sql @@ -96,4 +96,23 @@ ALTER TABLE `sys_task_email` ALTER TABLE `sys_task_email` - ADD COLUMN `reci_users` varchar(255) NULL COMMENT '接收人账号' AFTER `conditions`; \ No newline at end of file + ADD COLUMN `reci_users` varchar(255) NULL COMMENT '接收人账号' AFTER `conditions`; + + + +DROP TABLE IF EXISTS `sys_login_limit`; +CREATE TABLE `sys_login_limit` ( + `login_type` int(8) NOT NULL, + `username` varchar(255) NOT NULL, + `record_time` bigint(13) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci; + +UPDATE `sys_menu` SET `pid` = 0, `sub_count` = 0, `type` = 1, `title` = '模板市场', `name` = 'template-market', `component` = 'panel/templateMarket/index', `menu_sort` = 5, `icon` = 'dashboard', `path` = '/templateMarket', `i_frame` = 0, `cache` = 0, `hidden` = 0, `permission` = 'template-market:read', `create_by` = NULL, `update_by` = NULL, `create_time` = NULL, `update_time` = 1620444227389 WHERE `menu_id` = 202; + +INSERT INTO `sys_auth` (`id`, `auth_source`, `auth_source_type`, `auth_target`, `auth_target_type`, `auth_time`, `auth_details`, `auth_user`, `update_time`, `copy_from`, `copy_id`) VALUES ('e6b2cebf-02d8-4d46-833c-56fb07febb0f', '202', 'menu', '2', 'user', 1663661210626, NULL, 'admin', NULL, NULL, NULL); +INSERT INTO `sys_auth` (`id`, `auth_source`, `auth_source_type`, `auth_target`, `auth_target_type`, `auth_time`, `auth_details`, `auth_user`, `update_time`, `copy_from`, `copy_id`) VALUES ('f4e07708-26f1-4f42-9a4a-8e6dae63353c', '202', 'menu', '2', 'role', 1663661388831, NULL, 'admin', NULL, NULL, NULL); +INSERT INTO `sys_auth_detail` (`id`, `auth_id`, `privilege_name`, `privilege_type`, `privilege_value`, `privilege_extend`, `remark`, `create_user`, `create_time`, `update_time`, `copy_from`, `copy_id`) VALUES ('2e9f56e5-38bb-11ed-8383-0242ac130005', 'e6b2cebf-02d8-4d46-833c-56fb07febb0f', 'i18n_auth_grant', 15, 0, 'grant', '基础权限-授权', 'admin', 1663661211000, NULL, NULL, NULL); +INSERT INTO `sys_auth_detail` (`id`, `auth_id`, `privilege_name`, `privilege_type`, `privilege_value`, `privilege_extend`, `remark`, `create_user`, `create_time`, `update_time`, `copy_from`, `copy_id`) VALUES ('2e9f5ca1-38bb-11ed-8383-0242ac130005', 'e6b2cebf-02d8-4d46-833c-56fb07febb0f', 'i18n_auth_use', 1, 0, 'use', '基础权限-使用', 'admin', 1663661211000, NULL, NULL, NULL); +INSERT INTO `sys_auth_detail` (`id`, `auth_id`, `privilege_name`, `privilege_type`, `privilege_value`, `privilege_extend`, `remark`, `create_user`, `create_time`, `update_time`, `copy_from`, `copy_id`) VALUES ('98d77463-38bb-11ed-8383-0242ac130005', 'f4e07708-26f1-4f42-9a4a-8e6dae63353c', 'i18n_auth_grant', 15, 0, 'grant', '基础权限-授权', 'admin', 1663661389000, NULL, NULL, NULL); +INSERT INTO `sys_auth_detail` (`id`, `auth_id`, `privilege_name`, `privilege_type`, `privilege_value`, `privilege_extend`, `remark`, `create_user`, `create_time`, `update_time`, `copy_from`, `copy_id`) VALUES ('98d77856-38bb-11ed-8383-0242ac130005', 'f4e07708-26f1-4f42-9a4a-8e6dae63353c', 'i18n_auth_use', 1, 1, 'use', '基础权限-使用', 'admin', 1663661389000, NULL, NULL, NULL); + diff --git a/backend/src/main/resources/i18n/messages_en_US.properties b/backend/src/main/resources/i18n/messages_en_US.properties index fe4db142b5..a096a7ff41 100644 --- a/backend/src/main/resources/i18n/messages_en_US.properties +++ b/backend/src/main/resources/i18n/messages_en_US.properties @@ -72,7 +72,11 @@ i18n_sql_not_empty=SQL can not be empty. i18n_datasource_not_allow_delete_msg= datasets are using this data source and cannot be deleted i18n_task_name_repeat=Name is used in same data set i18n_id_or_pwd_error=Invalid ID or password -i18n_datasource_delete=Data source is delete +i18n_user_do_not_exist=User do not exist +i18n_user_is_disable=User is disabled +i18n_login_type_error=Login type error +i18n_account_is_locked=Account is locked +i18n_datasource_delete=Data source is deleted i18n_dataset_delete=Data set is deleted i18n_dataset_no_permission=Data set no permission i18n_chart_delete=Chart is delete @@ -217,12 +221,17 @@ I18N_STATUS=Status I18N_DATA=Data I18N_SYNC_LOG=Sync log -I18N_USER_DONOT_EXIST=User do not exist +I18N_USER_DONOT_EXIST=User does not exist I18N_USER_SOURCE_PWD_ERROR=Source password error I18N_USER_PWD_FORMAT_ERROR=Password format error I18N_DS_INVALID=Datasource is invalid. I18N_DS_INVALID_TABLE=Datasource has invalid tables + + +I18N_ACCOUNT_LOCKED=Account\u3010%s\u3011is locked\uFF01 + I18N_PANEL_EXIST=The current panel name already exists under this directory I18N_DATASET_GROUP_EXIST=The current dataset grouping name already exists under this directory + diff --git a/backend/src/main/resources/i18n/messages_zh_CN.properties b/backend/src/main/resources/i18n/messages_zh_CN.properties index ca5ee13079..d0dc945e12 100644 --- a/backend/src/main/resources/i18n/messages_zh_CN.properties +++ b/backend/src/main/resources/i18n/messages_zh_CN.properties @@ -72,6 +72,10 @@ i18n_sql_not_empty=SQL \u4E0D\u80FD\u4E3A\u7A7A i18n_datasource_not_allow_delete_msg= \u4E2A\u6570\u636E\u96C6\u6B63\u5728\u4F7F\u7528\u6B64\u6570\u636E\u6E90\uFF0C\u65E0\u6CD5\u5220\u9664 i18n_task_name_repeat=\u540C\u4E00\u6570\u636E\u96C6\u4E0B\u4EFB\u52A1\u540D\u79F0\u5DF2\u88AB\u4F7F\u7528 i18n_id_or_pwd_error=\u65E0\u6548\u7684ID\u6216\u5BC6\u7801 +i18n_user_do_not_exist=\u7528\u6237\u4E0D\u5B58\u5728 +i18n_user_is_disable=\u7528\u6237\u72B6\u6001\u65E0\u6548 +i18n_login_type_error=\u767B\u5F55\u65B9\u5F0F\u9519\u8BEF +i18n_account_is_locked=\u8D26\u6237\u5DF2\u9501\u5B9A i18n_datasource_delete=\u5F53\u524D\u7528\u5230\u7684\u6570\u636E\u6E90\u5DF2\u88AB\u5220\u9664 i18n_dataset_delete=\u5F53\u524D\u7528\u5230\u7684\u6570\u636E\u96C6\u5DF2\u88AB\u5220\u9664 i18n_dataset_no_permission=\u5F53\u524D\u7528\u5230\u7684\u6570\u636E\u96C6\u6CA1\u6709\u6743\u9650 @@ -224,5 +228,10 @@ I18N_USER_PWD_FORMAT_ERROR=\u5BC6\u7801\u683C\u5F0F\u9519\u8BEF I18N_DS_INVALID=\u6570\u636E\u6E90\u65E0\u6548. I18N_DS_INVALID_TABLE=\u6570\u636E\u6E90\u4E2D\u6709\u65E0\u6548\u7684\u8868 + + +I18N_ACCOUNT_LOCKED=\u8D26\u53F7\u3010%s\u3011\u5DF2\u9501\u5B9A\uFF01 + I18N_PANEL_EXIST=\u5F53\u524D\u4EEA\u8868\u677F\u540D\u79F0\u5728\u8BE5\u76EE\u5F55\u4E0B\u9762\u5DF2\u7ECF\u5B58\u5728 I18N_DATASET_GROUP_EXIST=\u5F53\u524D\u6570\u636E\u96C6\u5206\u7EC4\u540D\u79F0\u5728\u8BE5\u76EE\u5F55\u4E0B\u9762\u5DF2\u7ECF\u5B58\u5728 + diff --git a/backend/src/main/resources/i18n/messages_zh_TW.properties b/backend/src/main/resources/i18n/messages_zh_TW.properties index e80cab88ef..84c61b385e 100644 --- a/backend/src/main/resources/i18n/messages_zh_TW.properties +++ b/backend/src/main/resources/i18n/messages_zh_TW.properties @@ -72,6 +72,10 @@ i18n_sql_not_empty=SQL \u4E0D\u80FD\u70BA\u7A7A i18n_datasource_not_allow_delete_msg= \u500B\u6578\u64DA\u96C6\u6B63\u5728\u4F7F\u7528\u6B64\u6578\u64DA\u6E90\uFF0C\u7121\u6CD5\u522A\u9664 i18n_task_name_repeat=\u540C\u4E00\u6578\u64DA\u96C6\u4E0B\u4EFB\u52D9\u540D\u7A31\u5DF2\u88AB\u4F7F\u7528 i18n_id_or_pwd_error=\u7121\u6548\u7684ID\u6216\u5BC6\u78BC +i18n_user_do_not_exist=\u7528\u6236\u4E0D\u5B58\u5728 +i18n_user_is_disable=\u7528\u6236\u72C0\u614B\u7121\u6548 +i18n_login_type_error=\u767B\u9304\u65B9\u5F0F\u932F\u8AA4 +i18n_account_is_locked=\u8CEC\u6236\u5DF2\u9396\u5B9A i18n_datasource_delete=\u7576\u524D\u7528\u5230\u7684\u6578\u64DA\u6E90\u5DF2\u88AB\u522A\u9664 i18n_dataset_delete=\u7576\u524D\u7528\u5230\u7684\u6578\u64DA\u96C6\u5DF2\u88AB\u522A\u9664 i18n_dataset_no_permission=\u7576\u524D\u7528\u5230\u7684\u6578\u64DA\u96C6\u6C92\u6709\u6B0A\u9650 @@ -220,5 +224,10 @@ I18N_USER_PWD_FORMAT_ERROR=\u5BC6\u78BC\u683C\u5F0F\u932F\u8AA4 I18N_DS_INVALID=\u6578\u64DA\u6E90\u7121\u6548. I18N_DS_INVALID_TABLE=\u6578\u64DA\u6E90\u4E2D\u6709\u7121\u6548\u7684\u8868 + + +I18N_ACCOUNT_LOCKED=\u8CEC\u865F\u3010%s\u3011\u5DF2\u9396\u5B9A\uFF01 + I18N_PANEL_EXIST=\u7576\u524D\u5100\u9336\u95C6\u540D\u7A31\u5728\u8A72\u76EE\u9304\u4E0B\u9762\u5DF2\u7D93\u5B58\u5728 I18N_DATASET_GROUP_EXIST=\u7576\u524D\u6578\u64DA\u96C6\u5206\u7D44\u540D\u7A31\u5728\u8A72\u76EE\u9304\u4E0B\u9762\u5DF2\u7D93\u5B58\u5728 + diff --git a/frontend/src/api/system/user.js b/frontend/src/api/system/user.js index 3e4d895d24..4e4ee78512 100644 --- a/frontend/src/api/system/user.js +++ b/frontend/src/api/system/user.js @@ -9,7 +9,8 @@ const pathMap = { createPath: '/api/user/create', updatePath: '/api/user/update', editPasswordPath: '/api/user/adminUpdatePwd', - editStatusPath: '/api/user/updateStatus' + editStatusPath: '/api/user/updateStatus', + unlockPath: '/api/user/unlock/' } export function userLists(page, size, data) { return request({ @@ -133,4 +134,12 @@ export function existLdapUsers() { }) } -export default { editPassword, delUser, editUser, addUser, userLists, editStatus, personInfo, updatePerson, updatePersonPwd, allRoles, roleGrid, ldapUsers, saveLdapUser, existLdapUsers } +export function unLock(username) { + return request({ + url: pathMap.unlockPath + username, + method: 'post', + loading: false + }) +} + +export default { editPassword, delUser, editUser, addUser, userLists, editStatus, personInfo, updatePerson, updatePersonPwd, allRoles, roleGrid, ldapUsers, saveLdapUser, existLdapUsers, unLock } diff --git a/frontend/src/components/canvas/custom-component/UserView.vue b/frontend/src/components/canvas/custom-component/UserView.vue index e658c49cd7..68a044312f 100644 --- a/frontend/src/components/canvas/custom-component/UserView.vue +++ b/frontend/src/components/canvas/custom-component/UserView.vue @@ -109,7 +109,7 @@ @onJumpClick="jumpClick" />
- +
@@ -582,14 +582,14 @@ export default { this.chart['position'] = this.inTab ? 'tab' : 'panel' // 记录当前数据 this.panelViewDetailsInfo[id] = JSON.stringify(this.chart) - if(this.element.needAdaptor){ + if (this.element.needAdaptor) { const customStyleObj = JSON.parse(this.chart.customStyle) const customAttrObj = JSON.parse(this.chart.customAttr) adaptCurTheme(customStyleObj, customAttrObj) this.chart.customStyle = JSON.stringify(customStyleObj) this.chart.customAttr = JSON.stringify(customAttrObj) - viewEditSave(this.panelInfo.id,{ id: this.chart.id, customStyle: this.chart.customStyle, customAttr: this.chart.customAttr }) - this.$store.commit('adaptorStatusDisable',this.element.id) + viewEditSave(this.panelInfo.id, { id: this.chart.id, customStyle: this.chart.customStyle, customAttr: this.chart.customAttr }) + this.$store.commit('adaptorStatusDisable', this.element.id) } this.sourceCustomAttrStr = this.chart.customAttr this.sourceCustomStyleStr = this.chart.customStyle diff --git a/frontend/src/lang/en.js b/frontend/src/lang/en.js index 5afe4982aa..c5923c6fdf 100644 --- a/frontend/src/lang/en.js +++ b/frontend/src/lang/en.js @@ -131,7 +131,13 @@ export default { default_login: 'Normal' }, commons: { - uninstall: 'Uninstall', + + + unlock: 'Unlock', + unlock_success: 'Unlock success', + + uninstall:'Uninstall', + no_result: 'No Result', manage_member: 'Managing members', confirm_remove_cancel: 'Are you sure to delete the role?', diff --git a/frontend/src/lang/tw.js b/frontend/src/lang/tw.js index d7ab8752a8..43e2b36f90 100644 --- a/frontend/src/lang/tw.js +++ b/frontend/src/lang/tw.js @@ -131,7 +131,13 @@ export default { default_login: '普通登錄' }, commons: { - uninstall: '卸载', + + + unlock: '解鎖', + unlock_success: '解鎖成功', + + uninstall:'卸载', + no_result: '没有找到相关内容', manage_member: '管理成員', user_confirm_remove_cancel: '確定將該用戶從角色中移除嗎?', diff --git a/frontend/src/lang/zh.js b/frontend/src/lang/zh.js index b1d995e0f6..f511e030c8 100644 --- a/frontend/src/lang/zh.js +++ b/frontend/src/lang/zh.js @@ -131,7 +131,11 @@ export default { default_login: '普通登录' }, commons: { - uninstall: '卸载', + + unlock: '解锁', + unlock_success: '解锁成功', + uninstall:'卸载', + no_result: '没有找到相关内容', manage_member: '管理成员', confirm_remove_cancel: '确定删除该角色吗?', diff --git a/frontend/src/views/background/index.vue b/frontend/src/views/background/index.vue index 5b1ff9de34..9474251b10 100644 --- a/frontend/src/views/background/index.vue +++ b/frontend/src/views/background/index.vue @@ -80,7 +80,7 @@ - +
- {{ $t('commons.all') }} - {{ filter.value[0] }} + + {{ $t('commons.all') }} + + + {{ filter.value[0] }} +