Skip to content

Commit

Permalink
[Improvement-16106] Add admin-user-filter in LDAP (#16826)
Browse files Browse the repository at this point in the history
* improvement 16106

* fix ut

* fix comment

* fix spotless
  • Loading branch information
SbloodyS authored Nov 22, 2024
1 parent b885970 commit 627c76b
Show file tree
Hide file tree
Showing 23 changed files with 802 additions and 255 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/api-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ jobs:
class: org.apache.dolphinscheduler.api.test.cases.ExecutorAPITest
- name: WorkflowInstanceAPITest
class: org.apache.dolphinscheduler.api.test.cases.WorkflowInstanceAPITest
- name: LdapLoginAPITest
class: org.apache.dolphinscheduler.api.test.cases.LdapLoginAPITest
env:
RECORDING_PATH: /tmp/recording-${{ matrix.case.name }}
steps:
Expand Down
37 changes: 7 additions & 30 deletions docs/docs/en/guide/security/authentication-type.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,23 @@
security:
authentication:
# Authentication types (supported types: PASSWORD,LDAP,CASDOOR_SSO)
type: PASSWORD
type: LDAP
# IF you set type `LDAP`, below config will be effective
ldap:
# ldap server config
urls: ldap://ldap.forumsys.com:389/
url: ldap://ldap.forumsys.com:389/
base-dn: dc=example,dc=com
username: cn=read-only-admin,dc=example,dc=com
username: cn=admin,dc=example,dc=com
password: password
user:
# 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
email-attribute: mail
# action when ldap user is not exist (supported types: CREATE,DENY)
not-exist-action: CREATE
not-exist-action: DENY
ssl:
enable: false
# jks file absolute path && password
Expand Down Expand Up @@ -71,31 +73,6 @@ casdoor:
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](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:
Expand Down
50 changes: 13 additions & 37 deletions docs/docs/zh/guide/security/authentication-type.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,23 @@ security:
# IF you set type `LDAP`, below config will be effective
ldap:
# ldap server config
urls: ldap://ldap.forumsys.com:389/
url: ldap://ldap.forumsys.com:389/
base-dn: dc=example,dc=com
username: cn=read-only-admin,dc=example,dc=com
username: cn=admin,dc=example,dc=com
password: password
user:
# admin userId when you use LDAP login
admin: read-only-admin
identity-attribute: uid
email-attribute: mail
# action when ldap user is not exist (supported types: CREATE,DENY)
not-exist-action: CREATE
# admin userId when you use LDAP login
admin: ldap-admin
# user search filter to find admin user
identity-attribute: uid
email-attribute: mail
# action when ldap user is not exist (supported types: CREATE,DENY)
not-exist-action: DENY
ssl:
enable: false
# jks file absolute path && password
trust-store: "/ldapkeystore.jks"
trust-store-password: "password"
enable: false
# jks file absolute path && password
trust-store: "/ldapkeystore.jks"
trust-store-password: "password"
casdoor:
user:
admin: ""
Expand Down Expand Up @@ -71,31 +72,6 @@ casdoor:
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 是基于 OAuth 2.0、OIDC、SAML 和 CAS 的面向 UI 的身份访问管理(IAM)/单点登录(SSO)平台。您可以通过以下步骤通过 Casdoor 为 Dolphinscheduler 添加 SSO 功能:
Expand Down
26 changes: 26 additions & 0 deletions dolphinscheduler-api-test/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,12 @@
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Order;
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.
@DolphinScheduler(composeFiles = "docker/basic/docker-compose.yaml")
@Slf4j
@DisableIfTestFails
public class ExecutorAPITest {

private static final String username = "admin";
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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<>());
}
}
Loading

0 comments on commit 627c76b

Please sign in to comment.