This is an automated email from the ASF dual-hosted git repository.

lzljs3620320 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-paimon-webui.git


The following commit(s) were added to refs/heads/main by this push:
     new 6b61132  Feat ldap login (#21)
6b61132 is described below

commit 6b611323024ffea04bd35ed58b69bbf82d936063
Author: G-XD <[email protected]>
AuthorDate: Wed Aug 16 14:37:37 2023 +0800

    Feat ldap login (#21)
---
 paimon-web-server/pom.xml                          |  10 ++
 .../server/configrue/GlobalExceptionHandler.java   |  46 ++++++++
 .../web/server/controller/LoginController.java     |  10 +-
 .../paimon/web/server/data/dto/LoginDto.java       |  38 ++++++
 .../paimon/web/server/data/enums/UserType.java     |  43 +++++++
 .../web/server/data/result/enums/Status.java       |   6 +-
 .../exception/user/UserDisabledException.java}     |  40 ++-----
 .../service/{UserService.java => LdapService.java} |  38 +++---
 .../web/server/service/PermissionService.java      |   1 -
 .../paimon/web/server/service/SysRoleService.java  |   6 +-
 .../paimon/web/server/service/UserService.java     |   6 +-
 .../web/server/service/impl/LdapServiceImpl.java   | 131 +++++++++++++++++++++
 .../web/server/service/impl/UserServiceImpl.java   |  55 ++++++++-
 .../src/main/resources/application-dev.yml         |   8 +-
 .../src/main/resources/i18n/messages.properties    |   4 +-
 .../main/resources/i18n/messages_en_US.properties  |   4 +-
 .../main/resources/i18n/messages_zh_CN.properties  |   4 +-
 .../src/main/resources/mapper/SysRoleMapper.xml    |   2 +-
 .../web/server/controller/PermissionTest.java      |   8 +-
 .../server/controller/SysMenuControllerTest.java   |   8 +-
 .../server/controller/SysRoleControllerTest.java   |   8 +-
 21 files changed, 390 insertions(+), 86 deletions(-)

diff --git a/paimon-web-server/pom.xml b/paimon-web-server/pom.xml
index 993f48e..ca9b0ac 100644
--- a/paimon-web-server/pom.xml
+++ b/paimon-web-server/pom.xml
@@ -129,6 +129,16 @@ under the License.
             <artifactId>spring-boot-starter-aop</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-ldap</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>com.baomidou</groupId>
             <artifactId>mybatis-plus-boot-starter</artifactId>
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/configrue/GlobalExceptionHandler.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/configrue/GlobalExceptionHandler.java
index f8de4da..fa4f5cb 100644
--- 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/configrue/GlobalExceptionHandler.java
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/configrue/GlobalExceptionHandler.java
@@ -26,14 +26,27 @@ import 
org.apache.paimon.web.server.data.result.exception.BaseException;
 import cn.dev33.satoken.exception.NotLoginException;
 import cn.dev33.satoken.exception.NotPermissionException;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.context.support.DefaultMessageSourceResolvable;
 import org.springframework.dao.DataAccessException;
 import org.springframework.http.HttpStatus;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.validation.BindException;
+import org.springframework.validation.FieldError;
 import org.springframework.web.HttpRequestMethodNotSupportedException;
 import org.springframework.web.bind.annotation.ControllerAdvice;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.ResponseBody;
 import org.springframework.web.bind.annotation.ResponseStatus;
 
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+
 /** GlobalExceptionHandler. */
 @Slf4j
 @ControllerAdvice
@@ -73,6 +86,39 @@ public class GlobalExceptionHandler {
         return R.failed(Status.UNKNOWN_ERROR, "sql error");
     }
 
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    @ExceptionHandler(BindException.class)
+    public R<Void> bindExceptionHandler(BindException e) {
+        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
+        Optional<String> msg =
+                fieldErrors.stream()
+                        .filter(Objects::nonNull)
+                        .map(DefaultMessageSourceResolvable::getDefaultMessage)
+                        .filter(StringUtils::isNotBlank)
+                        .findFirst();
+        return msg.<R<Void>>map(s -> R.failed(Status.REQUEST_PARAMS_ERROR, s))
+                .orElseGet(() -> R.failed(Status.REQUEST_PARAMS_ERROR));
+    }
+
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    @ExceptionHandler(ConstraintViolationException.class)
+    public R<Void> 
constraintViolationExceptionHandler(ConstraintViolationException e) {
+        Set<ConstraintViolation<?>> constraintViolations = 
e.getConstraintViolations();
+        Optional<String> msg =
+                constraintViolations.stream()
+                        .filter(Objects::nonNull)
+                        .map(ConstraintViolation::getMessage)
+                        .findFirst();
+        return msg.<R<Void>>map(s -> R.failed(Status.REQUEST_PARAMS_ERROR, s))
+                .orElseGet(() -> R.failed(Status.REQUEST_PARAMS_ERROR));
+    }
+
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    @ExceptionHandler(HttpMessageNotReadableException.class)
+    public R<Void> 
constraintViolationExceptionHandler(HttpMessageNotReadableException e) {
+        return R.failed(Status.REQUEST_PARAMS_ERROR, "Required request body is 
missing");
+    }
+
     @ExceptionHandler
     public R<Void> unknownException(Exception e) {
         log.error("UnknownException", e);
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/LoginController.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/LoginController.java
index 3db7dd2..b209464 100644
--- 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/LoginController.java
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/LoginController.java
@@ -18,6 +18,7 @@
 
 package org.apache.paimon.web.server.controller;
 
+import org.apache.paimon.web.server.data.dto.LoginDto;
 import org.apache.paimon.web.server.data.result.R;
 import org.apache.paimon.web.server.service.UserService;
 
@@ -25,8 +26,10 @@ import cn.dev33.satoken.stp.SaTokenInfo;
 import cn.dev33.satoken.stp.StpUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
@@ -41,13 +44,12 @@ public class LoginController {
     /**
      * login by username and password.
      *
-     * @param username username
-     * @param password password
+     * @param loginDto login info
      * @return token string
      */
     @PostMapping("/login")
-    public R<String> login(String username, String password) {
-        return R.succeed(userService.login(username, password));
+    public R<String> login(@Validated @RequestBody LoginDto loginDto) {
+        return R.succeed(userService.login(loginDto));
     }
 
     /**
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/dto/LoginDto.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/dto/LoginDto.java
new file mode 100644
index 0000000..1e23c7b
--- /dev/null
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/dto/LoginDto.java
@@ -0,0 +1,38 @@
+/*
+ * 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.paimon.web.server.data.dto;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+/** login dto. */
+@Data
+public class LoginDto {
+    /** login username. */
+    @NotBlank(message = "username is required")
+    private String username;
+    /** login password. */
+    @NotBlank(message = "password is required")
+    private String password;
+    /** remember me flag. */
+    private boolean rememberMe;
+    /** ldap login flag. */
+    private boolean ldapLogin;
+}
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/enums/UserType.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/enums/UserType.java
new file mode 100644
index 0000000..5d1e257
--- /dev/null
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/enums/UserType.java
@@ -0,0 +1,43 @@
+/*
+ * 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.paimon.web.server.data.enums;
+
+/** user type. */
+public enum UserType {
+    /** ldap user. */
+    LDAP(1, "LDAP"),
+    /** local user. */
+    LOCAL(0, "LOCAL");
+
+    private final int code;
+    private final String type;
+
+    public int getCode() {
+        return this.code;
+    }
+
+    public String getType() {
+        return this.type;
+    }
+
+    UserType(int code, String type) {
+        this.code = code;
+        this.type = type;
+    }
+}
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/result/enums/Status.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/result/enums/Status.java
index c8dc0d3..27283ba 100644
--- 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/result/enums/Status.java
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/result/enums/Status.java
@@ -36,11 +36,13 @@ public enum Status {
     // TODO
     UNKNOWN_ERROR(500, "unknown.error"),
     INTERNAL_SERVER_ERROR_ARGS(501, "internal.server.error"),
-    REQUEST_PARAMS_NOT_VALID_ERROR(7002, "invalid.request.parameter"),
-    REQUEST_PARAMS_ERROR(7003, "request.parameter.error"),
+
+    REQUEST_PARAMS_NOT_VALID_ERROR(4001, "invalid.request.parameter"),
+    REQUEST_PARAMS_ERROR(4002, "request.parameter.error"),
 
     USER_NOT_EXIST(10001, "user.not.exist"),
     USER_PASSWORD_ERROR(10002, "user.password.error"),
+    USER_DISABLED_ERROR(10003, "user.is.disabled"),
     /** ------------role-----------------. */
     ROLE_IN_USED(10101, "role.in.used"),
     ROLE_NAME_IS_EXIST(10102, "role.name.exist"),
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/UserService.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/result/exception/user/UserDisabledException.java
similarity index 51%
copy from 
paimon-web-server/src/main/java/org/apache/paimon/web/server/service/UserService.java
copy to 
paimon-web-server/src/main/java/org/apache/paimon/web/server/data/result/exception/user/UserDisabledException.java
index e0bd750..01341cc 100644
--- 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/UserService.java
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/result/exception/user/UserDisabledException.java
@@ -16,40 +16,16 @@
  * limitations under the License.
  */
 
-package org.apache.paimon.web.server.service;
+package org.apache.paimon.web.server.data.result.exception.user;
 
-import org.apache.paimon.web.server.data.model.User;
+import org.apache.paimon.web.server.data.result.enums.Status;
 import org.apache.paimon.web.server.data.result.exception.BaseException;
 
-import com.baomidou.mybatisplus.extension.service.IService;
+/** Exception to user disabled. */
+public class UserDisabledException extends BaseException {
+    private static final long serialVersionUID = 1L;
 
-import java.util.List;
-
-/** User Service. */
-public interface UserService extends IService<User> {
-
-    /**
-     * login by username and password.
-     *
-     * @param username username
-     * @param password pwd
-     * @return {@link String}
-     */
-    String login(String username, String password) throws BaseException;
-
-    /**
-     * Query the list of assigned user roles.
-     *
-     * @param user query params
-     * @return user list
-     */
-    List<User> selectAllocatedList(User user);
-
-    /**
-     * Query the list of unassigned user roles.
-     *
-     * @param user query params
-     * @return user list
-     */
-    List<User> selectUnallocatedList(User user);
+    public UserDisabledException() {
+        super(Status.USER_DISABLED_ERROR);
+    }
 }
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/UserService.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/LdapService.java
similarity index 55%
copy from 
paimon-web-server/src/main/java/org/apache/paimon/web/server/service/UserService.java
copy to 
paimon-web-server/src/main/java/org/apache/paimon/web/server/service/LdapService.java
index e0bd750..a28f80a 100644
--- 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/UserService.java
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/LdapService.java
@@ -19,37 +19,27 @@
 package org.apache.paimon.web.server.service;
 
 import org.apache.paimon.web.server.data.model.User;
-import org.apache.paimon.web.server.data.result.exception.BaseException;
 
-import com.baomidou.mybatisplus.extension.service.IService;
-
-import java.util.List;
-
-/** User Service. */
-public interface UserService extends IService<User> {
-
-    /**
-     * login by username and password.
-     *
-     * @param username username
-     * @param password pwd
-     * @return {@link String}
-     */
-    String login(String username, String password) throws BaseException;
+import java.util.Optional;
 
+/** ldap service. */
+public interface LdapService {
     /**
-     * Query the list of assigned user roles.
+     * get user info by ldap user identification.
      *
-     * @param user query params
-     * @return user list
+     * @param uid login name of ldap user
+     * @return {@link Optional} of {@link User} when user not exist then 
return {@link
+     *     Optional#empty()}
      */
-    List<User> selectAllocatedList(User user);
+    Optional<User> getUser(String uid);
 
     /**
-     * Query the list of unassigned user roles.
+     * authenticate by ldap.
      *
-     * @param user query params
-     * @return user list
+     * @param name login name of ldap user
+     * @param password login password of ldap user
+     * @return {@link Optional} of {@link User} when user authenticate failed 
then return {@link
+     *     Optional#empty()}
      */
-    List<User> selectUnallocatedList(User user);
+    Optional<User> authenticate(String name, String password);
 }
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/PermissionService.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/PermissionService.java
index 6a746a7..acce42e 100644
--- 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/PermissionService.java
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/PermissionService.java
@@ -50,7 +50,6 @@ public class PermissionService implements StpInterface {
         Preconditions.checkArgument(userId != null);
         int userIdNum = Integer.parseInt(userId.toString());
         Set<String> perms = new HashSet<String>();
-        // 管理员拥有所有权限
         if (userIdNum == Constants.ADMIN_ID) {
             perms.add(Constants.ALL_PERMISSION);
         } else {
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/SysRoleService.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/SysRoleService.java
index d7701c0..dd0ab61 100644
--- 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/SysRoleService.java
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/SysRoleService.java
@@ -36,7 +36,7 @@ public interface SysRoleService extends IService<SysRole> {
      * @param role query params
      * @return role list
      */
-    public List<SysRole> selectRoleList(IPage<SysRole> page, SysRole role);
+    List<SysRole> selectRoleList(IPage<SysRole> page, SysRole role);
 
     /**
      * Query role list based on user ID.
@@ -44,7 +44,7 @@ public interface SysRoleService extends IService<SysRole> {
      * @param userId user ID
      * @return role list
      */
-    public List<SysRole> selectRolesByUserId(Integer userId);
+    List<SysRole> selectRolesByUserId(Integer userId);
 
     /**
      * Query role permissions based on user ID.
@@ -52,7 +52,7 @@ public interface SysRoleService extends IService<SysRole> {
      * @param userId user ID
      * @return permissions
      */
-    public Set<String> selectRolePermissionByUserId(Integer userId);
+    Set<String> selectRolePermissionByUserId(Integer userId);
 
     /**
      * Query user role list by user ID.
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/UserService.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/UserService.java
index e0bd750..c6ed35e 100644
--- 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/UserService.java
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/UserService.java
@@ -18,6 +18,7 @@
 
 package org.apache.paimon.web.server.service;
 
+import org.apache.paimon.web.server.data.dto.LoginDto;
 import org.apache.paimon.web.server.data.model.User;
 import org.apache.paimon.web.server.data.result.exception.BaseException;
 
@@ -31,11 +32,10 @@ public interface UserService extends IService<User> {
     /**
      * login by username and password.
      *
-     * @param username username
-     * @param password pwd
+     * @param loginDto login info
      * @return {@link String}
      */
-    String login(String username, String password) throws BaseException;
+    String login(LoginDto loginDto) throws BaseException;
 
     /**
      * Query the list of assigned user roles.
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/impl/LdapServiceImpl.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/impl/LdapServiceImpl.java
new file mode 100644
index 0000000..9f9d060
--- /dev/null
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/impl/LdapServiceImpl.java
@@ -0,0 +1,131 @@
+/*
+ * 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.paimon.web.server.service.impl;
+
+import org.apache.paimon.web.server.data.enums.UserType;
+import org.apache.paimon.web.server.data.model.User;
+import org.apache.paimon.web.server.service.LdapService;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.ldap.core.AttributesMapper;
+import org.springframework.ldap.core.LdapTemplate;
+import org.springframework.ldap.filter.EqualsFilter;
+import org.springframework.ldap.query.LdapQuery;
+import org.springframework.ldap.query.LdapQueryBuilder;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+
+import java.util.List;
+import java.util.Optional;
+
+/** ldap service impl. */
+@Slf4j
+@Service
+public class LdapServiceImpl implements LdapService {
+
+    private static final UserAttributesMapperMapper MAPPER = new 
UserAttributesMapperMapper();
+
+    private static final String FILTER = "cn";
+
+    @Autowired private LdapTemplate ldapTemplate;
+
+    /**
+     * get user info by ldap user identification.
+     *
+     * @param uid login name of ldap user
+     * @return {@link Optional} of {@link User} when user not exist then 
return {@link
+     *     Optional#empty()}
+     */
+    @Override
+    public Optional<User> getUser(String uid) {
+        LdapQuery query = LdapQueryBuilder.query().where(FILTER).is(uid);
+        try {
+            List<User> users = ldapTemplate.search(query, MAPPER);
+            return CollectionUtils.isEmpty(users)
+                    ? Optional.empty()
+                    : Optional.ofNullable(users.get(0));
+        } catch (EmptyResultDataAccessException e) {
+            return Optional.empty();
+        }
+    }
+
+    /**
+     * authenticate by ldap.
+     *
+     * @param name login name of ldap user
+     * @param password login password of ldap user
+     * @return {@link Optional} of {@link User} when user authenticate failed 
then return {@link
+     *     Optional#empty()}
+     */
+    @Override
+    public Optional<User> authenticate(String name, String password) {
+        EqualsFilter filter = new EqualsFilter(FILTER, name);
+        if (ldapTemplate.authenticate(StringUtils.EMPTY, filter.toString(), 
password)) {
+            return this.getUser(name);
+        }
+        return Optional.empty();
+    }
+
+    /** Attributes mapper from LDAP user to Local user. */
+    private static class UserAttributesMapperMapper implements 
AttributesMapper<User> {
+
+        /**
+         * Map the LDAP attributes to User object.
+         *
+         * @param attributes LDAP attributes
+         * @return User object
+         * @throws NamingException if there is an error during mapping
+         */
+        @Override
+        public User mapFromAttributes(Attributes attributes) throws 
NamingException {
+            Attribute usernameAttr = attributes.get(FILTER);
+            Attribute nicknameAttr = attributes.get("sn");
+            Attribute email = attributes.get("email");
+
+            if (usernameAttr != null && nicknameAttr != null) {
+                User user = new User();
+                user.setUsername(usernameAttr.get().toString());
+                user.setNickname(nicknameAttr.get().toString());
+                user.setEmail(
+                        Optional.ofNullable(email)
+                                .map(
+                                        e -> {
+                                            try {
+                                                return e.get().toString();
+                                            } catch (NamingException ignore) {
+                                            }
+                                            return StringUtils.EMPTY;
+                                        })
+                                .orElse(StringUtils.EMPTY));
+                user.setUserType(UserType.LDAP.getCode());
+                user.setEnabled(true);
+                return user;
+            }
+
+            return null;
+        }
+    }
+}
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/impl/UserServiceImpl.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/impl/UserServiceImpl.java
index 35e45e5..5d66c7f 100644
--- 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/impl/UserServiceImpl.java
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/impl/UserServiceImpl.java
@@ -18,11 +18,15 @@
 
 package org.apache.paimon.web.server.service.impl;
 
+import org.apache.paimon.web.server.data.dto.LoginDto;
+import org.apache.paimon.web.server.data.enums.UserType;
 import org.apache.paimon.web.server.data.model.User;
 import org.apache.paimon.web.server.data.result.exception.BaseException;
+import 
org.apache.paimon.web.server.data.result.exception.user.UserDisabledException;
 import 
org.apache.paimon.web.server.data.result.exception.user.UserNotExistsException;
 import 
org.apache.paimon.web.server.data.result.exception.user.UserPasswordNotMatchException;
 import org.apache.paimon.web.server.mapper.UserMapper;
+import org.apache.paimon.web.server.service.LdapService;
 import org.apache.paimon.web.server.service.UserService;
 
 import cn.dev33.satoken.stp.StpUtil;
@@ -31,32 +35,71 @@ import 
org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import java.util.List;
+import java.util.Optional;
 
 /** UserServiceImpl. */
 @Service
 public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements 
UserService {
 
+    @Autowired private LdapService ldapService;
     @Autowired private UserMapper userMapper;
 
     /**
      * login by username and password.
      *
-     * @param username username
-     * @param password pwd
+     * @param loginDto login info
      * @return {@link String}
      */
     @Override
-    public String login(String username, String password) throws BaseException 
{
-        User user = this.lambdaQuery().eq(User::getUsername, username).one();
+    public String login(LoginDto loginDto) throws BaseException {
+        String username = loginDto.getUsername();
+        String password = loginDto.getPassword();
+
+        User user =
+                loginDto.isLdapLogin()
+                        ? ldapLogin(username, password)
+                        : localLogin(username, password);
+        if (!user.getEnabled()) {
+            throw new UserDisabledException();
+        }
+
+        StpUtil.login(user.getId(), loginDto.isRememberMe());
+
+        return StpUtil.getTokenValue();
+    }
+
+    private User localLogin(String username, String password) throws 
BaseException {
+        User user =
+                this.lambdaQuery()
+                        .eq(User::getUsername, username)
+                        .eq(User::getUserType, UserType.LOCAL.getCode())
+                        .one();
         if (user == null) {
             throw new UserNotExistsException();
         }
         if (!user.getPassword().equals(password)) {
             throw new UserPasswordNotMatchException();
         }
-        StpUtil.login(user.getId());
+        return user;
+    }
 
-        return StpUtil.getTokenValue();
+    private User ldapLogin(String username, String password) throws 
BaseException {
+        Optional<User> authenticate = ldapService.authenticate(username, 
password);
+        if (!authenticate.isPresent()) {
+            throw new UserPasswordNotMatchException();
+        }
+
+        User user =
+                this.lambdaQuery()
+                        .eq(User::getUsername, username)
+                        .eq(User::getUserType, UserType.LDAP.getCode())
+                        .one();
+        if (user == null) {
+            user = authenticate.get();
+            this.save(user);
+            // TODO assign default roles and tenants
+        }
+        return user;
     }
 
     /**
diff --git a/paimon-web-server/src/main/resources/application-dev.yml 
b/paimon-web-server/src/main/resources/application-dev.yml
index 70756dd..3a4532d 100644
--- a/paimon-web-server/src/main/resources/application-dev.yml
+++ b/paimon-web-server/src/main/resources/application-dev.yml
@@ -28,4 +28,10 @@ spring:
       continue-on-error: true
       username: root
       password: root
-      mode: always
\ No newline at end of file
+      mode: always
+
+  ldap:
+    urls: ldap://${LDAP_HOST:127.0.0.1}:${LDAP_PORT:389}
+    base: dc=test,dc=com
+    username: ${LDAP_USER:cn=admin,dc=test,dc=com}
+    password: ${LDAP_PASSWORD:admin}
diff --git a/paimon-web-server/src/main/resources/i18n/messages.properties 
b/paimon-web-server/src/main/resources/i18n/messages.properties
index a96af9f..957a9fc 100644
--- a/paimon-web-server/src/main/resources/i18n/messages.properties
+++ b/paimon-web-server/src/main/resources/i18n/messages.properties
@@ -21,9 +21,11 @@ forbidden=User Not Have Permission
 unknown.error=Unknown Error:{0}
 method.not.allowed=Method Not Allowed
 invalid.request.parameter=Request Parameter {0} Is Not Valid
-request.parameter.error=Request Parameter Error
+request.parameter.error=Request Parameter Error:{0}
 internal.server.error=Internal Server Error:{0}
 user.not.exist=User Not Exist
+user.password.error=User Password Error
+user.is.disabled=User Is Disabled
 role.in.used=This role {0} is in used
 role.name.exist=This role name {0} is exist
 role.key.exist=This role key {0} is exist
diff --git 
a/paimon-web-server/src/main/resources/i18n/messages_en_US.properties 
b/paimon-web-server/src/main/resources/i18n/messages_en_US.properties
index ed08361..df1cb11 100644
--- a/paimon-web-server/src/main/resources/i18n/messages_en_US.properties
+++ b/paimon-web-server/src/main/resources/i18n/messages_en_US.properties
@@ -21,9 +21,11 @@ forbidden=User Not Have Permission
 unknown.error=Unknown Error:{0}
 method.not.allowed=Method Not Allowed
 invalid.request.parameter=Request Parameter {0} Is Not Valid
-request.parameter.error=Request Parameter Error
+request.parameter.error=Request Parameter Error:{0}
 internal.server.error=Internal Server Error:{0}
 user.not.exist=User Not Exist
+user.password.error=User Password Error
+user.is.disabled=User Is Disabled
 role.in.used=This role {0} is in used
 role.name.exist=This role name {0} is exist
 role.key.exist=This role key {0} is exist
diff --git 
a/paimon-web-server/src/main/resources/i18n/messages_zh_CN.properties 
b/paimon-web-server/src/main/resources/i18n/messages_zh_CN.properties
index 84e3484..98aa255 100644
--- a/paimon-web-server/src/main/resources/i18n/messages_zh_CN.properties
+++ b/paimon-web-server/src/main/resources/i18n/messages_zh_CN.properties
@@ -21,9 +21,11 @@ forbidden=用户无权限
 unknown.error=未知错误:{0}
 method.not.allowed=请求方法不支持
 invalid.request.parameter=请求参数 {0} 无效
-request.parameter.error=请求参数错误
+request.parameter.error=请求参数错误:{0}
 internal.server.error=服务器内部错误:{0}
 user.not.exist=用户不存在
+user.password.error=密码错误
+user.is.disabled=用户已禁用
 role.in.used=此角色{0}正在使用中
 role.name.exist=此角色名称{0}已使用
 role.key.exist=此角色关键字{0}已使用
diff --git a/paimon-web-server/src/main/resources/mapper/SysRoleMapper.xml 
b/paimon-web-server/src/main/resources/mapper/SysRoleMapper.xml
index 213ae26..75256ea 100644
--- a/paimon-web-server/src/main/resources/mapper/SysRoleMapper.xml
+++ b/paimon-web-server/src/main/resources/mapper/SysRoleMapper.xml
@@ -68,7 +68,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     
        <select id="selectRolePermissionByUserId" parameterType="Integer" 
resultMap="SysRoleResult">
                <include refid="selectRoleVo"/>
-               WHERE r.is_delete = '0' and ur.user_id = #{userId}
+               WHERE r.is_delete = '0' and urole.user_id = #{userId}
        </select>
        
        <select id="selectRoleAll" resultMap="SysRoleResult">
diff --git 
a/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/PermissionTest.java
 
b/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/PermissionTest.java
index 730693e..21d22dd 100644
--- 
a/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/PermissionTest.java
+++ 
b/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/PermissionTest.java
@@ -18,6 +18,7 @@
 
 package org.apache.paimon.web.server.controller;
 
+import org.apache.paimon.web.server.data.dto.LoginDto;
 import org.apache.paimon.web.server.data.model.User;
 import org.apache.paimon.web.server.data.result.R;
 import org.apache.paimon.web.server.data.result.enums.Status;
@@ -59,11 +60,14 @@ public class PermissionTest {
 
     @BeforeEach
     public void before() throws Exception {
+        LoginDto login = new LoginDto();
+        login.setUsername("common");
+        login.setPassword("21232f297a57a5a743894a0e4a801fc3");
+
         String result =
                 mockMvc.perform(
                                 MockMvcRequestBuilders.post(loginPath)
-                                        .queryParam("username", "common")
-                                        .queryParam("password", 
"21232f297a57a5a743894a0e4a801fc3")
+                                        
.content(ObjectMapperUtils.toJSON(login))
                                         
.contentType(MediaType.APPLICATION_JSON_VALUE)
                                         
.accept(MediaType.APPLICATION_JSON_VALUE))
                         .andExpect(MockMvcResultMatchers.status().isOk())
diff --git 
a/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/SysMenuControllerTest.java
 
b/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/SysMenuControllerTest.java
index ae4505b..6d9f09e 100644
--- 
a/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/SysMenuControllerTest.java
+++ 
b/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/SysMenuControllerTest.java
@@ -18,6 +18,7 @@
 
 package org.apache.paimon.web.server.controller;
 
+import org.apache.paimon.web.server.data.dto.LoginDto;
 import org.apache.paimon.web.server.data.model.SysMenu;
 import org.apache.paimon.web.server.data.result.R;
 import org.apache.paimon.web.server.data.tree.TreeSelect;
@@ -63,11 +64,14 @@ public class SysMenuControllerTest {
 
     @BeforeEach
     public void before() throws Exception {
+        LoginDto login = new LoginDto();
+        login.setUsername("admin");
+        login.setPassword("21232f297a57a5a743894a0e4a801fc3");
+
         String result =
                 mockMvc.perform(
                                 MockMvcRequestBuilders.post(loginPath)
-                                        .queryParam("username", "admin")
-                                        .queryParam("password", 
"21232f297a57a5a743894a0e4a801fc3")
+                                        
.content(ObjectMapperUtils.toJSON(login))
                                         
.contentType(MediaType.APPLICATION_JSON_VALUE)
                                         
.accept(MediaType.APPLICATION_JSON_VALUE))
                         .andExpect(MockMvcResultMatchers.status().isOk())
diff --git 
a/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/SysRoleControllerTest.java
 
b/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/SysRoleControllerTest.java
index 462a585..216e269 100644
--- 
a/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/SysRoleControllerTest.java
+++ 
b/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/SysRoleControllerTest.java
@@ -18,6 +18,7 @@
 
 package org.apache.paimon.web.server.controller;
 
+import org.apache.paimon.web.server.data.dto.LoginDto;
 import org.apache.paimon.web.server.data.model.SysRole;
 import org.apache.paimon.web.server.data.result.PageR;
 import org.apache.paimon.web.server.data.result.R;
@@ -67,11 +68,14 @@ public class SysRoleControllerTest {
 
     @BeforeEach
     public void before() throws Exception {
+        LoginDto login = new LoginDto();
+        login.setUsername("admin");
+        login.setPassword("21232f297a57a5a743894a0e4a801fc3");
+
         String result =
                 mockMvc.perform(
                                 MockMvcRequestBuilders.post(loginPath)
-                                        .queryParam("username", "admin")
-                                        .queryParam("password", 
"21232f297a57a5a743894a0e4a801fc3")
+                                        
.content(ObjectMapperUtils.toJSON(login))
                                         
.contentType(MediaType.APPLICATION_JSON_VALUE)
                                         
.accept(MediaType.APPLICATION_JSON_VALUE))
                         .andExpect(MockMvcResultMatchers.status().isOk())


Reply via email to