mirror of
https://gitee.com/dolphinscheduler/DolphinScheduler.git
synced 2024-11-29 18:58:05 +08:00
[Improvement-16106] Add admin-user-filter in LDAP (#16826)
Some checks failed
API-Test / API-Test-Path-Filter (push) Has been cancelled
Backend / Backend-Path-Filter (push) Has been cancelled
CodeQL / Analyze (push) Has been cancelled
E2E-K8S / E2E-K8S-Path-Filter (push) Has been cancelled
E2E / E2E-Path-Filter (push) Has been cancelled
publish-docker / build (push) Has been cancelled
publish-helm-chart / build (push) Has been cancelled
Test / Unit-Test-Path-Filter (push) Has been cancelled
API-Test / API-Test-Build (push) Has been cancelled
API-Test / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.api.test.cases.ExecutorAPITest name:ExecutorAPITest]) (push) Has been cancelled
API-Test / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.api.test.cases.LdapLoginAPITest name:LdapLoginAPITest]) (push) Has been cancelled
API-Test / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.api.test.cases.ProjectAPITest name:ProjectAPITest]) (push) Has been cancelled
API-Test / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.api.test.cases.SchedulerAPITest name:SchedulerAPITest]) (push) Has been cancelled
API-Test / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.api.test.cases.TenantAPITest name:TenantAPITest]) (push) Has been cancelled
API-Test / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.api.test.cases.WorkerGroupAPITest name:WorkerGroupAPITest]) (push) Has been cancelled
API-Test / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.api.test.cases.WorkflowDefinitionAPITest name:WorkflowDefinitionAPITest]) (push) Has been cancelled
API-Test / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.api.test.cases.WorkflowInstanceAPITest name:WorkflowInstanceAPITest]) (push) Has been cancelled
API-Test / API-Test-Result (push) Has been cancelled
Backend / Backend-Build (11) (push) Has been cancelled
Backend / Backend-Build (8) (push) Has been cancelled
Backend / ${{ matrix.case.name }} (map[name:cluster-test-mysql-with-mysql-registry script:.github/workflows/cluster-test/mysql_with_mysql_registry/start-job.sh]) (push) Has been cancelled
Backend / ${{ matrix.case.name }} (map[name:cluster-test-mysql-with-zookeeper-registry script:.github/workflows/cluster-test/mysql_with_zookeeper_registry/start-job.sh]) (push) Has been cancelled
Backend / ${{ matrix.case.name }} (map[name:cluster-test-postgresql-with-postgresql-registry script:.github/workflows/cluster-test/postgresql_with_postgresql_registry/start-job.sh]) (push) Has been cancelled
Backend / ${{ matrix.case.name }} (map[name:cluster-test-postgresql-zookeeper-registry script:.github/workflows/cluster-test/postgresql_with_zookeeper_registry/start-job.sh]) (push) Has been cancelled
Backend / ${{ matrix.case.name }}-${{ matrix.version }} (map[name:schema-check-with-mysql script:.github/workflows/schema-check/mysql/start-job.sh], 3.1.9) (push) Has been cancelled
Backend / ${{ matrix.case.name }}-${{ matrix.version }} (map[name:schema-check-with-mysql script:.github/workflows/schema-check/mysql/start-job.sh], 3.2.0) (push) Has been cancelled
Backend / ${{ matrix.case.name }}-${{ matrix.version }} (map[name:schema-check-with-postgresql script:.github/workflows/schema-check/postgresql/start-job.sh], 3.1.9) (push) Has been cancelled
Backend / ${{ matrix.case.name }}-${{ matrix.version }} (map[name:schema-check-with-postgresql script:.github/workflows/schema-check/postgresql/start-job.sh], 3.2.0) (push) Has been cancelled
Backend / Build (push) Has been cancelled
E2E-K8S / E2E-K8S-Execute (push) Has been cancelled
E2E-K8S / E2E-K8S-Result (push) Has been cancelled
E2E / E2E-Build (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.ClickhouseDataSourceE2ETest name:ClickhouseDataSourceE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.ClusterE2ETest name:ClusterE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.EnvironmentE2ETest name:EnvironmentE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.FileManageE2ETest name:FileManageE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.HiveDataSourceE2ETest name:HiveDataSourceE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.MysqlDataSourceE2ETest name:MysqlDataSourceE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.PostgresDataSourceE2ETest name:PostgresDataSourceE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.ProjectE2ETest name:ProjectE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.QueueE2ETest name:QueueE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.SqlServerDataSourceE2ETest name:SqlServerDataSourceE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.TenantE2ETest name:TenantE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.TokenE2ETest name:TokenE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.UserE2ETest name:UserE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.WorkerGroupE2ETest name:WorkerGroupE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.WorkflowE2ETest name:WorkflowE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.WorkflowHttpTaskE2ETest name:WorkflowHttpTaskE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.WorkflowJavaTaskE2ETest name:WorkflowJavaTaskE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.tasks.PythonTaskE2ETest name:PythonTaskE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.tasks.ShellTaskE2ETest name:ShellTaskE2ETest]) (push) Has been cancelled
E2E / E2E (push) Has been cancelled
Test / Unit-Test (11) (push) Has been cancelled
Test / Unit-Test (8) (push) Has been cancelled
Test / Unit Test (push) Has been cancelled
Some checks failed
API-Test / API-Test-Path-Filter (push) Has been cancelled
Backend / Backend-Path-Filter (push) Has been cancelled
CodeQL / Analyze (push) Has been cancelled
E2E-K8S / E2E-K8S-Path-Filter (push) Has been cancelled
E2E / E2E-Path-Filter (push) Has been cancelled
publish-docker / build (push) Has been cancelled
publish-helm-chart / build (push) Has been cancelled
Test / Unit-Test-Path-Filter (push) Has been cancelled
API-Test / API-Test-Build (push) Has been cancelled
API-Test / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.api.test.cases.ExecutorAPITest name:ExecutorAPITest]) (push) Has been cancelled
API-Test / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.api.test.cases.LdapLoginAPITest name:LdapLoginAPITest]) (push) Has been cancelled
API-Test / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.api.test.cases.ProjectAPITest name:ProjectAPITest]) (push) Has been cancelled
API-Test / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.api.test.cases.SchedulerAPITest name:SchedulerAPITest]) (push) Has been cancelled
API-Test / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.api.test.cases.TenantAPITest name:TenantAPITest]) (push) Has been cancelled
API-Test / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.api.test.cases.WorkerGroupAPITest name:WorkerGroupAPITest]) (push) Has been cancelled
API-Test / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.api.test.cases.WorkflowDefinitionAPITest name:WorkflowDefinitionAPITest]) (push) Has been cancelled
API-Test / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.api.test.cases.WorkflowInstanceAPITest name:WorkflowInstanceAPITest]) (push) Has been cancelled
API-Test / API-Test-Result (push) Has been cancelled
Backend / Backend-Build (11) (push) Has been cancelled
Backend / Backend-Build (8) (push) Has been cancelled
Backend / ${{ matrix.case.name }} (map[name:cluster-test-mysql-with-mysql-registry script:.github/workflows/cluster-test/mysql_with_mysql_registry/start-job.sh]) (push) Has been cancelled
Backend / ${{ matrix.case.name }} (map[name:cluster-test-mysql-with-zookeeper-registry script:.github/workflows/cluster-test/mysql_with_zookeeper_registry/start-job.sh]) (push) Has been cancelled
Backend / ${{ matrix.case.name }} (map[name:cluster-test-postgresql-with-postgresql-registry script:.github/workflows/cluster-test/postgresql_with_postgresql_registry/start-job.sh]) (push) Has been cancelled
Backend / ${{ matrix.case.name }} (map[name:cluster-test-postgresql-zookeeper-registry script:.github/workflows/cluster-test/postgresql_with_zookeeper_registry/start-job.sh]) (push) Has been cancelled
Backend / ${{ matrix.case.name }}-${{ matrix.version }} (map[name:schema-check-with-mysql script:.github/workflows/schema-check/mysql/start-job.sh], 3.1.9) (push) Has been cancelled
Backend / ${{ matrix.case.name }}-${{ matrix.version }} (map[name:schema-check-with-mysql script:.github/workflows/schema-check/mysql/start-job.sh], 3.2.0) (push) Has been cancelled
Backend / ${{ matrix.case.name }}-${{ matrix.version }} (map[name:schema-check-with-postgresql script:.github/workflows/schema-check/postgresql/start-job.sh], 3.1.9) (push) Has been cancelled
Backend / ${{ matrix.case.name }}-${{ matrix.version }} (map[name:schema-check-with-postgresql script:.github/workflows/schema-check/postgresql/start-job.sh], 3.2.0) (push) Has been cancelled
Backend / Build (push) Has been cancelled
E2E-K8S / E2E-K8S-Execute (push) Has been cancelled
E2E-K8S / E2E-K8S-Result (push) Has been cancelled
E2E / E2E-Build (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.ClickhouseDataSourceE2ETest name:ClickhouseDataSourceE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.ClusterE2ETest name:ClusterE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.EnvironmentE2ETest name:EnvironmentE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.FileManageE2ETest name:FileManageE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.HiveDataSourceE2ETest name:HiveDataSourceE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.MysqlDataSourceE2ETest name:MysqlDataSourceE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.PostgresDataSourceE2ETest name:PostgresDataSourceE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.ProjectE2ETest name:ProjectE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.QueueE2ETest name:QueueE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.SqlServerDataSourceE2ETest name:SqlServerDataSourceE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.TenantE2ETest name:TenantE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.TokenE2ETest name:TokenE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.UserE2ETest name:UserE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.WorkerGroupE2ETest name:WorkerGroupE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.WorkflowE2ETest name:WorkflowE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.WorkflowHttpTaskE2ETest name:WorkflowHttpTaskE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.WorkflowJavaTaskE2ETest name:WorkflowJavaTaskE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.tasks.PythonTaskE2ETest name:PythonTaskE2ETest]) (push) Has been cancelled
E2E / ${{ matrix.case.name }} (map[class:org.apache.dolphinscheduler.e2e.cases.tasks.ShellTaskE2ETest name:ShellTaskE2ETest]) (push) Has been cancelled
E2E / E2E (push) Has been cancelled
Test / Unit-Test (11) (push) Has been cancelled
Test / Unit-Test (8) (push) Has been cancelled
Test / Unit Test (push) Has been cancelled
* improvement 16106 * fix ut * fix comment * fix spotless
This commit is contained in:
parent
b885970b6b
commit
627c76b75a
2
.github/workflows/api-test.yml
vendored
2
.github/workflows/api-test.yml
vendored
@ -115,6 +115,8 @@ jobs:
|
|||||||
class: org.apache.dolphinscheduler.api.test.cases.ExecutorAPITest
|
class: org.apache.dolphinscheduler.api.test.cases.ExecutorAPITest
|
||||||
- name: WorkflowInstanceAPITest
|
- name: WorkflowInstanceAPITest
|
||||||
class: org.apache.dolphinscheduler.api.test.cases.WorkflowInstanceAPITest
|
class: org.apache.dolphinscheduler.api.test.cases.WorkflowInstanceAPITest
|
||||||
|
- name: LdapLoginAPITest
|
||||||
|
class: org.apache.dolphinscheduler.api.test.cases.LdapLoginAPITest
|
||||||
env:
|
env:
|
||||||
RECORDING_PATH: /tmp/recording-${{ matrix.case.name }}
|
RECORDING_PATH: /tmp/recording-${{ matrix.case.name }}
|
||||||
steps:
|
steps:
|
||||||
|
@ -10,21 +10,23 @@
|
|||||||
security:
|
security:
|
||||||
authentication:
|
authentication:
|
||||||
# Authentication types (supported types: PASSWORD,LDAP,CASDOOR_SSO)
|
# Authentication types (supported types: PASSWORD,LDAP,CASDOOR_SSO)
|
||||||
type: PASSWORD
|
type: LDAP
|
||||||
# IF you set type `LDAP`, below config will be effective
|
# IF you set type `LDAP`, below config will be effective
|
||||||
ldap:
|
ldap:
|
||||||
# ldap server config
|
# ldap server config
|
||||||
urls: ldap://ldap.forumsys.com:389/
|
url: ldap://ldap.forumsys.com:389/
|
||||||
base-dn: dc=example,dc=com
|
base-dn: dc=example,dc=com
|
||||||
username: cn=read-only-admin,dc=example,dc=com
|
username: cn=admin,dc=example,dc=com
|
||||||
password: password
|
password: password
|
||||||
user:
|
user:
|
||||||
# admin userId when you use LDAP login
|
# admin userId when you use LDAP login
|
||||||
admin: read-only-admin
|
admin: ldap-admin
|
||||||
|
# user search filter to find admin user
|
||||||
|
admin-user-filter: (&(cn={0}))
|
||||||
identity-attribute: uid
|
identity-attribute: uid
|
||||||
email-attribute: mail
|
email-attribute: mail
|
||||||
# action when ldap user is not exist (supported types: CREATE,DENY)
|
# action when ldap user is not exist (supported types: CREATE,DENY)
|
||||||
not-exist-action: CREATE
|
not-exist-action: DENY
|
||||||
ssl:
|
ssl:
|
||||||
enable: false
|
enable: false
|
||||||
# jks file absolute path && password
|
# jks file absolute path && password
|
||||||
@ -71,31 +73,6 @@ casdoor:
|
|||||||
redirect-url: ""
|
redirect-url: ""
|
||||||
```
|
```
|
||||||
|
|
||||||
For detailed explanation of specific fields, please see: [Api-server related configuration](../../architecture/configuration.md)
|
|
||||||
|
|
||||||
## LDAP Test
|
|
||||||
|
|
||||||
We offer you a unit-test class while you can test the integration of DolphinScheduler with LDAP without running the service.
|
|
||||||
|
|
||||||
> dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/security/impl/ldap/LdapServiceTest.java
|
|
||||||
|
|
||||||
You can follow guide below:
|
|
||||||
- Change`TestPropertySource`configuration to your LDAP information.
|
|
||||||
- Change userId && userPwd to your information in the `ldapLogin` method.
|
|
||||||
- Change the expected email to the return value you expect in the `ldapLogin` method.
|
|
||||||
- Run`ldapLogin`method and determine whether the LDAP login result is expected.
|
|
||||||
|
|
||||||
If you want to enable ssl, please change configuration in `TestPropertySource` like below:
|
|
||||||
|
|
||||||
```
|
|
||||||
security.authentication.ldap.ssl.enable=false
|
|
||||||
// absolute path
|
|
||||||
security.authentication.ldap.ssl.trust-store=/ldapkeystore.jks
|
|
||||||
security.authentication.ldap.ssl.trust-store-password=yourpassword
|
|
||||||
```
|
|
||||||
|
|
||||||
Then run`ldapLoginSSL`method and determine whether the LDAP login result is expected.
|
|
||||||
|
|
||||||
## Casdoor SSO
|
## Casdoor SSO
|
||||||
|
|
||||||
[Casdoor](https://casdoor.org/) is a UI-first Identity Access Management (IAM) / Single-Sign-On (SSO) platform based on OAuth 2.0, OIDC, SAML and CAS. You can add SSO capability to Dolphinscheduler through Casdoor by following these steps:
|
[Casdoor](https://casdoor.org/) is a UI-first Identity Access Management (IAM) / Single-Sign-On (SSO) platform based on OAuth 2.0, OIDC, SAML and CAS. You can add SSO capability to Dolphinscheduler through Casdoor by following these steps:
|
||||||
|
@ -14,17 +14,18 @@ security:
|
|||||||
# IF you set type `LDAP`, below config will be effective
|
# IF you set type `LDAP`, below config will be effective
|
||||||
ldap:
|
ldap:
|
||||||
# ldap server config
|
# ldap server config
|
||||||
urls: ldap://ldap.forumsys.com:389/
|
url: ldap://ldap.forumsys.com:389/
|
||||||
base-dn: dc=example,dc=com
|
base-dn: dc=example,dc=com
|
||||||
username: cn=read-only-admin,dc=example,dc=com
|
username: cn=admin,dc=example,dc=com
|
||||||
password: password
|
password: password
|
||||||
user:
|
user:
|
||||||
# admin userId when you use LDAP login
|
# admin userId when you use LDAP login
|
||||||
admin: read-only-admin
|
admin: ldap-admin
|
||||||
|
# user search filter to find admin user
|
||||||
identity-attribute: uid
|
identity-attribute: uid
|
||||||
email-attribute: mail
|
email-attribute: mail
|
||||||
# action when ldap user is not exist (supported types: CREATE,DENY)
|
# action when ldap user is not exist (supported types: CREATE,DENY)
|
||||||
not-exist-action: CREATE
|
not-exist-action: DENY
|
||||||
ssl:
|
ssl:
|
||||||
enable: false
|
enable: false
|
||||||
# jks file absolute path && password
|
# jks file absolute path && password
|
||||||
@ -71,31 +72,6 @@ casdoor:
|
|||||||
redirect-url: ""
|
redirect-url: ""
|
||||||
```
|
```
|
||||||
|
|
||||||
具体字段解释详见:[Api-server相关配置](../../architecture/configuration.md)
|
|
||||||
|
|
||||||
## 开发者LDAP测试
|
|
||||||
|
|
||||||
我们提供了一个单元测试类,可以在不启动项目的情况下测试DolphinScheduler与LDAP的集成。
|
|
||||||
|
|
||||||
> dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/security/impl/ldap/LdapServiceTest.java
|
|
||||||
|
|
||||||
使用步骤如下:
|
|
||||||
- 修改`TestPropertySource`配置参数为你的LDAP信息;
|
|
||||||
- 修改`ldapLogin`方法中的userId和userPwd为你的账号密码;
|
|
||||||
- 修改`ldapLogin`方法中的expected email为正常登陆的返回值;
|
|
||||||
- 执行`ldapLogin`方法,判断LDAP登陆结果是否为预期;
|
|
||||||
|
|
||||||
如果你要启用ssl,请修改`TestPropertySource`配置中ssl相关参数为:
|
|
||||||
|
|
||||||
```
|
|
||||||
security.authentication.ldap.ssl.enable=false
|
|
||||||
// absolute path
|
|
||||||
security.authentication.ldap.ssl.trust-store=/ldapkeystore.jks
|
|
||||||
security.authentication.ldap.ssl.trust-store-password=yourpassword
|
|
||||||
```
|
|
||||||
|
|
||||||
运行`ldapLoginSSL`方法,判断email是否为预期的返回值。
|
|
||||||
|
|
||||||
## 通过 Casdoor 实现 SSO 登录
|
## 通过 Casdoor 实现 SSO 登录
|
||||||
|
|
||||||
Casdoor 是基于 OAuth 2.0、OIDC、SAML 和 CAS 的面向 UI 的身份访问管理(IAM)/单点登录(SSO)平台。您可以通过以下步骤通过 Casdoor 为 Dolphinscheduler 添加 SSO 功能:
|
Casdoor 是基于 OAuth 2.0、OIDC、SAML 和 CAS 的面向 UI 的身份访问管理(IAM)/单点登录(SSO)平台。您可以通过以下步骤通过 Casdoor 为 Dolphinscheduler 添加 SSO 功能:
|
||||||
|
@ -43,3 +43,29 @@ class TenantAPITest {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
## Local development
|
||||||
|
|
||||||
|
### Mac M1
|
||||||
|
Add VM options to the test configuration in IntelliJ IDEA:
|
||||||
|
```
|
||||||
|
# In this mode you need to install docker desktop for mac and run it with locally
|
||||||
|
-Dm1_chip=true
|
||||||
|
```
|
||||||
|
|
||||||
|
### Running locally(without Docker)
|
||||||
|
```
|
||||||
|
# In this mode you need to start frontend and backend services locally
|
||||||
|
-Dlocal=true
|
||||||
|
```
|
||||||
|
|
||||||
|
### Running locally(with Docker)
|
||||||
|
```
|
||||||
|
# In this mode you only need to install docker locally
|
||||||
|
```
|
||||||
|
|
||||||
|
- To run the tests locally, you need to have the DolphinScheduler running locally. You should add `dolphinscheduler-api-test/pom.xml` to the maven project
|
||||||
|
Since it does not participate in project compilation, it is not in the main project.
|
||||||
|
- Running run test class `org.apache.dolphinscheduler.api.test.cases.TenantAPITest` in the IDE.
|
||||||
|
|
||||||
|
@ -47,10 +47,12 @@ import org.junit.jupiter.api.Assertions;
|
|||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.junit.jupiter.api.Order;
|
import org.junit.jupiter.api.Order;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junitpioneer.jupiter.DisableIfTestFails;
|
||||||
|
|
||||||
//TODO: Some test cases rely on WorkflowInstance APIs. Should complete remaining cases after WorkflowInstance related API tests done.
|
//TODO: Some test cases rely on WorkflowInstance APIs. Should complete remaining cases after WorkflowInstance related API tests done.
|
||||||
@DolphinScheduler(composeFiles = "docker/basic/docker-compose.yaml")
|
@DolphinScheduler(composeFiles = "docker/basic/docker-compose.yaml")
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@DisableIfTestFails
|
||||||
public class ExecutorAPITest {
|
public class ExecutorAPITest {
|
||||||
|
|
||||||
private static final String username = "admin";
|
private static final String username = "admin";
|
||||||
|
@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You 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 org.apache.dolphinscheduler.api.test.cases;
|
||||||
|
|
||||||
|
import org.apache.dolphinscheduler.api.test.core.DolphinScheduler;
|
||||||
|
import org.apache.dolphinscheduler.api.test.entity.GetUserInfoResponseData;
|
||||||
|
import org.apache.dolphinscheduler.api.test.entity.HttpResponse;
|
||||||
|
import org.apache.dolphinscheduler.api.test.entity.LoginResponseData;
|
||||||
|
import org.apache.dolphinscheduler.api.test.pages.LoginPage;
|
||||||
|
import org.apache.dolphinscheduler.api.test.pages.security.UserPage;
|
||||||
|
import org.apache.dolphinscheduler.api.test.utils.JSONUtils;
|
||||||
|
import org.apache.dolphinscheduler.common.enums.UserType;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Order;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junitpioneer.jupiter.DisableIfTestFails;
|
||||||
|
|
||||||
|
@DolphinScheduler(composeFiles = "docker/ldap-login/docker-compose.yaml")
|
||||||
|
@Slf4j
|
||||||
|
@DisableIfTestFails
|
||||||
|
public class LdapLoginAPITest {
|
||||||
|
|
||||||
|
private static String sessionId;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(10)
|
||||||
|
public void testAdminUserLoginSuccess() {
|
||||||
|
final String username = "admin_user01";
|
||||||
|
|
||||||
|
final String password = "123";
|
||||||
|
|
||||||
|
LoginPage loginPage = new LoginPage();
|
||||||
|
HttpResponse loginHttpResponse = loginPage.login(username, password);
|
||||||
|
sessionId =
|
||||||
|
JSONUtils.convertValue(loginHttpResponse.getBody().getData(), LoginResponseData.class).getSessionId();
|
||||||
|
UserPage userPage = new UserPage();
|
||||||
|
HttpResponse getUserInfoHttpResponse = userPage.getUserInfo(sessionId);
|
||||||
|
GetUserInfoResponseData getUserInfoResponseData =
|
||||||
|
JSONUtils.convertValue(getUserInfoHttpResponse.getBody().getData(), GetUserInfoResponseData.class);
|
||||||
|
Assertions.assertEquals(username, getUserInfoResponseData.getUserName());
|
||||||
|
Assertions.assertEquals(UserType.ADMIN_USER, getUserInfoResponseData.getUserType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(20)
|
||||||
|
public void testAdminUserFilterLoginSuccess() {
|
||||||
|
final String username = "admin_user03";
|
||||||
|
|
||||||
|
final String password = "123";
|
||||||
|
|
||||||
|
LoginPage loginPage = new LoginPage();
|
||||||
|
HttpResponse loginHttpResponse = loginPage.login(username, password);
|
||||||
|
sessionId =
|
||||||
|
JSONUtils.convertValue(loginHttpResponse.getBody().getData(), LoginResponseData.class).getSessionId();
|
||||||
|
UserPage userPage = new UserPage();
|
||||||
|
HttpResponse getUserInfoHttpResponse = userPage.getUserInfo(sessionId);
|
||||||
|
GetUserInfoResponseData getUserInfoResponseData =
|
||||||
|
JSONUtils.convertValue(getUserInfoHttpResponse.getBody().getData(), GetUserInfoResponseData.class);
|
||||||
|
Assertions.assertEquals(username, getUserInfoResponseData.getUserName());
|
||||||
|
Assertions.assertEquals(UserType.ADMIN_USER, getUserInfoResponseData.getUserType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(30)
|
||||||
|
public void testGeneralUserLoginSuccess() {
|
||||||
|
final String username = "general_user02";
|
||||||
|
|
||||||
|
final String password = "123";
|
||||||
|
|
||||||
|
LoginPage loginPage = new LoginPage();
|
||||||
|
HttpResponse loginHttpResponse = loginPage.login(username, password);
|
||||||
|
sessionId =
|
||||||
|
JSONUtils.convertValue(loginHttpResponse.getBody().getData(), LoginResponseData.class).getSessionId();
|
||||||
|
UserPage userPage = new UserPage();
|
||||||
|
HttpResponse getUserInfoHttpResponse = userPage.getUserInfo(sessionId);
|
||||||
|
GetUserInfoResponseData getUserInfoResponseData =
|
||||||
|
JSONUtils.convertValue(getUserInfoHttpResponse.getBody().getData(), GetUserInfoResponseData.class);
|
||||||
|
Assertions.assertEquals(username, getUserInfoResponseData.getUserName());
|
||||||
|
Assertions.assertEquals(UserType.GENERAL_USER, getUserInfoResponseData.getUserType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(40)
|
||||||
|
public void testGeneralUserLoginFailed() {
|
||||||
|
final String username = "general_user02";
|
||||||
|
|
||||||
|
final String password = "1";
|
||||||
|
|
||||||
|
LoginPage loginPage = new LoginPage();
|
||||||
|
HttpResponse loginHttpResponse = loginPage.login(username, password);
|
||||||
|
Boolean loginResult = loginHttpResponse.getBody().getSuccess();
|
||||||
|
Assertions.assertFalse(loginResult);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You 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 org.apache.dolphinscheduler.api.test.entity;
|
||||||
|
|
||||||
|
import org.apache.dolphinscheduler.common.enums.UserType;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class GetUserInfoResponseData {
|
||||||
|
|
||||||
|
private Integer id;
|
||||||
|
private String userName;
|
||||||
|
private String userPassword;
|
||||||
|
private String email;
|
||||||
|
private Integer phone;
|
||||||
|
private UserType userType;
|
||||||
|
private Integer tenantId;
|
||||||
|
private Integer state;
|
||||||
|
private String tenantCode;
|
||||||
|
private String queueName;
|
||||||
|
private String alertGroup;
|
||||||
|
private String queue;
|
||||||
|
private String timeZone;
|
||||||
|
private Date createTime;
|
||||||
|
private Date updateTime;
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You 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 org.apache.dolphinscheduler.api.test.pages.security;
|
||||||
|
|
||||||
|
import org.apache.dolphinscheduler.api.test.core.Constants;
|
||||||
|
import org.apache.dolphinscheduler.api.test.entity.HttpResponse;
|
||||||
|
import org.apache.dolphinscheduler.api.test.utils.RequestClient;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class UserPage {
|
||||||
|
|
||||||
|
public HttpResponse getUserInfo(String sessionId) {
|
||||||
|
Map<String, String> headers = new HashMap<>();
|
||||||
|
headers.put(Constants.SESSION_ID_KEY, sessionId);
|
||||||
|
|
||||||
|
RequestClient requestClient = new RequestClient();
|
||||||
|
|
||||||
|
return requestClient.get("/users/get-user-info", headers, new HashMap<>());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,321 @@
|
|||||||
|
#
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
# contributor license agreements. See the NOTICE file distributed with
|
||||||
|
# this work for additional information regarding copyright ownership.
|
||||||
|
# The ASF licenses this file to You 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
spring:
|
||||||
|
profiles:
|
||||||
|
active: h2
|
||||||
|
jackson:
|
||||||
|
time-zone: UTC
|
||||||
|
date-format: "yyyy-MM-dd HH:mm:ss"
|
||||||
|
banner:
|
||||||
|
charset: UTF-8
|
||||||
|
location: classpath:standalone-banner.txt
|
||||||
|
sql:
|
||||||
|
init:
|
||||||
|
schema-locations: classpath:sql/dolphinscheduler_h2.sql
|
||||||
|
datasource:
|
||||||
|
driver-class-name: org.h2.Driver
|
||||||
|
url: jdbc:h2:mem:dolphinscheduler;MODE=MySQL;DB_CLOSE_DELAY=-1;DATABASE_TO_LOWER=true
|
||||||
|
username: sa
|
||||||
|
password: ""
|
||||||
|
quartz:
|
||||||
|
job-store-type: jdbc
|
||||||
|
jdbc:
|
||||||
|
initialize-schema: never
|
||||||
|
properties:
|
||||||
|
org.quartz.threadPool.threadPriority: 5
|
||||||
|
org.quartz.jobStore.isClustered: true
|
||||||
|
org.quartz.jobStore.class: org.springframework.scheduling.quartz.LocalDataSourceJobStore
|
||||||
|
org.quartz.scheduler.instanceId: AUTO
|
||||||
|
org.quartz.jobStore.tablePrefix: QRTZ_
|
||||||
|
org.quartz.jobStore.acquireTriggersWithinLock: true
|
||||||
|
org.quartz.scheduler.instanceName: DolphinScheduler
|
||||||
|
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
|
||||||
|
org.quartz.jobStore.useProperties: false
|
||||||
|
org.quartz.threadPool.makeThreadsDaemons: true
|
||||||
|
org.quartz.threadPool.threadCount: 25
|
||||||
|
org.quartz.jobStore.misfireThreshold: 60000
|
||||||
|
org.quartz.scheduler.makeSchedulerThreadDaemon: true
|
||||||
|
org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
|
||||||
|
org.quartz.jobStore.clusterCheckinInterval: 5000
|
||||||
|
org.quartz.scheduler.batchTriggerAcquisitionMaxCount: 1
|
||||||
|
servlet:
|
||||||
|
multipart:
|
||||||
|
max-file-size: 1024MB
|
||||||
|
max-request-size: 1024MB
|
||||||
|
messages:
|
||||||
|
basename: i18n/messages
|
||||||
|
jpa:
|
||||||
|
hibernate:
|
||||||
|
ddl-auto: none
|
||||||
|
mvc:
|
||||||
|
pathmatch:
|
||||||
|
matching-strategy: ANT_PATH_MATCHER
|
||||||
|
cloud.discovery.client.composite-indicator.enabled: false
|
||||||
|
|
||||||
|
mybatis-plus:
|
||||||
|
mapper-locations: classpath:org/apache/dolphinscheduler/dao/mapper/*Mapper.xml
|
||||||
|
type-aliases-package: org.apache.dolphinscheduler.dao.entity
|
||||||
|
configuration:
|
||||||
|
cache-enabled: false
|
||||||
|
call-setters-on-nulls: true
|
||||||
|
map-underscore-to-camel-case: true
|
||||||
|
jdbc-type-for-null: NULL
|
||||||
|
global-config:
|
||||||
|
db-config:
|
||||||
|
id-type: auto
|
||||||
|
banner: false
|
||||||
|
|
||||||
|
registry:
|
||||||
|
type: jdbc
|
||||||
|
|
||||||
|
security:
|
||||||
|
authentication:
|
||||||
|
# Authentication types (supported types: PASSWORD,LDAP,CASDOOR_SSO)
|
||||||
|
type: LDAP
|
||||||
|
# IF you set type `LDAP`, below config will be effective
|
||||||
|
ldap:
|
||||||
|
# ldap server config
|
||||||
|
url: ldap://openldap:1389/
|
||||||
|
base-dn: ou=users,dc=example,dc=org
|
||||||
|
username: cn=admin,dc=example,dc=org
|
||||||
|
password: adminpassword
|
||||||
|
user:
|
||||||
|
# admin username when you use LDAP login
|
||||||
|
admin-username: admin_user01
|
||||||
|
admin-user-filter: (&(cn={0})(sn=Bar3))
|
||||||
|
identity-attribute: cn
|
||||||
|
email-attribute: uid
|
||||||
|
# action when ldap user is not exist (supported types: CREATE,DENY)
|
||||||
|
not-exist-action: CREATE
|
||||||
|
ssl:
|
||||||
|
enable: false
|
||||||
|
# jks file absolute path && password
|
||||||
|
trust-store: "/ldapkeystore.jks"
|
||||||
|
trust-store-password: ""
|
||||||
|
casdoor:
|
||||||
|
user:
|
||||||
|
admin: admin
|
||||||
|
oauth2:
|
||||||
|
enable: false
|
||||||
|
provider:
|
||||||
|
github:
|
||||||
|
authorizationUri: "https://github.com/login/oauth/authorize"
|
||||||
|
redirectUri: "http://localhost:12345/dolphinscheduler/redirect/login/oauth2"
|
||||||
|
clientId: ""
|
||||||
|
clientSecret: ""
|
||||||
|
tokenUri: "https://github.com/login/oauth/access_token"
|
||||||
|
userInfoUri: "https://api.github.com/user"
|
||||||
|
callbackUrl: "http://localhost:5173/login"
|
||||||
|
iconUri: ""
|
||||||
|
provider: github
|
||||||
|
gitee:
|
||||||
|
authorizationUri: "https://gitee.com/oauth/authorize"
|
||||||
|
redirectUri: "http://127.0.0.1:12345/dolphinscheduler/redirect/login/oauth2"
|
||||||
|
clientId: ""
|
||||||
|
clientSecret: ""
|
||||||
|
tokenUri: "https://gitee.com/oauth/token?grant_type=authorization_code"
|
||||||
|
userInfoUri: "https://gitee.com/api/v5/user"
|
||||||
|
callbackUrl: "http://127.0.0.1:5173/login"
|
||||||
|
iconUri: ""
|
||||||
|
provider: gitee
|
||||||
|
|
||||||
|
casdoor:
|
||||||
|
# Your Casdoor server url
|
||||||
|
endpoint: http://localhost:8000
|
||||||
|
client-id: ""
|
||||||
|
client-secret: ""
|
||||||
|
# The certificate may be multi-line, you can use `|-` for ease
|
||||||
|
certificate: ""
|
||||||
|
# Your organization name added in Casdoor
|
||||||
|
organization-name: built-in
|
||||||
|
# Your application name added in Casdoor
|
||||||
|
application-name: dolphinscheduler
|
||||||
|
# Doplhinscheduler login url
|
||||||
|
redirect-url: http://localhost:5173/login
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
master:
|
||||||
|
listen-port: 5678
|
||||||
|
# master heartbeat interval
|
||||||
|
max-heartbeat-interval: 10s
|
||||||
|
server-load-protection:
|
||||||
|
enabled: true
|
||||||
|
# Master max system cpu usage, when the master's system cpu usage is smaller then this value, master server can execute workflow.
|
||||||
|
max-system-cpu-usage-percentage-thresholds: 1
|
||||||
|
# Master max jvm cpu usage, when the master's jvm cpu usage is smaller then this value, master server can execute workflow.
|
||||||
|
max-jvm-cpu-usage-percentage-thresholds: 0.9
|
||||||
|
# Master max System memory usage , when the master's system memory usage is smaller then this value, master server can execute workflow.
|
||||||
|
max-system-memory-usage-percentage-thresholds: 0.9
|
||||||
|
# Master max disk usage , when the master's disk usage is smaller then this value, master server can execute workflow.
|
||||||
|
max-disk-usage-percentage-thresholds: 0.9
|
||||||
|
worker-load-balancer-configuration-properties:
|
||||||
|
# RANDOM, ROUND_ROBIN, FIXED_WEIGHTED_ROUND_ROBIN, DYNAMIC_WEIGHTED_ROUND_ROBIN
|
||||||
|
type: DYNAMIC_WEIGHTED_ROUND_ROBIN
|
||||||
|
# dynamic-weight-config-properties only used in DYNAMIC_WEIGHTED_ROUND_ROBIN, the weight of memory-usage, cpu-usage, task-thread-pool-usage should sum to 100.
|
||||||
|
dynamic-weight-config-properties:
|
||||||
|
memory-usage-weight: 30
|
||||||
|
cpu-usage-weight: 30
|
||||||
|
task-thread-pool-usage-weight: 40
|
||||||
|
worker-group-refresh-interval: 10s
|
||||||
|
command-fetch-strategy:
|
||||||
|
type: ID_SLOT_BASED
|
||||||
|
config:
|
||||||
|
# The incremental id step
|
||||||
|
id-step: 1
|
||||||
|
# master fetch command num
|
||||||
|
fetch-size: 10
|
||||||
|
|
||||||
|
worker:
|
||||||
|
# worker listener port
|
||||||
|
listen-port: 1234
|
||||||
|
# worker execute thread number to limit task instances in parallel
|
||||||
|
exec-threads: 10
|
||||||
|
# worker heartbeat interval
|
||||||
|
max-heartbeat-interval: 10s
|
||||||
|
# worker host weight to dispatch tasks, default value 100
|
||||||
|
host-weight: 100
|
||||||
|
server-load-protection:
|
||||||
|
enabled: true
|
||||||
|
# Worker max system cpu usage, when the worker's system cpu usage is smaller then this value, worker server can be dispatched tasks.
|
||||||
|
max-system-cpu-usage-percentage-thresholds: 1
|
||||||
|
# Worker max jvm cpu usage, when the worker's jvm cpu usage is smaller then this value, worker server can be dispatched tasks.
|
||||||
|
max-jvm-cpu-usage-percentage-thresholds: 0.9
|
||||||
|
# Worker max System memory usage , when the worker's system memory usage is smaller then this value, worker server can be dispatched tasks.
|
||||||
|
max-system-memory-usage-percentage-thresholds: 0.9
|
||||||
|
# Worker max disk usage , when the worker's disk usage is smaller then this value, worker server can be dispatched tasks.
|
||||||
|
max-disk-usage-percentage-thresholds: 0.9
|
||||||
|
task-execute-threads-full-policy: REJECT
|
||||||
|
tenant-config:
|
||||||
|
# tenant corresponds to the user of the system, which is used by the worker to submit the job. If system does not have this user, it will be automatically created after the parameter worker.tenant.auto.create is true.
|
||||||
|
auto-create-tenant-enabled: true
|
||||||
|
# Scenes to be used for distributed users. For example, users created by FreeIpa are stored in LDAP. This parameter only applies to Linux, When this parameter is true, worker.tenant.auto.create has no effect and will not automatically create tenants.
|
||||||
|
distributed-tenant: false
|
||||||
|
# If set true, will use worker bootstrap user as the tenant to execute task when the tenant is `default`;
|
||||||
|
default-tenant-enabled: true
|
||||||
|
|
||||||
|
alert:
|
||||||
|
port: 50052
|
||||||
|
# Mark each alert of alert server if late after x milliseconds as failed.
|
||||||
|
# Define value is (0 = infinite), and alert server would be waiting alert result.
|
||||||
|
wait-timeout: 0
|
||||||
|
max-heartbeat-interval: 60s
|
||||||
|
# The maximum number of alerts that can be processed in parallel
|
||||||
|
sender-parallelism: 5
|
||||||
|
|
||||||
|
api:
|
||||||
|
audit-enable: false
|
||||||
|
# Traffic control, if you turn on this config, the maximum number of request/s will be limited.
|
||||||
|
# global max request number per second
|
||||||
|
# default tenant-level max request number
|
||||||
|
traffic-control:
|
||||||
|
global-switch: false
|
||||||
|
max-global-qps-rate: 300
|
||||||
|
tenant-switch: false
|
||||||
|
default-tenant-qps-rate: 10
|
||||||
|
#customize-tenant-qps-rate:
|
||||||
|
# eg.
|
||||||
|
#tenant1: 11
|
||||||
|
#tenant2: 20
|
||||||
|
python-gateway:
|
||||||
|
# Weather enable python gateway server or not. The default value is true.
|
||||||
|
enabled: true
|
||||||
|
# Authentication token for connection from python api to python gateway server. Should be changed the default value
|
||||||
|
# when you deploy in public network.
|
||||||
|
auth-token: jwUDzpLsNKEFER4*a8gruBH_GsAurNxU7A@Xc
|
||||||
|
# The address of Python gateway server start. Set its value to `0.0.0.0` if your Python API run in different
|
||||||
|
# between Python gateway server. It could be be specific to other address like `127.0.0.1` or `localhost`
|
||||||
|
gateway-server-address: 0.0.0.0
|
||||||
|
# The port of Python gateway server start. Define which port you could connect to Python gateway server from
|
||||||
|
# Python API side.
|
||||||
|
gateway-server-port: 25333
|
||||||
|
# The address of Python callback client.
|
||||||
|
python-address: 127.0.0.1
|
||||||
|
# The port of Python callback client.
|
||||||
|
python-port: 25334
|
||||||
|
# Close connection of socket server if no other request accept after x milliseconds. Define value is (0 = infinite),
|
||||||
|
# and socket server would never close even though no requests accept
|
||||||
|
connect-timeout: 0
|
||||||
|
# Close each active connection of socket server if python program not active after x milliseconds. Define value is
|
||||||
|
# (0 = infinite), and socket server would never close even though no requests accept
|
||||||
|
read-timeout: 0
|
||||||
|
|
||||||
|
server:
|
||||||
|
port: 12345
|
||||||
|
servlet:
|
||||||
|
session:
|
||||||
|
timeout: 120m
|
||||||
|
context-path: /dolphinscheduler/
|
||||||
|
compression:
|
||||||
|
enabled: true
|
||||||
|
mime-types: text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json,application/xml
|
||||||
|
jetty:
|
||||||
|
max-http-form-post-size: 5000000
|
||||||
|
accesslog:
|
||||||
|
enabled: true
|
||||||
|
custom-format: '%{client}a - %u %t "%r" %s %O %{ms}Tms'
|
||||||
|
|
||||||
|
management:
|
||||||
|
endpoints:
|
||||||
|
web:
|
||||||
|
exposure:
|
||||||
|
include: health,metrics,prometheus
|
||||||
|
endpoint:
|
||||||
|
health:
|
||||||
|
enabled: true
|
||||||
|
show-details: always
|
||||||
|
health:
|
||||||
|
db:
|
||||||
|
enabled: true
|
||||||
|
defaults:
|
||||||
|
enabled: false
|
||||||
|
metrics:
|
||||||
|
tags:
|
||||||
|
application: ${spring.application.name}
|
||||||
|
|
||||||
|
metrics:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Override by profile
|
||||||
|
---
|
||||||
|
spring:
|
||||||
|
config:
|
||||||
|
activate:
|
||||||
|
on-profile: postgresql
|
||||||
|
quartz:
|
||||||
|
properties:
|
||||||
|
org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
|
||||||
|
datasource:
|
||||||
|
driver-class-name: org.postgresql.Driver
|
||||||
|
url: jdbc:postgresql://127.0.0.1:5432/dolphinscheduler
|
||||||
|
username: root
|
||||||
|
password: root
|
||||||
|
|
||||||
|
---
|
||||||
|
spring:
|
||||||
|
config:
|
||||||
|
activate:
|
||||||
|
on-profile: mysql
|
||||||
|
sql:
|
||||||
|
init:
|
||||||
|
schema-locations: classpath:sql/dolphinscheduler_mysql.sql
|
||||||
|
datasource:
|
||||||
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
url: jdbc:mysql://127.0.0.1:3306/dolphinscheduler?useUnicode=true&characterEncoding=UTF-8
|
||||||
|
username: root
|
||||||
|
password: root
|
@ -0,0 +1,63 @@
|
|||||||
|
#
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
# contributor license agreements. See the NOTICE file distributed with
|
||||||
|
# this work for additional information regarding copyright ownership.
|
||||||
|
# The ASF licenses this file to You 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
dolphinscheduler:
|
||||||
|
image: apache/dolphinscheduler-standalone-server:ci
|
||||||
|
environment:
|
||||||
|
WORKER_TENANT_AUTO_CREATE: 'true'
|
||||||
|
ports:
|
||||||
|
- "12345:12345"
|
||||||
|
volumes:
|
||||||
|
- ./application.yaml:/opt/dolphinscheduler/standalone-server/conf/application.yaml
|
||||||
|
networks:
|
||||||
|
- api-test
|
||||||
|
healthcheck:
|
||||||
|
test: [ "CMD", "curl", "http://localhost:12345/dolphinscheduler/actuator/health" ]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 60s
|
||||||
|
retries: 120
|
||||||
|
depends_on:
|
||||||
|
- openldap
|
||||||
|
openldap:
|
||||||
|
hostname: openldap
|
||||||
|
image: bitnami/openldap:2.6
|
||||||
|
ports:
|
||||||
|
- '1389:1389'
|
||||||
|
- '1636:1636'
|
||||||
|
environment:
|
||||||
|
- LDAP_ADMIN_USERNAME=admin
|
||||||
|
- LDAP_ADMIN_PASSWORD=adminpassword
|
||||||
|
- LDAP_USERS=admin_user01,general_user02,admin_user03
|
||||||
|
- LDAP_PASSWORDS=123,123,123
|
||||||
|
- LDAP_ROOT=dc=example,dc=org
|
||||||
|
- LDAP_ADMIN_DN=cn=admin,dc=example,dc=org
|
||||||
|
networks:
|
||||||
|
- api-test
|
||||||
|
tty: true
|
||||||
|
stdin_open: true
|
||||||
|
restart: always
|
||||||
|
healthcheck:
|
||||||
|
test: ldapsearch -x -H 'ldap://127.0.0.1:1389' -D 'cn=admin,dc=example,dc=org' -w adminpassword -b 'ou=users,dc=example,dc=org' '(cn=admin_user01)'
|
||||||
|
interval: 5s
|
||||||
|
timeout: 60s
|
||||||
|
retries: 120
|
||||||
|
|
||||||
|
networks:
|
||||||
|
api-test:
|
@ -36,7 +36,8 @@
|
|||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
|
||||||
<junit.version>5.7.2</junit.version>
|
<junit.version>5.7.2</junit.version>
|
||||||
<selenium.version>3.141.59</selenium.version>
|
<selenium.version>4.21.0</selenium.version>
|
||||||
|
<testcontainers.version>1.19.8</testcontainers.version>
|
||||||
<lombok.version>1.18.24</lombok.version>
|
<lombok.version>1.18.24</lombok.version>
|
||||||
<assertj-core.version>3.23.1</assertj-core.version>
|
<assertj-core.version>3.23.1</assertj-core.version>
|
||||||
<awaitility.version>4.1.0</awaitility.version>
|
<awaitility.version>4.1.0</awaitility.version>
|
||||||
@ -133,7 +134,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.testcontainers</groupId>
|
<groupId>org.testcontainers</groupId>
|
||||||
<artifactId>testcontainers-bom</artifactId>
|
<artifactId>testcontainers-bom</artifactId>
|
||||||
<version>1.18.1</version>
|
<version>${testcontainers.version}</version>
|
||||||
<scope>import</scope>
|
<scope>import</scope>
|
||||||
<type>pom</type>
|
<type>pom</type>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You 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 org.apache.dolphinscheduler.api.dto;
|
||||||
|
|
||||||
|
import org.apache.dolphinscheduler.common.enums.UserType;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class LdapLoginResult {
|
||||||
|
|
||||||
|
private boolean success;
|
||||||
|
private String ldapEmail;
|
||||||
|
private UserType userType;
|
||||||
|
private String userName;
|
||||||
|
}
|
@ -59,16 +59,16 @@ public abstract class AbstractAuthenticator implements Authenticator {
|
|||||||
/**
|
/**
|
||||||
* user login and return user in db
|
* user login and return user in db
|
||||||
*
|
*
|
||||||
* @param userId user identity field
|
* @param userName user identity field
|
||||||
* @param password user login password
|
* @param password user login password
|
||||||
* @return user object in databse
|
* @return user object in databse
|
||||||
*/
|
*/
|
||||||
public abstract User login(@NonNull String userId, String password);
|
public abstract User login(@NonNull String userName, String password);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result<Map<String, String>> authenticate(@NonNull String userId, String password, @NonNull String ip) {
|
public Result<Map<String, String>> authenticate(@NonNull String userName, String password, @NonNull String ip) {
|
||||||
Result<Map<String, String>> result = new Result<>();
|
Result<Map<String, String>> result = new Result<>();
|
||||||
User user = login(userId, password);
|
User user = login(userName, password);
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
if (Objects.equals(securityConfig.getType(), AuthenticationType.CASDOOR_SSO.name())) {
|
if (Objects.equals(securityConfig.getType(), AuthenticationType.CASDOOR_SSO.name())) {
|
||||||
log.error("State or code entered incorrectly.");
|
log.error("State or code entered incorrectly.");
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
package org.apache.dolphinscheduler.api.security.impl.ldap;
|
package org.apache.dolphinscheduler.api.security.impl.ldap;
|
||||||
|
|
||||||
|
import org.apache.dolphinscheduler.api.dto.LdapLoginResult;
|
||||||
import org.apache.dolphinscheduler.api.security.impl.AbstractAuthenticator;
|
import org.apache.dolphinscheduler.api.security.impl.AbstractAuthenticator;
|
||||||
import org.apache.dolphinscheduler.dao.entity.User;
|
import org.apache.dolphinscheduler.dao.entity.User;
|
||||||
|
|
||||||
@ -30,14 +31,14 @@ public class LdapAuthenticator extends AbstractAuthenticator {
|
|||||||
LdapService ldapService;
|
LdapService ldapService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public User login(@NonNull String userId, String password) {
|
public User login(@NonNull String userName, String password) {
|
||||||
User user = null;
|
User user = null;
|
||||||
String ldapEmail = ldapService.ldapLogin(userId, password);
|
LdapLoginResult ldapLoginResult = ldapService.ldapLogin(userName, password);
|
||||||
if (ldapEmail != null) {
|
if (ldapLoginResult.isSuccess()) {
|
||||||
// check if user exist
|
// check if user exist
|
||||||
user = userService.getUserByUserName(userId);
|
user = userService.getUserByUserName(userName);
|
||||||
if (user == null && ldapService.createIfUserNotExists()) {
|
if (user == null && ldapService.createIfUserNotExists()) {
|
||||||
user = userService.createUser(ldapService.getUserType(userId), userId, ldapEmail);
|
user = userService.createUser(ldapLoginResult.getUserType(), userName, ldapLoginResult.getLdapEmail());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return user;
|
return user;
|
||||||
|
@ -17,11 +17,13 @@
|
|||||||
|
|
||||||
package org.apache.dolphinscheduler.api.security.impl.ldap;
|
package org.apache.dolphinscheduler.api.security.impl.ldap;
|
||||||
|
|
||||||
|
import org.apache.dolphinscheduler.api.dto.LdapLoginResult;
|
||||||
import org.apache.dolphinscheduler.api.security.LdapUserNotExistActionType;
|
import org.apache.dolphinscheduler.api.security.LdapUserNotExistActionType;
|
||||||
import org.apache.dolphinscheduler.common.enums.UserType;
|
import org.apache.dolphinscheduler.common.enums.UserType;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
import javax.naming.Context;
|
import javax.naming.Context;
|
||||||
@ -46,11 +48,14 @@ import org.springframework.stereotype.Component;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class LdapService {
|
public class LdapService {
|
||||||
|
|
||||||
@Value("${security.authentication.ldap.user.admin:#{null}}")
|
@Value("${security.authentication.ldap.user.admin-username:#{null}}")
|
||||||
private String adminUserId;
|
private String ldapAdminUserName;
|
||||||
|
|
||||||
@Value("${security.authentication.ldap.urls:#{null}}")
|
@Value("${security.authentication.ldap.user.admin-user-filter:#{null}}")
|
||||||
private String ldapUrls;
|
private String ldapAdminUserFilter;
|
||||||
|
|
||||||
|
@Value("${security.authentication.ldap.url:#{null}}")
|
||||||
|
private String ldapUrl;
|
||||||
|
|
||||||
@Value("${security.authentication.ldap.base-dn:#{null}}")
|
@Value("${security.authentication.ldap.base-dn:#{null}}")
|
||||||
private String ldapBaseDn;
|
private String ldapBaseDn;
|
||||||
@ -67,7 +72,7 @@ public class LdapService {
|
|||||||
@Value("${security.authentication.ldap.user.email-attribute:#{null}}")
|
@Value("${security.authentication.ldap.user.email-attribute:#{null}}")
|
||||||
private String ldapEmailAttribute;
|
private String ldapEmailAttribute;
|
||||||
|
|
||||||
@Value("${security.authentication.ldap.user.not-exist-action:CREATE}")
|
@Value("${security.authentication.ldap.user.not-exist-action:DENY}")
|
||||||
private String ldapUserNotExistAction;
|
private String ldapUserNotExistAction;
|
||||||
|
|
||||||
@Value("${security.authentication.ldap.ssl.enable:false}")
|
@Value("${security.authentication.ldap.ssl.enable:false}")
|
||||||
@ -79,33 +84,105 @@ public class LdapService {
|
|||||||
@Value("${security.authentication.ldap.ssl.trust-store-password:#{null}}")
|
@Value("${security.authentication.ldap.ssl.trust-store-password:#{null}}")
|
||||||
private String trustStorePassword;
|
private String trustStorePassword;
|
||||||
|
|
||||||
/***
|
|
||||||
* get user type by configured admin userId
|
|
||||||
* @param userId login userId
|
|
||||||
* @return user type
|
|
||||||
*/
|
|
||||||
public UserType getUserType(String userId) {
|
|
||||||
return adminUserId.equalsIgnoreCase(userId) ? UserType.ADMIN_USER : UserType.GENERAL_USER;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* login by userId and return user email
|
* login by userName and return LdapLoginResult
|
||||||
*
|
|
||||||
* @param userId user identity id
|
|
||||||
* @param userPwd user login password
|
|
||||||
* @return user email
|
|
||||||
*/
|
*/
|
||||||
public String ldapLogin(String userId, String userPwd) {
|
public LdapLoginResult ldapLogin(String userName, String userPwd) {
|
||||||
Properties searchEnv = getManagerLdapEnv();
|
Properties searchEnv = getManagerLdapEnv();
|
||||||
LdapContext ctx = null;
|
LdapContext ctx = null;
|
||||||
|
LdapLoginResult ldapLoginResult = new LdapLoginResult();
|
||||||
|
ldapLoginResult.setSuccess(false);
|
||||||
|
if (StringUtils.isEmpty(ldapEmailAttribute)) {
|
||||||
|
log.warn("ldap email attribute is empty, skipping ldap authentication");
|
||||||
|
return ldapLoginResult;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Connect to the LDAP server and Authenticate with a service user of whom we know the DN and credentials
|
// Connect to the LDAP server and Authenticate with a service user of whom we know the DN and credentials
|
||||||
ctx = new InitialLdapContext(searchEnv, null);
|
ctx = new InitialLdapContext(searchEnv, null);
|
||||||
SearchControls sc = new SearchControls();
|
SearchControls sc = new SearchControls();
|
||||||
sc.setReturningAttributes(new String[]{ldapEmailAttribute});
|
sc.setReturningAttributes(new String[]{ldapEmailAttribute});
|
||||||
sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
|
sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
|
||||||
EqualsFilter filter = new EqualsFilter(ldapUserIdentifyingAttribute, userId);
|
EqualsFilter userFilter = new EqualsFilter(ldapUserIdentifyingAttribute, userName);
|
||||||
NamingEnumeration<SearchResult> results = ctx.search(ldapBaseDn, filter.toString(), sc);
|
String userSearchEmail = ldapSearch(ctx, null, userPwd, userFilter.toString(), sc, searchEnv);
|
||||||
|
|
||||||
|
if (StringUtils.isNotEmpty(ldapAdminUserFilter)) {
|
||||||
|
String adminFilterSearchEmail = ldapSearch(ctx, userName, userPwd, ldapAdminUserFilter, sc, searchEnv);
|
||||||
|
if (adminFilterSearchEmail != null) {
|
||||||
|
ldapLoginResult.setLdapEmail(adminFilterSearchEmail);
|
||||||
|
ldapLoginResult.setUserType(UserType.ADMIN_USER);
|
||||||
|
ldapLoginResult.setUserName(userName);
|
||||||
|
ldapLoginResult.setSuccess(true);
|
||||||
|
return ldapLoginResult;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.debug("ldap admin user filter is empty, skipping admin user filter search");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userSearchEmail != null) {
|
||||||
|
if (Objects.equals(ldapAdminUserName, userName)) {
|
||||||
|
ldapLoginResult.setUserType(UserType.ADMIN_USER);
|
||||||
|
} else {
|
||||||
|
ldapLoginResult.setUserType(UserType.GENERAL_USER);
|
||||||
|
}
|
||||||
|
|
||||||
|
ldapLoginResult.setLdapEmail(userSearchEmail);
|
||||||
|
ldapLoginResult.setUserName(userName);
|
||||||
|
ldapLoginResult.setSuccess(true);
|
||||||
|
return ldapLoginResult;
|
||||||
|
} else {
|
||||||
|
log.debug("user email attribute {} not found in ldap", ldapEmailAttribute);
|
||||||
|
}
|
||||||
|
} catch (NamingException e) {
|
||||||
|
log.error("ldap search error", e);
|
||||||
|
return ldapLoginResult;
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (ctx != null) {
|
||||||
|
ctx.close();
|
||||||
|
}
|
||||||
|
} catch (NamingException e) {
|
||||||
|
log.error("ldap context close error", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ldapLoginResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* get ldap env fot ldap server search
|
||||||
|
* @return Properties
|
||||||
|
*/
|
||||||
|
Properties getManagerLdapEnv() {
|
||||||
|
Properties env = new Properties();
|
||||||
|
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
|
||||||
|
env.put(Context.SECURITY_AUTHENTICATION, "simple");
|
||||||
|
env.put(Context.SECURITY_PRINCIPAL, ldapSecurityPrincipal);
|
||||||
|
env.put(Context.SECURITY_CREDENTIALS, ldapPrincipalPassword);
|
||||||
|
env.put(Context.PROVIDER_URL, ldapUrl);
|
||||||
|
|
||||||
|
if (sslEnable) {
|
||||||
|
env.put(Context.SECURITY_PROTOCOL, "ssl");
|
||||||
|
System.setProperty("javax.net.ssl.trustStore", trustStore);
|
||||||
|
if (StringUtils.isNotEmpty(trustStorePassword)) {
|
||||||
|
System.setProperty("javax.net.ssl.trustStorePassword", trustStorePassword);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return env;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String ldapSearch(LdapContext ctx,
|
||||||
|
String userName,
|
||||||
|
String userPwd,
|
||||||
|
String filter,
|
||||||
|
SearchControls sc,
|
||||||
|
Properties searchEnv) throws NamingException {
|
||||||
|
NamingEnumeration<SearchResult> results;
|
||||||
|
if (userName == null) {
|
||||||
|
results = ctx.search(ldapBaseDn, filter, sc);
|
||||||
|
} else {
|
||||||
|
results = ctx.search(ldapBaseDn, filter, new Object[]{userName}, sc);
|
||||||
|
}
|
||||||
if (results.hasMore()) {
|
if (results.hasMore()) {
|
||||||
// get the users DN (distinguishedName) from the result
|
// get the users DN (distinguishedName) from the result
|
||||||
SearchResult result = results.next();
|
SearchResult result = results.next();
|
||||||
@ -126,44 +203,10 @@ public class LdapService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (NamingException e) {
|
|
||||||
log.error("ldap search error", e);
|
|
||||||
return null;
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
if (ctx != null) {
|
|
||||||
ctx.close();
|
|
||||||
}
|
|
||||||
} catch (NamingException e) {
|
|
||||||
log.error("ldap context close error", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
|
||||||
* get ldap env fot ldap server search
|
|
||||||
* @return Properties
|
|
||||||
*/
|
|
||||||
Properties getManagerLdapEnv() {
|
|
||||||
Properties env = new Properties();
|
|
||||||
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
|
|
||||||
env.put(Context.SECURITY_AUTHENTICATION, "simple");
|
|
||||||
env.put(Context.SECURITY_PRINCIPAL, ldapSecurityPrincipal);
|
|
||||||
env.put(Context.SECURITY_CREDENTIALS, ldapPrincipalPassword);
|
|
||||||
env.put(Context.PROVIDER_URL, ldapUrls);
|
|
||||||
|
|
||||||
if (sslEnable) {
|
|
||||||
env.put(Context.SECURITY_PROTOCOL, "ssl");
|
|
||||||
System.setProperty("javax.net.ssl.trustStore", trustStore);
|
|
||||||
if (StringUtils.isNotEmpty(trustStorePassword)) {
|
|
||||||
System.setProperty("javax.net.ssl.trustStorePassword", trustStorePassword);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return env;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LdapUserNotExistActionType getLdapUserNotExistAction() {
|
public LdapUserNotExistActionType getLdapUserNotExistAction() {
|
||||||
if (StringUtils.isBlank(ldapUserNotExistAction)) {
|
if (StringUtils.isBlank(ldapUserNotExistAction)) {
|
||||||
log.info(
|
log.info(
|
||||||
|
@ -23,7 +23,7 @@ import org.apache.dolphinscheduler.dao.entity.User;
|
|||||||
public class PasswordAuthenticator extends AbstractAuthenticator {
|
public class PasswordAuthenticator extends AbstractAuthenticator {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public User login(String userId, String password) {
|
public User login(String userName, String password) {
|
||||||
return userService.queryUser(userId, password);
|
return userService.queryUser(userName, password);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ public class CasdoorAuthenticator extends AbstractSsoAuthenticator {
|
|||||||
private String adminUserName;
|
private String adminUserName;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public User login(@NonNull String state, String code) {
|
public User login(@NonNull String userName, String code) {
|
||||||
ServletRequestAttributes servletRequestAttributes =
|
ServletRequestAttributes servletRequestAttributes =
|
||||||
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||||
if (servletRequestAttributes == null) {
|
if (servletRequestAttributes == null) {
|
||||||
@ -59,11 +59,11 @@ public class CasdoorAuthenticator extends AbstractSsoAuthenticator {
|
|||||||
// Invalid state
|
// Invalid state
|
||||||
request.getSession().setAttribute(Constants.SSO_LOGIN_USER_STATE, null);
|
request.getSession().setAttribute(Constants.SSO_LOGIN_USER_STATE, null);
|
||||||
// Check state to protect from CSRF attack
|
// Check state to protect from CSRF attack
|
||||||
if (originalState == null || !MessageDigest.isEqual(originalState.getBytes(), state.getBytes())) {
|
if (originalState == null || !MessageDigest.isEqual(originalState.getBytes(), userName.getBytes())) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
String token = casdoorAuthService.getOAuthToken(code, state);
|
String token = casdoorAuthService.getOAuthToken(code, userName);
|
||||||
CasdoorUser casdoorUser = casdoorAuthService.parseJwtToken(token);
|
CasdoorUser casdoorUser = casdoorAuthService.parseJwtToken(token);
|
||||||
User user = null;
|
User user = null;
|
||||||
if (casdoorUser.getName() != null) {
|
if (casdoorUser.getName() != null) {
|
||||||
|
@ -175,17 +175,19 @@ security:
|
|||||||
# IF you set type `LDAP`, below config will be effective
|
# IF you set type `LDAP`, below config will be effective
|
||||||
ldap:
|
ldap:
|
||||||
# ldap server config
|
# ldap server config
|
||||||
urls: ldap://ldap.forumsys.com:389/
|
url: ldap://ldap.forumsys.com:389/
|
||||||
base-dn: dc=example,dc=com
|
base-dn: dc=example,dc=com
|
||||||
username: cn=read-only-admin,dc=example,dc=com
|
username: cn=admin,dc=example,dc=com
|
||||||
password: password
|
password: password
|
||||||
user:
|
user:
|
||||||
# admin userId when you use LDAP login
|
# admin username when you use LDAP login
|
||||||
admin: read-only-admin
|
admin-username: ldap-admin
|
||||||
|
# user search filter to find admin user
|
||||||
|
admin-user-filter: (&(cn={0}))
|
||||||
identity-attribute: uid
|
identity-attribute: uid
|
||||||
email-attribute: mail
|
email-attribute: mail
|
||||||
# action when ldap user is not exist (supported types: CREATE,DENY)
|
# action when ldap user is not exist (supported types: CREATE,DENY)
|
||||||
not-exist-action: CREATE
|
not-exist-action: DENY
|
||||||
ssl:
|
ssl:
|
||||||
enable: false
|
enable: false
|
||||||
# jks file absolute path && password
|
# jks file absolute path && password
|
||||||
|
@ -27,6 +27,7 @@ import org.springframework.test.context.TestPropertySource;
|
|||||||
|
|
||||||
@TestPropertySource(properties = {
|
@TestPropertySource(properties = {
|
||||||
"security.authentication.type=LDAP",
|
"security.authentication.type=LDAP",
|
||||||
|
"security.authentication.ldap.user.not-exist-action=CREATE"
|
||||||
})
|
})
|
||||||
public class SecurityConfigLDAPTest extends AbstractControllerTest {
|
public class SecurityConfigLDAPTest extends AbstractControllerTest {
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ import static org.mockito.ArgumentMatchers.any;
|
|||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import org.apache.dolphinscheduler.api.controller.AbstractControllerTest;
|
import org.apache.dolphinscheduler.api.controller.AbstractControllerTest;
|
||||||
|
import org.apache.dolphinscheduler.api.dto.LdapLoginResult;
|
||||||
import org.apache.dolphinscheduler.api.enums.Status;
|
import org.apache.dolphinscheduler.api.enums.Status;
|
||||||
import org.apache.dolphinscheduler.api.security.LdapUserNotExistActionType;
|
import org.apache.dolphinscheduler.api.security.LdapUserNotExistActionType;
|
||||||
import org.apache.dolphinscheduler.api.service.SessionService;
|
import org.apache.dolphinscheduler.api.service.SessionService;
|
||||||
@ -81,11 +82,13 @@ public class LdapAuthenticatorTest extends AbstractControllerTest {
|
|||||||
private User mockUser;
|
private User mockUser;
|
||||||
private Session mockSession;
|
private Session mockSession;
|
||||||
|
|
||||||
private String ldapUid = "test";
|
private final String ldapUid = "test";
|
||||||
private String ldapUserPwd = "password";
|
private final String ldapUserPwd = "password";
|
||||||
private String ldapEmail = "test@example.com";
|
private final String ldapEmail = "test@example.com";
|
||||||
private String ip = "127.0.0.1";
|
private final String ip = "127.0.0.1";
|
||||||
private UserType userType = UserType.GENERAL_USER;
|
private final UserType userType = UserType.GENERAL_USER;
|
||||||
|
private final LdapLoginResult ldapLoginResultSuccess = new LdapLoginResult(true, ldapEmail, userType, ldapUid);
|
||||||
|
private final LdapLoginResult ldapLoginResultFailed = new LdapLoginResult(false, ldapEmail, userType, ldapUid);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
@ -109,7 +112,7 @@ public class LdapAuthenticatorTest extends AbstractControllerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAuthenticate() {
|
public void testAuthenticate() {
|
||||||
when(ldapService.ldapLogin(ldapUid, ldapUserPwd)).thenReturn(ldapEmail);
|
when(ldapService.ldapLogin(ldapUid, ldapUserPwd)).thenReturn(ldapLoginResultSuccess);
|
||||||
when(sessionService.createSessionIfAbsent(Mockito.any(User.class))).thenReturn(mockSession);
|
when(sessionService.createSessionIfAbsent(Mockito.any(User.class))).thenReturn(mockSession);
|
||||||
|
|
||||||
// test username pwd correct and user not exist, config user not exist action deny, so login denied
|
// test username pwd correct and user not exist, config user not exist action deny, so login denied
|
||||||
@ -131,8 +134,9 @@ public class LdapAuthenticatorTest extends AbstractControllerTest {
|
|||||||
Assertions.assertEquals(Status.LOGIN_SESSION_FAILED.getCode(), (int) result.getCode());
|
Assertions.assertEquals(Status.LOGIN_SESSION_FAILED.getCode(), (int) result.getCode());
|
||||||
|
|
||||||
// test username pwd error, login failed
|
// test username pwd error, login failed
|
||||||
when(ldapService.ldapLogin(ldapUid, ldapUserPwd)).thenReturn(null);
|
when(sessionService.createSessionIfAbsent(Mockito.any(User.class))).thenReturn(mockSession);
|
||||||
result = ldapAuthenticator.authenticate(ldapUid, ldapUserPwd, ip);
|
when(ldapService.ldapLogin(ldapUid, "123")).thenReturn(ldapLoginResultFailed);
|
||||||
|
result = ldapAuthenticator.authenticate(ldapUid, "123", ip);
|
||||||
Assertions.assertEquals(Status.USER_NAME_PASSWD_ERROR.getCode(), (int) result.getCode());
|
Assertions.assertEquals(Status.USER_NAME_PASSWD_ERROR.getCode(), (int) result.getCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,107 +0,0 @@
|
|||||||
/*
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file distributed with
|
|
||||||
* this work for additional information regarding copyright ownership.
|
|
||||||
* The ASF licenses this file to You 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 org.apache.dolphinscheduler.api.security.impl.ldap;
|
|
||||||
|
|
||||||
import org.apache.dolphinscheduler.api.ApiApplicationServer;
|
|
||||||
import org.apache.dolphinscheduler.common.enums.ProfileType;
|
|
||||||
import org.apache.dolphinscheduler.common.enums.UserType;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Disabled;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
|
||||||
import org.springframework.test.context.ActiveProfiles;
|
|
||||||
import org.springframework.test.context.TestPropertySource;
|
|
||||||
|
|
||||||
@Disabled
|
|
||||||
@ActiveProfiles(ProfileType.H2)
|
|
||||||
@SpringBootTest(classes = ApiApplicationServer.class)
|
|
||||||
@TestPropertySource(properties = {
|
|
||||||
"security.authentication.type=LDAP",
|
|
||||||
"security.authentication.ldap.user.admin=read-only-admin",
|
|
||||||
"security.authentication.ldap.urls=ldap://ldap.forumsys.com:389/",
|
|
||||||
"security.authentication.ldap.base-dn=dc=example,dc=com",
|
|
||||||
"security.authentication.ldap.username=cn=read-only-admin,dc=example,dc=com",
|
|
||||||
"security.authentication.ldap.password=password",
|
|
||||||
"security.authentication.ldap.user.identity-attribute=uid",
|
|
||||||
"security.authentication.ldap.user.email-attribute=mail",
|
|
||||||
"security.authentication.ldap.user.not-exist-action=CREATE",
|
|
||||||
"security.authentication.ldap.ssl.enable=false",
|
|
||||||
"security.authentication.ldap.ssl.trust-store=",
|
|
||||||
"security.authentication.ldap.ssl.trust-store-password=",
|
|
||||||
})
|
|
||||||
public class LdapServiceTest {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
protected AutowireCapableBeanFactory beanFactory;
|
|
||||||
|
|
||||||
private LdapService ldapService;
|
|
||||||
|
|
||||||
private final String username = "tesla";
|
|
||||||
private final String correctPassword = "password";
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
public void setUp() {
|
|
||||||
ldapService = new LdapService();
|
|
||||||
beanFactory.autowireBean(ldapService);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getUserType() {
|
|
||||||
UserType userType = ldapService.getUserType("read-only-admin");
|
|
||||||
Assertions.assertEquals(UserType.ADMIN_USER, userType);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void ldapLogin() throws NoSuchFieldException, IllegalAccessException {
|
|
||||||
changeSslEnable(false);
|
|
||||||
String email = ldapService.ldapLogin(username, correctPassword);
|
|
||||||
Assertions.assertEquals("tesla@ldap.forumsys.com", email);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void ldapLoginError() throws NoSuchFieldException, IllegalAccessException {
|
|
||||||
changeSslEnable(false);
|
|
||||||
String email2 = ldapService.ldapLogin(username, "error password");
|
|
||||||
Assertions.assertNull(email2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void ldapLoginSSL() throws NoSuchFieldException, IllegalAccessException {
|
|
||||||
changeSslEnable(true);
|
|
||||||
String email = ldapService.ldapLogin(username, correctPassword);
|
|
||||||
Assertions.assertNull(email);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void changeSslEnable(boolean sslEnable) throws NoSuchFieldException, IllegalAccessException {
|
|
||||||
Class<LdapService> ldapServiceClass = LdapService.class;
|
|
||||||
Field sslEnableField = ldapServiceClass.getDeclaredField("sslEnable");
|
|
||||||
sslEnableField.setAccessible(true);
|
|
||||||
sslEnableField.set(ldapService, sslEnable);
|
|
||||||
if (sslEnable) {
|
|
||||||
Field trustStorePasswordField = ldapServiceClass.getDeclaredField("trustStorePassword");
|
|
||||||
trustStorePasswordField.setAccessible(true);
|
|
||||||
trustStorePasswordField.set(ldapService, "trustStorePassword");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -100,17 +100,18 @@ security:
|
|||||||
# IF you set type `LDAP`, below config will be effective
|
# IF you set type `LDAP`, below config will be effective
|
||||||
ldap:
|
ldap:
|
||||||
# ldap server config
|
# ldap server config
|
||||||
urls: ldap://ldap.forumsys.com:389/
|
url: ldap://ldap.forumsys.com:389/
|
||||||
base-dn: dc=example,dc=com
|
base-dn: dc=example,dc=com
|
||||||
username: cn=read-only-admin,dc=example,dc=com
|
username: cn=read-only-admin,dc=example,dc=com
|
||||||
password: password
|
password: password
|
||||||
user:
|
user:
|
||||||
# admin userId when you use LDAP login
|
# admin username when you use LDAP login
|
||||||
admin: read-only-admin
|
admin-username: admin
|
||||||
|
admin-user-filter: (&(cn={0}))
|
||||||
identity-attribute: uid
|
identity-attribute: uid
|
||||||
email-attribute: mail
|
email-attribute: mail
|
||||||
# action when ldap user is not exist (supported types: CREATE,DENY)
|
# action when ldap user is not exist (supported types: CREATE,DENY)
|
||||||
not-exist-action: CREATE
|
not-exist-action: DENY
|
||||||
ssl:
|
ssl:
|
||||||
enable: false
|
enable: false
|
||||||
# jks file absolute path && password
|
# jks file absolute path && password
|
||||||
|
@ -90,17 +90,19 @@ security:
|
|||||||
# IF you set type `LDAP`, below config will be effective
|
# IF you set type `LDAP`, below config will be effective
|
||||||
ldap:
|
ldap:
|
||||||
# ldap server config
|
# ldap server config
|
||||||
urls: ldap://ldap.forumsys.com:389/
|
url: ldap://ldap.forumsys.com:389/
|
||||||
base-dn: dc=example,dc=com
|
base-dn: dc=example,dc=com
|
||||||
username: cn=read-only-admin,dc=example,dc=com
|
username: admin,dc=example,dc=com
|
||||||
password: password
|
password: password
|
||||||
user:
|
user:
|
||||||
# admin userId when you use LDAP login
|
# admin username when you use LDAP login
|
||||||
admin: read-only-admin
|
admin-username: ldap-admin
|
||||||
|
# user search filter to find admin user
|
||||||
|
admin-user-filter: (&(cn={0}))
|
||||||
identity-attribute: uid
|
identity-attribute: uid
|
||||||
email-attribute: mail
|
email-attribute: mail
|
||||||
# action when ldap user is not exist (supported types: CREATE,DENY)
|
# action when ldap user is not exist (supported types: CREATE,DENY)
|
||||||
not-exist-action: CREATE
|
not-exist-action: DENY
|
||||||
ssl:
|
ssl:
|
||||||
enable: false
|
enable: false
|
||||||
# jks file absolute path && password
|
# jks file absolute path && password
|
||||||
|
Loading…
Reference in New Issue
Block a user