Merge remote-tracking branch 'origin/master'

# Conflicts:
#	images/group-qr-code.png
pull/90/head
xiuchen 2024-06-18 11:18:33 +08:00
commit ca213a8c05
11 changed files with 65 additions and 36 deletions

View File

@ -23,21 +23,26 @@
用于管理企业内员工账号、权限、身份认证、应用访问,帮助整合部署在本地或云端的内部办公系统、业务系统及三方 SaaS
系统的所有身份,实现一个账号打通所有应用的服务。
官网https://topiam.cn
管理端演示https://eiam-console.topiam.cn
用户端演示https://eiam-portal.topiam.cn
付费服务支持或商务合作:
<img src="images/contact-qr-code.png" alt="logo" width="200px"/>
## 核心特性
+ 提供统一组织信息管理,多维度建立对应关系,实现在一个平台对企业人员、组织架构、应用信息的高效统一管理。
+ 支持钉钉、飞书、企业微信等身份源集成能力实现系统和企业OA平台数据联动以用户为管理基点结合入职、离职、调岗、兼职等人事事件关联其相关应用权限变化而变化保证应用访问权限的安全控制。
+ 支持多因素认证,行为验证码、社交认证,融合认证等机制,保证用户认证安全可靠。
+ 支持微信、钉钉、、飞书QQ等社交认证集成使企业具有快速纳入互联网化认证能力。
+ 支持微信、钉钉、飞书QQ等社交认证集成使企业具有快速纳入互联网化认证能力。
+ 支持 `SAML2``OAuth2``OIDC``CAS``JWT`,`表单代填`等认证协议及机制,实现单点登录功能,预配置大量 SaaS 应用及传统应用模板,开箱即用。
+ 完善的安全审计,详尽记录每一次用户行为,使每一步操作有据可循,实时记录企业信息安全状况,精准识别企业异常访问和潜在威胁的源头。
+ 提供标准`RESTAPI`、`SCIM2.0`接口轻松完成机构用户同步,提供`HTTP`、`MQ`事件通知,实现企业对于账号生命周期的精细化管理。
## 在线演示
+ 管理端https://eiam-console.topiam.cn
+ 用户端https://eiam-portal.topiam.cn
## 系统架构
![](https://github.com/topiam/eiam/assets/30397655/dc2c2749-e873-4d4d-ba20-43d5db81c6b8)

View File

@ -1,6 +1,6 @@
#
# eiam-common - Employee Identity and Access Management
# Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
# Copyright \u00A9 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
@ -23,7 +23,7 @@ forget_password=\u9A8C\u8BC1\u7801${verify_code}\uFF0C\u8BE5\u9A8C\u8BC1\u7801${
update_password=\u9A8C\u8BC1\u7801${verify_code}\uFF0C\u8BE5\u9A8C\u8BC1\u7801${expire_time}\u5206\u949F\u5185\u6709\u6548\uFF0C\u8BF7\u52FF\u6CC4\u9732\u4E8E\u4ED6\u4EBA\u3002
login=\u9A8C\u8BC1\u7801${verify_code}\uFF0C\u8BE5\u9A8C\u8BC1\u7801${expire_time}\u5206\u949F\u5185\u6709\u6548\uFF0C\u8BF7\u52FF\u6CC4\u9732\u4E8E\u4ED6\u4EBA\u3002
again_verify=\u9A8C\u8BC1\u7801${verify_code}\uFF0C\u8BE5\u9A8C\u8BC1\u7801${expire_time}\u5206\u949F\u5185\u6709\u6548\uFF0C\u8BF7\u52FF\u6CC4\u9732\u4E8E\u4ED6\u4EBA\u3002
welcome_sms=\u5c0a\u656c\u7684\u7528\u6237\uff1a${username}\uff0c\u60a8\u7684 TopIAM \u8d26\u6237\u7684\u521d\u59cb\u5bc6\u7801\u662f\uff1a${password}
welcome_sms=\u5C0A\u656C\u7684\u7528\u6237\uFF1A${username}\uFF0C\u60A8\u7684 TOPIAM \u8D26\u6237\u7684\u521D\u59CB\u5BC6\u7801\u662F\uFF1A${password}
reset_password=\u9A8C\u8BC1\u7801${verify_code}\uFF0C\u8BE5\u9A8C\u8BC1\u7801${expire_time}\u5206\u949F\u5185\u6709\u6548\uFF0C\u8BF7\u52FF\u6CC4\u9732\u4E8E\u4ED6\u4EBA\u3002
reset_password_success=\u7528\u6237\u540D${username}\uFF0C\u5BC6\u7801${password}\uFF0C\u8BF7\u52FF\u6CC4\u9732\u4E8E\u4ED6\u4EBA\u3002
warning=\u5C0A\u656C\u7684\u7528\u6237\uFF1A\u4F60\u597D\uFF01\u68C0\u6D4B\u5230\u60A8\u7684\u8D26\u53F7\u201C\u7528\u6237\u540D ${username}\u201D\u5B58\u5728\u5F02\u5E38\u98CE\u9669\uFF0C\u60A8\u53EF\u81F3\u7528\u6237\u4E2D\u5FC3-\u8D26\u53F7\u8BBE\u7F6E-\u98CE\u9669\u4E8B\u4EF6\u67E5\u770B\u5F02\u5E38\u8BE6\u60C5\u3002

View File

@ -49,6 +49,7 @@
"@ant-design/icons": "^5.3.7",
"@ant-design/maps": "^1.0.8",
"@ant-design/pro-components": "^2.7.1",
"@ant-design/pro-editor": "^1.2.1",
"ahooks": "^3.7.11",
"antd": "^5.17.0",
"antd-img-crop": "^4.21.0",
@ -56,13 +57,13 @@
"classnames": "^2.5.1",
"codemirror": "^5.65.5",
"content-security-policy-parser": "^0.6.0",
"copy-to-clipboard": "^3.3.3",
"crypto-js": "^4.2.0",
"dayjs": "^1.11.11",
"echarts": "^5.5.0",
"fetch-jsonp": "^1.3.0",
"form-render": "^2.4.4",
"google-libphonenumber": "^3.2.34",
"copy-to-clipboard": "^3.3.3",
"js-base64": "^3.7.7",
"js-yaml": "^4.1.0",
"jsencrypt": "^3.3.2",

View File

@ -15,8 +15,10 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { DraggablePanel } from '@ant-design/pro-editor';
import { PageContainer } from '@ant-design/pro-components';
import { Col, Row } from 'antd';
import { Col, Flex, Row } from 'antd';
import { useState } from 'react';
import OrgTree from './components/Organization';
import UserList from './components/User';
@ -48,16 +50,19 @@ export const User = () => {
return (
<PageContainer content={intl.formatMessage({ id: 'pages.account.user_list.desc' })}>
<Row gutter={[16, 16]}>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
{/* 左侧 */}
<Col {...leftLayout} style={{ minHeight: '100%', overflow: 'auto' }}>
<DraggablePanel
placement="left"
maxWidth={800}
style={{ flex: 1, padding: 0, borderRadius: 12 }}
>
<OrgTree onSelect={treeOnSelect} />
</Col>
</DraggablePanel>
<div style={{ width: 'auto', flex: 1, padding: '0px 6px' }}></div>
{/* 表格 */}
<Col {...rightLayout} style={{ minHeight: '100%', overflow: 'auto' }}>
<UserList organization={organization} />
</Col>
</Row>
<UserList organization={organization} />
</div>
</PageContainer>
);
};

View File

@ -318,7 +318,7 @@ export const OrganizationTree = (props: {
return (
<div className={styles}>
<Card
style={{ height: 'calc(100vh - 200px)', overflow: 'auto' }}
style={{ height: 'calc(100vh - 220px)', overflow: 'auto' }}
bordered={false}
className={classnames(`${prefixCls}`)}
>

View File

@ -387,7 +387,7 @@ export default (props: UserListProps) => {
return (
<>
{!organization ? (
<Card style={{ height: 'calc(100vh - 200px)' }} bordered={false}>
<Card style={{ height: 'calc(100vh - 220px)' }} bordered={false}>
<Skeleton paragraph={{ rows: 10 }} active={true} />
</Card>
) : (
@ -396,10 +396,10 @@ export default (props: UserListProps) => {
scroll={{ x: 1200 }}
params={{ organizationId: organization?.id, inclSubOrganization }}
style={{
height: 'calc(100vh - 200px)',
height: 'calc(100vh - 220px)',
overflow: 'auto',
}}
cardProps={{ style: { minHeight: 'calc(100vh - 200px)' } }}
cardProps={{ style: { minHeight: 'calc(100vh - 220px)' } }}
search={{
defaultCollapsed: true,
}}

View File

@ -26,7 +26,11 @@ import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import cn.topiam.employee.audit.context.AuditContext;
import cn.topiam.employee.audit.entity.Target;
@ -245,20 +249,28 @@ public class IdentitySourceServiceImpl implements IdentitySourceService {
*/
@Override
public Boolean identitySourceConfigValidator(IdentitySourceConfigValidatorParam param) {
return switch (param.getProvider()) {
//钉钉
case DINGTALK -> {
DingTalkConfig config = JSONObject.parseObject(param.getConfig().toJSONString(),
DingTalkConfig.class);
yield new DingTalkConfigValidator().validate(config);
}
case FEISHU -> {
FeiShuConfig config = JSONObject.parseObject(param.getConfig().toJSONString(),
FeiShuConfig.class);
yield new FeiShuConfigValidator().validate(config);
}
default -> throw new TopIamException("暂未支持此提供商连接验证");
};
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
try {
return switch (param.getProvider()) {
//钉钉
case DINGTALK -> {
DingTalkConfig config = objectMapper
.readValue(param.getConfig().toJSONString(), DingTalkConfig.class);;
yield new DingTalkConfigValidator().validate(config);
}
case FEISHU -> {
FeiShuConfig config = objectMapper
.readValue(param.getConfig().toJSONString(), FeiShuConfig.class);
yield new FeiShuConfigValidator().validate(config);
}
default -> throw new TopIamException("暂未支持此提供商连接验证");
};
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
/**

View File

@ -67,4 +67,7 @@ public class DingTalkConfig extends IdentitySourceConfig {
*/
@JsonPropertyEncrypt
private String token;
public DingTalkConfig() {
}
}

View File

@ -63,4 +63,7 @@ public class FeiShuConfig extends IdentitySourceConfig {
*/
@JsonPropertyEncrypt
private String verificationToken;
public FeiShuConfig() {
}
}

BIN
images/contact-qr-code.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 876 KiB