This is an automated email from the ASF dual-hosted git repository.
mchades pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/main by this push:
new f02647dc90 [#10963] feat(authn): Add built-in IdP model (#11022)
f02647dc90 is described below
commit f02647dc901d171122411aa2a111cd38cc191a31
Author: MaSai <[email protected]>
AuthorDate: Tue May 26 16:15:36 2026 +0800
[#10963] feat(authn): Add built-in IdP model (#11022)
### What changes were proposed in this pull request?
This PR adds the built-in IdP API model classes under the
`plugins:idp-basic` module, including DTOs, requests, responses, and
their unit tests.
### Why are the changes needed?
The built-in IdP management work needs its API model classes to live
with the `idp-basic` plugin instead of introducing them in broader
shared modules.
Fix: #10963
### Does this PR introduce _any_ user-facing change?
No.
### How was this patch tested?
```bash
./gradlew :plugins:idp-basic:test \
--tests org.apache.gravitino.idp.basic.dto.TestIdpUserDTO \
--tests org.apache.gravitino.idp.basic.dto.TestIdpGroupDTO \
--tests org.apache.gravitino.idp.basic.dto.requests.TestCreateUserRequest
\
--tests
org.apache.gravitino.idp.basic.dto.requests.TestCreateGroupRequest \
--tests
org.apache.gravitino.idp.basic.dto.requests.TestResetPasswordRequest \
--tests
org.apache.gravitino.idp.basic.dto.requests.TestUpdateGroupUsersRequest \
--tests org.apache.gravitino.idp.basic.dto.responses.TestIdpUserResponse \
--tests org.apache.gravitino.idp.basic.dto.responses.TestIdpGroupResponse
\
-PskipWeb=true
```
---------
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Cursor <[email protected]>
---
design-docs/gravitino-local-authentication.md | 8 +-
plugins/idp-basic/build.gradle.kts | 4 +-
.../apache/gravitino/idp/IdpUserGroupManager.java | 17 ++--
.../org/apache/gravitino/idp/dto/IdpGroupDTO.java | 82 +++++++++++++++++++
.../org/apache/gravitino/idp/dto/IdpUserDTO.java | 83 +++++++++++++++++++
.../idp/dto/requests/AddGroupRequest.java | 68 ++++++++++++++++
.../gravitino/idp/dto/requests/AddUserRequest.java | 76 +++++++++++++++++
.../dto/requests/GroupMembershipChangeRequest.java | 84 +++++++++++++++++++
.../idp/dto/requests/ResetPasswordRequest.java | 69 ++++++++++++++++
.../idp/dto/responses/IdpGroupResponse.java | 79 ++++++++++++++++++
.../idp/dto/responses/IdpUserResponse.java | 79 ++++++++++++++++++
.../idp/storage/service/IdpGroupMetaService.java | 10 +--
.../apache/gravitino/idp/dto/TestIdpGroupDTO.java | 77 ++++++++++++++++++
.../apache/gravitino/idp/dto/TestIdpUserDTO.java | 77 ++++++++++++++++++
.../idp/dto/requests/TestAddGroupRequest.java | 58 +++++++++++++
.../idp/dto/requests/TestAddUserRequest.java | 71 ++++++++++++++++
.../requests/TestGroupMembershipChangeRequest.java | 95 ++++++++++++++++++++++
.../idp/dto/requests/TestResetPasswordRequest.java | 68 ++++++++++++++++
.../idp/dto/responses/TestIdpGroupResponse.java | 94 +++++++++++++++++++++
.../idp/dto/responses/TestIdpUserResponse.java | 94 +++++++++++++++++++++
20 files changed, 1275 insertions(+), 18 deletions(-)
diff --git a/design-docs/gravitino-local-authentication.md
b/design-docs/gravitino-local-authentication.md
index 498ff153ba..42f0dc2e29 100644
--- a/design-docs/gravitino-local-authentication.md
+++ b/design-docs/gravitino-local-authentication.md
@@ -518,9 +518,9 @@ http://localhost:8090/api/idp/users/alice
}
```
-#### 9.1.2 Create a local user
+#### 9.1.2 Add a local user
-You can create a local user by providing a user name and password. The
password must be stored as a
+You can add a local user by providing a user name and password. The password
must be stored as a
hash rather than plaintext.
The request path for REST API is `/api/idp/users`.
@@ -631,9 +631,9 @@ http://localhost:8090/api/idp/groups/engineering
}
```
-#### 9.1.6 Create a local group
+#### 9.1.6 Add a local group
-You can create a local group by providing a group name.
+You can add a local group by providing a group name.
The request path for REST API is `/api/idp/groups`.
diff --git a/plugins/idp-basic/build.gradle.kts
b/plugins/idp-basic/build.gradle.kts
index a977c0d581..26182a1608 100644
--- a/plugins/idp-basic/build.gradle.kts
+++ b/plugins/idp-basic/build.gradle.kts
@@ -32,6 +32,8 @@ dependencies {
implementation(libs.bcprov.jdk18on)
implementation(libs.commons.lang3)
implementation(libs.guava)
+ implementation(libs.jackson.annotations)
+ implementation(libs.jackson.databind)
implementation(libs.mybatis)
compileOnly(libs.lombok)
@@ -42,9 +44,9 @@ dependencies {
testImplementation(project(":integration-test-common", "testArtifacts"))
testImplementation(libs.awaitility)
+ testImplementation(libs.commons.io)
testImplementation(libs.junit.jupiter.api)
testImplementation(libs.junit.jupiter.params)
- testImplementation(libs.commons.io)
testImplementation(libs.mysql.driver)
testImplementation(libs.postgresql.driver)
testImplementation(libs.testcontainers)
diff --git
a/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/IdpUserGroupManager.java
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/IdpUserGroupManager.java
index 38065951a4..bb26bfa4fc 100644
---
a/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/IdpUserGroupManager.java
+++
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/IdpUserGroupManager.java
@@ -147,18 +147,19 @@ public class IdpUserGroupManager implements Closeable {
* Changes built-in IdP group membership.
*
* @param groupName The group name.
- * @param additions The usernames to add, or null if none.
- * @param removals The usernames to remove, or null if none.
+ * @param usersToAdd The usernames to add, or null if none.
+ * @param usersToRemove The usernames to remove, or null if none.
* @return The updated built-in IdP group.
*/
public IdpGroup changeGroupMembership(
- String groupName, @Nullable List<String> additions, @Nullable
List<String> removals) {
- List<String> additionsList = additions == null ? Collections.emptyList() :
additions;
- List<String> removalsList = removals == null ? Collections.emptyList() :
removals;
+ String groupName, @Nullable List<String> usersToAdd, @Nullable
List<String> usersToRemove) {
+ List<String> usersToAddList = usersToAdd == null ? Collections.emptyList()
: usersToAdd;
+ List<String> usersToRemoveList =
+ usersToRemove == null ? Collections.emptyList() : usersToRemove;
Preconditions.checkArgument(
- !additionsList.isEmpty() || !removalsList.isEmpty(),
- "additions and removals cannot both be empty");
- GROUP_SERVICE.changeGroupMembership(groupName, additionsList,
removalsList);
+ !usersToAddList.isEmpty() || !usersToRemoveList.isEmpty(),
+ "usersToAdd and usersToRemove cannot both be empty");
+ GROUP_SERVICE.changeGroupMembership(groupName, usersToAddList,
usersToRemoveList);
return getGroup(groupName);
}
diff --git
a/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/dto/IdpGroupDTO.java
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/dto/IdpGroupDTO.java
new file mode 100644
index 0000000000..2fc1120466
--- /dev/null
+++
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/dto/IdpGroupDTO.java
@@ -0,0 +1,82 @@
+/*
+ * 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.gravitino.idp.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonSetter;
+import com.fasterxml.jackson.annotation.Nulls;
+import com.google.common.base.Preconditions;
+import java.util.Collections;
+import java.util.List;
+import lombok.AccessLevel;
+import lombok.Builder;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import lombok.ToString;
+import org.apache.commons.lang3.StringUtils;
+
+/** Represents a built-in IdP group Data Transfer Object (DTO). */
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+@EqualsAndHashCode
+@ToString
+public class IdpGroupDTO {
+
+ @JsonProperty("name")
+ private String name;
+
+ @JsonProperty("users")
+ @JsonSetter(nulls = Nulls.AS_EMPTY)
+ private List<String> users = Collections.emptyList();
+
+ /**
+ * Creates a new instance of IdpGroupDTO.
+ *
+ * @param name The name of the built-in IdP group DTO.
+ * @param users The users of the built-in IdP group DTO.
+ */
+ @Builder(setterPrefix = "with")
+ protected IdpGroupDTO(String name, List<String> users) {
+ Preconditions.checkArgument(StringUtils.isNotBlank(name), "name cannot be
null or empty");
+ if (users != null) {
+ users.forEach(
+ user ->
+ Preconditions.checkArgument(
+ StringUtils.isNotBlank(user), "users cannot contain null or
empty user names"));
+ }
+ this.name = name;
+ this.users = users == null ? Collections.emptyList() : users;
+ }
+
+ /**
+ * @return The name of the built-in IdP group DTO.
+ */
+ public String name() {
+ return name;
+ }
+
+ /**
+ * The users of the built-in IdP group. A group can contain multiple users.
+ *
+ * @return The users of the built-in IdP group.
+ */
+ public List<String> users() {
+ return users;
+ }
+}
diff --git
a/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/dto/IdpUserDTO.java
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/dto/IdpUserDTO.java
new file mode 100644
index 0000000000..80ec5c4e76
--- /dev/null
+++
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/dto/IdpUserDTO.java
@@ -0,0 +1,83 @@
+/*
+ * 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.gravitino.idp.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonSetter;
+import com.fasterxml.jackson.annotation.Nulls;
+import com.google.common.base.Preconditions;
+import java.util.Collections;
+import java.util.List;
+import lombok.AccessLevel;
+import lombok.Builder;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import lombok.ToString;
+import org.apache.commons.lang3.StringUtils;
+
+/** Represents a built-in IdP user Data Transfer Object (DTO). */
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+@EqualsAndHashCode
+@ToString
+public class IdpUserDTO {
+
+ @JsonProperty("name")
+ private String name;
+
+ @JsonProperty("groups")
+ @JsonSetter(nulls = Nulls.AS_EMPTY)
+ private List<String> groups = Collections.emptyList();
+
+ /**
+ * Creates a new instance of IdpUserDTO.
+ *
+ * @param name The name of the built-in IdP user DTO.
+ * @param groups The groups of the built-in IdP user DTO.
+ */
+ @Builder(setterPrefix = "with")
+ protected IdpUserDTO(String name, List<String> groups) {
+ Preconditions.checkArgument(StringUtils.isNotBlank(name), "name cannot be
null or empty");
+ if (groups != null) {
+ groups.forEach(
+ group ->
+ Preconditions.checkArgument(
+ StringUtils.isNotBlank(group),
+ "groups cannot contain null or empty group names"));
+ }
+ this.name = name;
+ this.groups = groups == null ? Collections.emptyList() : groups;
+ }
+
+ /**
+ * @return The name of the built-in IdP user DTO.
+ */
+ public String name() {
+ return name;
+ }
+
+ /**
+ * The groups of the built-in IdP user. A user can belong to multiple groups.
+ *
+ * @return The groups of the built-in IdP user.
+ */
+ public List<String> groups() {
+ return groups;
+ }
+}
diff --git
a/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/dto/requests/AddGroupRequest.java
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/dto/requests/AddGroupRequest.java
new file mode 100644
index 0000000000..54e0c92c5d
--- /dev/null
+++
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/dto/requests/AddGroupRequest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.gravitino.idp.dto.requests;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Preconditions;
+import lombok.Builder;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.ToString;
+import lombok.extern.jackson.Jacksonized;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.gravitino.rest.RESTRequest;
+
+/** Represents a request to add a built-in IdP group. */
+@Getter
+@EqualsAndHashCode
+@ToString
+@Builder
+@Jacksonized
+public class AddGroupRequest implements RESTRequest {
+
+ @JsonProperty("group")
+ private final String group;
+
+ /** Default constructor for AddGroupRequest. (Used for Jackson
deserialization.) */
+ public AddGroupRequest() {
+ this(null);
+ }
+
+ /**
+ * Creates a new AddGroupRequest.
+ *
+ * @param group The group name of the built-in IdP group.
+ */
+ public AddGroupRequest(String group) {
+ super();
+ this.group = group;
+ }
+
+ /**
+ * Validates the {@link AddGroupRequest} request.
+ *
+ * @throws IllegalArgumentException If the request is invalid, this
exception is thrown.
+ */
+ @Override
+ public void validate() throws IllegalArgumentException {
+ Preconditions.checkArgument(
+ StringUtils.isNotBlank(group), "\"group\" field is required and cannot
be empty");
+ }
+}
diff --git
a/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/dto/requests/AddUserRequest.java
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/dto/requests/AddUserRequest.java
new file mode 100644
index 0000000000..8625eff833
--- /dev/null
+++
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/dto/requests/AddUserRequest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.gravitino.idp.dto.requests;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Preconditions;
+import lombok.Builder;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.ToString;
+import lombok.extern.jackson.Jacksonized;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.gravitino.rest.RESTRequest;
+
+/** Represents a request to add a built-in IdP user. */
+@Getter
+@EqualsAndHashCode
+@ToString
+@Builder
+@Jacksonized
+public class AddUserRequest implements RESTRequest {
+
+ @JsonProperty("user")
+ private final String user;
+
+ @JsonProperty("password")
+ @ToString.Exclude
+ private final String password;
+
+ /** Default constructor for AddUserRequest. (Used for Jackson
deserialization.) */
+ public AddUserRequest() {
+ this(null, null);
+ }
+
+ /**
+ * Creates a new AddUserRequest.
+ *
+ * @param user The user name of the built-in IdP user.
+ * @param password The password of the built-in IdP user.
+ */
+ public AddUserRequest(String user, String password) {
+ super();
+ this.user = user;
+ this.password = password;
+ }
+
+ /**
+ * Validates the {@link AddUserRequest} request.
+ *
+ * @throws IllegalArgumentException If the request is invalid, this
exception is thrown.
+ */
+ @Override
+ public void validate() throws IllegalArgumentException {
+ Preconditions.checkArgument(
+ StringUtils.isNotBlank(user), "\"user\" field is required and cannot
be empty");
+ Preconditions.checkArgument(
+ StringUtils.isNotBlank(password), "\"password\" field is required and
cannot be empty");
+ }
+}
diff --git
a/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/dto/requests/GroupMembershipChangeRequest.java
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/dto/requests/GroupMembershipChangeRequest.java
new file mode 100644
index 0000000000..1cc7b07f7b
--- /dev/null
+++
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/dto/requests/GroupMembershipChangeRequest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.gravitino.idp.dto.requests;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Preconditions;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.ToString;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.gravitino.rest.RESTRequest;
+
+/** Represents a request to change built-in IdP group membership. */
+@Getter
+@EqualsAndHashCode
+@ToString
+public class GroupMembershipChangeRequest implements RESTRequest {
+
+ @JsonProperty("usersToAdd")
+ private final String[] usersToAdd;
+
+ @JsonProperty("usersToRemove")
+ private final String[] usersToRemove;
+
+ /**
+ * Creates a new GroupMembershipChangeRequest.
+ *
+ * @param usersToAdd The user names to add to the built-in IdP group.
+ * @param usersToRemove The user names to remove from the built-in IdP group.
+ */
+ public GroupMembershipChangeRequest(String[] usersToAdd, String[]
usersToRemove) {
+ this.usersToAdd = usersToAdd;
+ this.usersToRemove = usersToRemove;
+ }
+
+ /** Default constructor for GroupMembershipChangeRequest. (Used for Jackson
deserialization.) */
+ public GroupMembershipChangeRequest() {
+ this(null, null);
+ }
+
+ /**
+ * Validates the {@link GroupMembershipChangeRequest} request.
+ *
+ * @throws IllegalArgumentException If the request is invalid, this
exception is thrown.
+ */
+ @Override
+ public void validate() throws IllegalArgumentException {
+ Preconditions.checkArgument(
+ usersToAdd != null || usersToRemove != null,
+ "usersToAdd and usersToRemove cannot both be null");
+
+ if (usersToAdd != null) {
+ for (String user : usersToAdd) {
+ Preconditions.checkArgument(
+ StringUtils.isNotBlank(user), "usersToAdd must not contain null or
empty user names");
+ }
+ }
+
+ if (usersToRemove != null) {
+ for (String user : usersToRemove) {
+ Preconditions.checkArgument(
+ StringUtils.isNotBlank(user),
+ "usersToRemove must not contain null or empty user names");
+ }
+ }
+ }
+}
diff --git
a/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/dto/requests/ResetPasswordRequest.java
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/dto/requests/ResetPasswordRequest.java
new file mode 100644
index 0000000000..a6b1908b33
--- /dev/null
+++
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/dto/requests/ResetPasswordRequest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.gravitino.idp.dto.requests;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Preconditions;
+import lombok.Builder;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.ToString;
+import lombok.extern.jackson.Jacksonized;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.gravitino.rest.RESTRequest;
+
+/** Represents a request to reset a built-in IdP user password. */
+@Getter
+@EqualsAndHashCode
+@ToString
+@Builder
+@Jacksonized
+public class ResetPasswordRequest implements RESTRequest {
+
+ @JsonProperty("password")
+ @ToString.Exclude
+ private final String password;
+
+ /** Default constructor for ResetPasswordRequest. (Used for Jackson
deserialization.) */
+ public ResetPasswordRequest() {
+ this(null);
+ }
+
+ /**
+ * Creates a new ResetPasswordRequest.
+ *
+ * @param password The new password of the built-in IdP user.
+ */
+ public ResetPasswordRequest(String password) {
+ super();
+ this.password = password;
+ }
+
+ /**
+ * Validates the {@link ResetPasswordRequest} request.
+ *
+ * @throws IllegalArgumentException If the request is invalid, this
exception is thrown.
+ */
+ @Override
+ public void validate() throws IllegalArgumentException {
+ Preconditions.checkArgument(
+ StringUtils.isNotBlank(password), "\"password\" field is required and
cannot be empty");
+ }
+}
diff --git
a/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/dto/responses/IdpGroupResponse.java
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/dto/responses/IdpGroupResponse.java
new file mode 100644
index 0000000000..b8a81f2408
--- /dev/null
+++
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/dto/responses/IdpGroupResponse.java
@@ -0,0 +1,79 @@
+/*
+ * 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.gravitino.idp.dto.responses;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Preconditions;
+import java.util.List;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.ToString;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.gravitino.dto.responses.BaseResponse;
+import org.apache.gravitino.idp.dto.IdpGroupDTO;
+
+/** Represents a response for a built-in IdP group. */
+@Getter
+@ToString
+@EqualsAndHashCode(callSuper = true)
+public class IdpGroupResponse extends BaseResponse {
+
+ @JsonProperty("group")
+ private final IdpGroupDTO group;
+
+ /**
+ * Constructor for IdpGroupResponse.
+ *
+ * @param group The built-in IdP group data transfer object.
+ */
+ public IdpGroupResponse(IdpGroupDTO group) {
+ super(0);
+ this.group = group;
+ }
+
+ /** Default constructor for IdpGroupResponse. (Used for Jackson
deserialization.) */
+ public IdpGroupResponse() {
+ super();
+ this.group = null;
+ }
+
+ /**
+ * Validates the response data.
+ *
+ * @throws IllegalArgumentException if the name is not set.
+ */
+ @Override
+ public void validate() throws IllegalArgumentException {
+ super.validate();
+
+ Preconditions.checkArgument(group != null, "group must not be null");
+ Preconditions.checkArgument(
+ StringUtils.isNotBlank(group.name()), "group 'name' must not be null
or empty");
+ validateNames(group.users(), "group 'users' must not contain null or empty
user names");
+ }
+
+ private void validateNames(List<String> names, String errorMessage) {
+ if (names == null) {
+ return;
+ }
+
+ names.forEach(name ->
Preconditions.checkArgument(StringUtils.isNotBlank(name), errorMessage));
+ }
+}
diff --git
a/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/dto/responses/IdpUserResponse.java
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/dto/responses/IdpUserResponse.java
new file mode 100644
index 0000000000..97ddd2cfc3
--- /dev/null
+++
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/dto/responses/IdpUserResponse.java
@@ -0,0 +1,79 @@
+/*
+ * 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.gravitino.idp.dto.responses;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Preconditions;
+import java.util.List;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.ToString;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.gravitino.dto.responses.BaseResponse;
+import org.apache.gravitino.idp.dto.IdpUserDTO;
+
+/** Represents a response for a built-in IdP user. */
+@Getter
+@ToString
+@EqualsAndHashCode(callSuper = true)
+public class IdpUserResponse extends BaseResponse {
+
+ @JsonProperty("user")
+ private final IdpUserDTO user;
+
+ /**
+ * Constructor for IdpUserResponse.
+ *
+ * @param user The built-in IdP user data transfer object.
+ */
+ public IdpUserResponse(IdpUserDTO user) {
+ super(0);
+ this.user = user;
+ }
+
+ /** Default constructor for IdpUserResponse. (Used for Jackson
deserialization.) */
+ public IdpUserResponse() {
+ super();
+ this.user = null;
+ }
+
+ /**
+ * Validates the response data.
+ *
+ * @throws IllegalArgumentException if the name is not set.
+ */
+ @Override
+ public void validate() throws IllegalArgumentException {
+ super.validate();
+
+ Preconditions.checkArgument(user != null, "user must not be null");
+ Preconditions.checkArgument(
+ StringUtils.isNotBlank(user.name()), "user 'name' must not be null or
empty");
+ validateNames(user.groups(), "user 'groups' must not contain null or empty
group names");
+ }
+
+ private void validateNames(List<String> names, String errorMessage) {
+ if (names == null) {
+ return;
+ }
+
+ names.forEach(name ->
Preconditions.checkArgument(StringUtils.isNotBlank(name), errorMessage));
+ }
+}
diff --git
a/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/storage/service/IdpGroupMetaService.java
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/storage/service/IdpGroupMetaService.java
index 09313581f9..93394b8475 100644
---
a/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/storage/service/IdpGroupMetaService.java
+++
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/storage/service/IdpGroupMetaService.java
@@ -122,14 +122,14 @@ public class IdpGroupMetaService {
* Changes built-in IdP group membership in a single transaction.
*
* @param groupName The group name.
- * @param additions The usernames to add.
- * @param removals The usernames to remove.
+ * @param usersToAdd The usernames to add.
+ * @param usersToRemove The usernames to remove.
*/
@Monitored(
metricsSource = GRAVITINO_RELATIONAL_STORE_METRIC_NAME,
baseMetricName = "changeGroupMembership")
public void changeGroupMembership(
- String groupName, List<String> additions, List<String> removals) {
+ String groupName, List<String> usersToAdd, List<String> usersToRemove) {
SessionUtils.doMultipleWithCommit(
() -> {
IdpGroupPO group =
@@ -149,8 +149,8 @@ public class IdpGroupMetaService {
mapper -> mapper.selectUsernamesByGroupName(groupName));
Set<String> oldUsernames = Sets.newHashSet(currentUsernames);
Set<String> newUsernames = Sets.newHashSet(oldUsernames);
- newUsernames.addAll(additions);
- newUsernames.removeAll(removals);
+ newUsernames.addAll(usersToAdd);
+ newUsernames.removeAll(usersToRemove);
Set<String> insertUsernames = Sets.difference(newUsernames,
oldUsernames);
Set<String> deleteUsernames = Sets.difference(oldUsernames,
newUsernames);
diff --git
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/dto/TestIdpGroupDTO.java
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/dto/TestIdpGroupDTO.java
new file mode 100644
index 0000000000..d0b5bd75ac
--- /dev/null
+++
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/dto/TestIdpGroupDTO.java
@@ -0,0 +1,77 @@
+/*
+ * 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.gravitino.idp.dto;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import java.util.Arrays;
+import java.util.Collections;
+import org.apache.gravitino.json.JsonUtils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class TestIdpGroupDTO {
+
+ @Test
+ public void testIdpGroupDTOSerDe() throws JsonProcessingException {
+ IdpGroupDTO groupDTO =
+ IdpGroupDTO.builder()
+ .withName("test_group")
+ .withUsers(Arrays.asList("user1", "user2"))
+ .build();
+
+ String json = JsonUtils.objectMapper().writeValueAsString(groupDTO);
+ IdpGroupDTO deserialized = JsonUtils.objectMapper().readValue(json,
IdpGroupDTO.class);
+
+ Assertions.assertEquals(groupDTO, deserialized);
+ Assertions.assertEquals("test_group", deserialized.name());
+ Assertions.assertEquals(Arrays.asList("user1", "user2"),
deserialized.users());
+
+ // Test with default users
+ IdpGroupDTO groupDTO1 =
IdpGroupDTO.builder().withName("test_group").build();
+
+ String json1 = JsonUtils.objectMapper().writeValueAsString(groupDTO1);
+ IdpGroupDTO deserialized1 = JsonUtils.objectMapper().readValue(json1,
IdpGroupDTO.class);
+
+ Assertions.assertEquals(groupDTO1, deserialized1);
+ Assertions.assertEquals("test_group", deserialized1.name());
+ Assertions.assertTrue(deserialized1.users().isEmpty());
+ Assertions.assertEquals("IdpGroupDTO(name=test_group, users=[])",
deserialized1.toString());
+
+ IdpGroupDTO deserializedWithNullUsers =
+ JsonUtils.objectMapper()
+ .readValue("{\"name\":\"test_group\",\"users\":null}",
IdpGroupDTO.class);
+ Assertions.assertTrue(deserializedWithNullUsers.users().isEmpty());
+ Assertions.assertEquals(
+ "IdpGroupDTO(name=test_group, users=[])",
deserializedWithNullUsers.toString());
+
+ Assertions.assertThrows(
+ IllegalArgumentException.class, () -> IdpGroupDTO.builder().withName("
").build());
+ IllegalArgumentException invalidUserException =
+ Assertions.assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ IdpGroupDTO.builder()
+ .withName("test_group")
+ .withUsers(Collections.singletonList(" "))
+ .build());
+ Assertions.assertEquals(
+ "users cannot contain null or empty user names",
invalidUserException.getMessage());
+ }
+}
diff --git
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/dto/TestIdpUserDTO.java
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/dto/TestIdpUserDTO.java
new file mode 100644
index 0000000000..b0cb2545bc
--- /dev/null
+++
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/dto/TestIdpUserDTO.java
@@ -0,0 +1,77 @@
+/*
+ * 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.gravitino.idp.dto;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import java.util.Arrays;
+import java.util.Collections;
+import org.apache.gravitino.json.JsonUtils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class TestIdpUserDTO {
+
+ @Test
+ public void testIdpUserDTOSerDe() throws JsonProcessingException {
+ IdpUserDTO userDTO =
+ IdpUserDTO.builder()
+ .withName("test_user")
+ .withGroups(Arrays.asList("group1", "group2"))
+ .build();
+
+ String json = JsonUtils.objectMapper().writeValueAsString(userDTO);
+ IdpUserDTO deserialized = JsonUtils.objectMapper().readValue(json,
IdpUserDTO.class);
+
+ Assertions.assertEquals(userDTO, deserialized);
+ Assertions.assertEquals("test_user", deserialized.name());
+ Assertions.assertEquals(Arrays.asList("group1", "group2"),
deserialized.groups());
+
+ // Test with default groups
+ IdpUserDTO userDTO1 = IdpUserDTO.builder().withName("test_user").build();
+
+ String json1 = JsonUtils.objectMapper().writeValueAsString(userDTO1);
+ IdpUserDTO deserialized1 = JsonUtils.objectMapper().readValue(json1,
IdpUserDTO.class);
+
+ Assertions.assertEquals(userDTO1, deserialized1);
+ Assertions.assertEquals("test_user", deserialized1.name());
+ Assertions.assertTrue(deserialized1.groups().isEmpty());
+ Assertions.assertEquals("IdpUserDTO(name=test_user, groups=[])",
deserialized1.toString());
+
+ IdpUserDTO deserializedWithNullGroups =
+ JsonUtils.objectMapper()
+ .readValue("{\"name\":\"test_user\",\"groups\":null}",
IdpUserDTO.class);
+ Assertions.assertTrue(deserializedWithNullGroups.groups().isEmpty());
+ Assertions.assertEquals(
+ "IdpUserDTO(name=test_user, groups=[])",
deserializedWithNullGroups.toString());
+
+ Assertions.assertThrows(
+ IllegalArgumentException.class, () -> IdpUserDTO.builder().withName("
").build());
+ IllegalArgumentException invalidGroupException =
+ Assertions.assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ IdpUserDTO.builder()
+ .withName("test_user")
+ .withGroups(Collections.singletonList(" "))
+ .build());
+ Assertions.assertEquals(
+ "groups cannot contain null or empty group names",
invalidGroupException.getMessage());
+ }
+}
diff --git
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/dto/requests/TestAddGroupRequest.java
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/dto/requests/TestAddGroupRequest.java
new file mode 100644
index 0000000000..cdc24d1f60
--- /dev/null
+++
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/dto/requests/TestAddGroupRequest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.gravitino.idp.dto.requests;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import org.apache.gravitino.json.JsonUtils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class TestAddGroupRequest {
+
+ @Test
+ public void testAddGroupRequestSerDe() throws JsonProcessingException {
+ AddGroupRequest request = new AddGroupRequest("test_group");
+
+ String serJson = JsonUtils.objectMapper().writeValueAsString(request);
+ AddGroupRequest deserRequest =
+ JsonUtils.objectMapper().readValue(serJson, AddGroupRequest.class);
+
+ Assertions.assertEquals(request, deserRequest);
+ Assertions.assertEquals("test_group", deserRequest.getGroup());
+
+ // Test with null group
+ AddGroupRequest request1 = new AddGroupRequest();
+
+ String serJson1 = JsonUtils.objectMapper().writeValueAsString(request1);
+ AddGroupRequest deserRequest1 =
+ JsonUtils.objectMapper().readValue(serJson1, AddGroupRequest.class);
+
+ Assertions.assertEquals(request1, deserRequest1);
+ Assertions.assertNull(deserRequest1.getGroup());
+ }
+
+ @Test
+ public void testAddGroupRequestValidate() {
+ Assertions.assertDoesNotThrow(() -> new
AddGroupRequest("test_group").validate());
+ Assertions.assertThrows(IllegalArgumentException.class, () -> new
AddGroupRequest().validate());
+ Assertions.assertThrows(
+ IllegalArgumentException.class, () -> new AddGroupRequest("
").validate());
+ }
+}
diff --git
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/dto/requests/TestAddUserRequest.java
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/dto/requests/TestAddUserRequest.java
new file mode 100644
index 0000000000..cbe3618945
--- /dev/null
+++
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/dto/requests/TestAddUserRequest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.gravitino.idp.dto.requests;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import org.apache.gravitino.json.JsonUtils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class TestAddUserRequest {
+
+ @Test
+ public void testAddUserRequestSerDe() throws JsonProcessingException {
+ AddUserRequest request = new AddUserRequest("test_user", "password");
+
+ String serJson = JsonUtils.objectMapper().writeValueAsString(request);
+ AddUserRequest deserRequest = JsonUtils.objectMapper().readValue(serJson,
AddUserRequest.class);
+
+ Assertions.assertEquals(request, deserRequest);
+ Assertions.assertEquals("test_user", deserRequest.getUser());
+ Assertions.assertEquals("password", deserRequest.getPassword());
+
+ // Test with null user and password
+ AddUserRequest request1 = new AddUserRequest();
+
+ String serJson1 = JsonUtils.objectMapper().writeValueAsString(request1);
+ AddUserRequest deserRequest1 =
+ JsonUtils.objectMapper().readValue(serJson1, AddUserRequest.class);
+
+ Assertions.assertEquals(request1, deserRequest1);
+ Assertions.assertNull(deserRequest1.getUser());
+ Assertions.assertNull(deserRequest1.getPassword());
+ }
+
+ @Test
+ public void testAddUserRequestValidate() {
+ Assertions.assertDoesNotThrow(() -> new AddUserRequest("test_user",
"password").validate());
+ Assertions.assertThrows(IllegalArgumentException.class, () -> new
AddUserRequest().validate());
+ Assertions.assertThrows(
+ IllegalArgumentException.class, () -> new AddUserRequest(" ",
"password").validate());
+ Assertions.assertThrows(
+ IllegalArgumentException.class, () -> new AddUserRequest("test_user",
" ").validate());
+ }
+
+ @Test
+ public void testAddUserRequestToStringDoesNotExposePassword() {
+ String requestString = new AddUserRequest("test_user",
"password").toString();
+
+ Assertions.assertTrue(requestString.contains("test_user"));
+ Assertions.assertFalse(requestString.contains("password="));
+ Assertions.assertFalse(requestString.contains("password)"));
+ Assertions.assertFalse(requestString.contains("\"password\""));
+ }
+}
diff --git
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/dto/requests/TestGroupMembershipChangeRequest.java
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/dto/requests/TestGroupMembershipChangeRequest.java
new file mode 100644
index 0000000000..51db3b5fb8
--- /dev/null
+++
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/dto/requests/TestGroupMembershipChangeRequest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.gravitino.idp.dto.requests;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import org.apache.gravitino.json.JsonUtils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class TestGroupMembershipChangeRequest {
+
+ @Test
+ public void testGroupMembershipChangeRequestSerDe() throws
JsonProcessingException {
+ GroupMembershipChangeRequest request =
+ new GroupMembershipChangeRequest(new String[] {"user1", "user2"}, new
String[] {"user3"});
+
+ String serJson = JsonUtils.objectMapper().writeValueAsString(request);
+ GroupMembershipChangeRequest deserRequest =
+ JsonUtils.objectMapper().readValue(serJson,
GroupMembershipChangeRequest.class);
+
+ Assertions.assertEquals(request, deserRequest);
+ Assertions.assertArrayEquals(new String[] {"user1", "user2"},
deserRequest.getUsersToAdd());
+ Assertions.assertArrayEquals(new String[] {"user3"},
deserRequest.getUsersToRemove());
+
+ GroupMembershipChangeRequest request1 = new GroupMembershipChangeRequest();
+
+ String serJson1 = JsonUtils.objectMapper().writeValueAsString(request1);
+ GroupMembershipChangeRequest deserRequest1 =
+ JsonUtils.objectMapper().readValue(serJson1,
GroupMembershipChangeRequest.class);
+
+ Assertions.assertEquals(request1, deserRequest1);
+ Assertions.assertNull(deserRequest1.getUsersToAdd());
+ Assertions.assertNull(deserRequest1.getUsersToRemove());
+ }
+
+ @Test
+ public void testGroupMembershipChangeRequestValidate() {
+ Assertions.assertDoesNotThrow(
+ () ->
+ new GroupMembershipChangeRequest(new String[] {"user1"}, new
String[] {"user2"})
+ .validate());
+ Assertions.assertDoesNotThrow(
+ () -> new GroupMembershipChangeRequest(new String[] {"user1"},
null).validate());
+ Assertions.assertDoesNotThrow(
+ () -> new GroupMembershipChangeRequest(null, new String[]
{"user2"}).validate());
+ Assertions.assertThrows(
+ IllegalArgumentException.class,
+ () -> new GroupMembershipChangeRequest(null, null).validate());
+ Assertions.assertThrows(
+ IllegalArgumentException.class,
+ () -> new GroupMembershipChangeRequest(new String[] {" "},
null).validate());
+ Assertions.assertThrows(
+ IllegalArgumentException.class,
+ () -> new GroupMembershipChangeRequest(null, new String[] {"user1",
""}).validate());
+ }
+
+ @Test
+ public void testGroupMembershipChangeRequestValidateNullUserInUsersToAdd() {
+ IllegalArgumentException exception =
+ Assertions.assertThrows(
+ IllegalArgumentException.class,
+ () -> new GroupMembershipChangeRequest(new String[] {"user1",
null}, null).validate());
+
+ Assertions.assertEquals(
+ "usersToAdd must not contain null or empty user names",
exception.getMessage());
+ }
+
+ @Test
+ public void
testGroupMembershipChangeRequestValidateNullUserInUsersToRemove() {
+ IllegalArgumentException exception =
+ Assertions.assertThrows(
+ IllegalArgumentException.class,
+ () -> new GroupMembershipChangeRequest(null, new String[]
{null}).validate());
+
+ Assertions.assertEquals(
+ "usersToRemove must not contain null or empty user names",
exception.getMessage());
+ }
+}
diff --git
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/dto/requests/TestResetPasswordRequest.java
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/dto/requests/TestResetPasswordRequest.java
new file mode 100644
index 0000000000..aa6ed997d7
--- /dev/null
+++
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/dto/requests/TestResetPasswordRequest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.gravitino.idp.dto.requests;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import org.apache.gravitino.json.JsonUtils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class TestResetPasswordRequest {
+
+ @Test
+ public void testResetPasswordRequestSerDe() throws JsonProcessingException {
+ ResetPasswordRequest request = new ResetPasswordRequest("new_password");
+
+ String serJson = JsonUtils.objectMapper().writeValueAsString(request);
+ ResetPasswordRequest deserRequest =
+ JsonUtils.objectMapper().readValue(serJson,
ResetPasswordRequest.class);
+
+ Assertions.assertEquals(request, deserRequest);
+ Assertions.assertEquals("new_password", deserRequest.getPassword());
+
+ // Test with null password
+ ResetPasswordRequest request1 = new ResetPasswordRequest();
+
+ String serJson1 = JsonUtils.objectMapper().writeValueAsString(request1);
+ ResetPasswordRequest deserRequest1 =
+ JsonUtils.objectMapper().readValue(serJson1,
ResetPasswordRequest.class);
+
+ Assertions.assertEquals(request1, deserRequest1);
+ Assertions.assertNull(deserRequest1.getPassword());
+ }
+
+ @Test
+ public void testResetPasswordRequestValidate() {
+ Assertions.assertDoesNotThrow(() -> new
ResetPasswordRequest("new_password").validate());
+ Assertions.assertThrows(
+ IllegalArgumentException.class, () -> new
ResetPasswordRequest().validate());
+ Assertions.assertThrows(
+ IllegalArgumentException.class, () -> new ResetPasswordRequest("
").validate());
+ }
+
+ @Test
+ public void testResetPasswordRequestToStringDoesNotExposePassword() {
+ String requestString = new ResetPasswordRequest("new_password").toString();
+
+ Assertions.assertFalse(requestString.contains("new_password"));
+ Assertions.assertFalse(requestString.contains("password="));
+ Assertions.assertFalse(requestString.contains("\"password\""));
+ }
+}
diff --git
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/dto/responses/TestIdpGroupResponse.java
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/dto/responses/TestIdpGroupResponse.java
new file mode 100644
index 0000000000..48e07b4dcd
--- /dev/null
+++
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/dto/responses/TestIdpGroupResponse.java
@@ -0,0 +1,94 @@
+/*
+ * 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.gravitino.idp.dto.responses;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import java.util.Arrays;
+import org.apache.gravitino.idp.dto.IdpGroupDTO;
+import org.apache.gravitino.json.JsonUtils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class TestIdpGroupResponse {
+
+ @Test
+ public void testIdpGroupResponseSerDe() throws JsonProcessingException {
+ IdpGroupDTO group =
+ IdpGroupDTO.builder()
+ .withName("test_group")
+ .withUsers(Arrays.asList("user1", "user2"))
+ .build();
+ IdpGroupResponse response = new IdpGroupResponse(group);
+
+ String serJson = JsonUtils.objectMapper().writeValueAsString(response);
+ IdpGroupResponse deserResponse =
+ JsonUtils.objectMapper().readValue(serJson, IdpGroupResponse.class);
+
+ Assertions.assertEquals(response, deserResponse);
+ Assertions.assertEquals("test_group", deserResponse.getGroup().name());
+ Assertions.assertEquals(Arrays.asList("user1", "user2"),
deserResponse.getGroup().users());
+ }
+
+ @Test
+ public void testIdpGroupResponseValidate() {
+ IdpGroupDTO group =
+ IdpGroupDTO.builder()
+ .withName("test_group")
+ .withUsers(Arrays.asList("user1", "user2"))
+ .build();
+ IdpGroupResponse response = new IdpGroupResponse(group);
+ response.validate(); // No exception thrown
+ }
+
+ @Test
+ public void testIdpGroupResponseException() {
+ IdpGroupResponse response = new IdpGroupResponse();
+ Assertions.assertThrows(IllegalArgumentException.class,
response::validate);
+ }
+
+ @Test
+ public void testIdpGroupResponseBlankNameMessage() throws
JsonProcessingException {
+ IdpGroupResponse response =
+ JsonUtils.objectMapper()
+ .readValue(
+ "{\"code\":0,\"group\":{\"name\":\"
\",\"users\":[\"user1\"]}}",
+ IdpGroupResponse.class);
+
+ IllegalArgumentException exception =
+ Assertions.assertThrows(IllegalArgumentException.class,
response::validate);
+
+ Assertions.assertEquals("group 'name' must not be null or empty",
exception.getMessage());
+ }
+
+ @Test
+ public void testIdpGroupResponseBlankUserMessage() throws
JsonProcessingException {
+ IdpGroupResponse response =
+ JsonUtils.objectMapper()
+ .readValue(
+ "{\"code\":0,\"group\":{\"name\":\"test_group\",\"users\":[\"
\"]}}",
+ IdpGroupResponse.class);
+
+ IllegalArgumentException exception =
+ Assertions.assertThrows(IllegalArgumentException.class,
response::validate);
+
+ Assertions.assertEquals(
+ "group 'users' must not contain null or empty user names",
exception.getMessage());
+ }
+}
diff --git
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/dto/responses/TestIdpUserResponse.java
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/dto/responses/TestIdpUserResponse.java
new file mode 100644
index 0000000000..3fa33f5269
--- /dev/null
+++
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/dto/responses/TestIdpUserResponse.java
@@ -0,0 +1,94 @@
+/*
+ * 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.gravitino.idp.dto.responses;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import java.util.Arrays;
+import org.apache.gravitino.idp.dto.IdpUserDTO;
+import org.apache.gravitino.json.JsonUtils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class TestIdpUserResponse {
+
+ @Test
+ public void testIdpUserResponseSerDe() throws JsonProcessingException {
+ IdpUserDTO user =
+ IdpUserDTO.builder()
+ .withName("test_user")
+ .withGroups(Arrays.asList("group1", "group2"))
+ .build();
+ IdpUserResponse response = new IdpUserResponse(user);
+
+ String serJson = JsonUtils.objectMapper().writeValueAsString(response);
+ IdpUserResponse deserResponse =
+ JsonUtils.objectMapper().readValue(serJson, IdpUserResponse.class);
+
+ Assertions.assertEquals(response, deserResponse);
+ Assertions.assertEquals("test_user", deserResponse.getUser().name());
+ Assertions.assertEquals(Arrays.asList("group1", "group2"),
deserResponse.getUser().groups());
+ }
+
+ @Test
+ public void testIdpUserResponseValidate() {
+ IdpUserDTO user =
+ IdpUserDTO.builder()
+ .withName("test_user")
+ .withGroups(Arrays.asList("group1", "group2"))
+ .build();
+ IdpUserResponse response = new IdpUserResponse(user);
+ response.validate(); // No exception thrown
+ }
+
+ @Test
+ public void testIdpUserResponseException() {
+ IdpUserResponse response = new IdpUserResponse();
+ Assertions.assertThrows(IllegalArgumentException.class,
response::validate);
+ }
+
+ @Test
+ public void testIdpUserResponseBlankNameMessage() throws
JsonProcessingException {
+ IdpUserResponse response =
+ JsonUtils.objectMapper()
+ .readValue(
+ "{\"code\":0,\"user\":{\"name\":\"
\",\"groups\":[\"group1\"]}}",
+ IdpUserResponse.class);
+
+ IllegalArgumentException exception =
+ Assertions.assertThrows(IllegalArgumentException.class,
response::validate);
+
+ Assertions.assertEquals("user 'name' must not be null or empty",
exception.getMessage());
+ }
+
+ @Test
+ public void testIdpUserResponseBlankGroupMessage() throws
JsonProcessingException {
+ IdpUserResponse response =
+ JsonUtils.objectMapper()
+ .readValue(
+ "{\"code\":0,\"user\":{\"name\":\"test_user\",\"groups\":[\"
\"]}}",
+ IdpUserResponse.class);
+
+ IllegalArgumentException exception =
+ Assertions.assertThrows(IllegalArgumentException.class,
response::validate);
+
+ Assertions.assertEquals(
+ "user 'groups' must not contain null or empty group names",
exception.getMessage());
+ }
+}