Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,18 @@ public boolean hasManageAppMasterPermission(String appId) {
throw new UnsupportedOperationException("Not supported operation");
}

@Override
public boolean hasCreateUserPermission() {
long consumerId = consumerAuthUtil.retrieveConsumerIdFromCtx();
return permissionService.consumerHasPermission(consumerId, PermissionType.CREATE_USER,
SYSTEM_PERMISSION_TARGET_ID);
}

@Override
public boolean hasCreateUserPermission(String userId) {
return false;
}

@Override
protected boolean hasPermissions(List<Permission> requiredPerms) {
if (requiredPerms == null || requiredPerms.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright 2025 Apollo Authors
*
* Licensed 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 com.ctrip.framework.apollo.openapi.dto;

/**
* Open API User DTO for user management operations.
*
* @author dreamweaver
*/
public class OpenUserDTO {

private String username;
private String userDisplayName;
private String password;
private String email;
private Integer enabled;

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getUserDisplayName() {
return userDisplayName;
}

public void setUserDisplayName(String userDisplayName) {
this.userDisplayName = userDisplayName;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public Integer getEnabled() {
return enabled;
}

public void setEnabled(Integer enabled) {
this.enabled = enabled;
}

@Override
public String toString() {
return "OpenUserDTO{" + "username='" + username + '\'' + ", userDisplayName='" + userDisplayName
+ '\'' + ", email='" + email + '\'' + ", enabled=" + enabled + '}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import static com.ctrip.framework.apollo.portal.service.SystemRoleManagerService.CREATE_APPLICATION_ROLE_NAME;

import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.portal.service.SystemRoleManagerService;
import com.ctrip.framework.apollo.common.exception.NotFoundException;
import com.ctrip.framework.apollo.openapi.entity.Consumer;
import com.ctrip.framework.apollo.openapi.entity.ConsumerAudit;
Expand Down Expand Up @@ -206,7 +207,7 @@ public List<ConsumerRole> assignNamespaceRoleToConsumer(String token, String app
}

private ConsumerInfo convert(Consumer consumer, String token, boolean allowCreateApplication,
Integer rateLimit) {
boolean allowCreateUser, Integer rateLimit) {
ConsumerInfo consumerInfo = new ConsumerInfo();
consumerInfo.setConsumerId(consumer.getId());
consumerInfo.setAppId(consumer.getAppId());
Expand All @@ -219,6 +220,7 @@ private ConsumerInfo convert(Consumer consumer, String token, boolean allowCreat

consumerInfo.setToken(token);
consumerInfo.setAllowCreateApplication(allowCreateApplication);
consumerInfo.setAllowCreateUser(allowCreateUser);
return consumerInfo;
}

Expand All @@ -232,13 +234,17 @@ public ConsumerInfo getConsumerInfoByAppId(String appId) {
return null;
}
return convert(consumer, consumerToken.getToken(), isAllowCreateApplication(consumer.getId()),
getRateLimit(consumer.getId()));
isAllowCreateUser(consumer.getId()), getRateLimit(consumer.getId()));
}

private boolean isAllowCreateApplication(Long consumerId) {
return isAllowCreateApplication(Collections.singletonList(consumerId)).get(0);
}

private boolean isAllowCreateUser(Long consumerId) {
return isAllowCreateUser(Collections.singletonList(consumerId)).get(0);
}

private Integer getRateLimit(Long consumerId) {
List<Integer> list = getRateLimit(Collections.singletonList(consumerId));
if (CollectionUtils.isEmpty(list)) {
Expand Down Expand Up @@ -268,6 +274,27 @@ private List<Boolean> isAllowCreateApplication(List<Long> consumerIdList) {
return list;
}

private List<Boolean> isAllowCreateUser(List<Long> consumerIdList) {
Role createUserRole = getCreateUserRole();
if (createUserRole == null) {
List<Boolean> list = new ArrayList<>(consumerIdList.size());
for (Long ignored : consumerIdList) {
list.add(false);
}
return list;
}

long roleId = createUserRole.getId();
List<Boolean> list = new ArrayList<>(consumerIdList.size());
for (Long consumerId : consumerIdList) {
ConsumerRole createUserConsumerRole =
consumerRoleRepository.findByConsumerIdAndRoleId(consumerId, roleId);
list.add(createUserConsumerRole != null);
}

return list;
}

private List<Integer> getRateLimit(List<Long> consumerIds) {
List<ConsumerToken> consumerTokens = consumerTokenRepository.findByConsumerIdIn(consumerIds);
Map<Long, Integer> consumerRateLimits = consumerTokens.stream().collect(Collectors.toMap(
Expand All @@ -282,6 +309,10 @@ private Role getCreateAppRole() {
return rolePermissionService.findRoleByRoleName(CREATE_APPLICATION_ROLE_NAME);
}

private Role getCreateUserRole() {
return rolePermissionService.findRoleByRoleName(SystemRoleManagerService.CREATE_USER_ROLE_NAME);
}

public ConsumerRole assignCreateApplicationRoleToConsumer(String token) {
Long consumerId = getConsumerIdByToken(token);
if (consumerId == null) {
Expand All @@ -304,6 +335,28 @@ public ConsumerRole assignCreateApplicationRoleToConsumer(String token) {
return consumerRoleRepository.save(consumerRole);
}

public ConsumerRole assignCreateUserRoleToConsumer(String token) {
Long consumerId = getConsumerIdByToken(token);
if (consumerId == null) {
throw new BadRequestException("Token is Illegal");
}
Role createUserRole = getCreateUserRole();
if (createUserRole == null) {
throw NotFoundException.roleNotFound(SystemRoleManagerService.CREATE_USER_ROLE_NAME);
}

long roleId = createUserRole.getId();
ConsumerRole createUserConsumerRole =
consumerRoleRepository.findByConsumerIdAndRoleId(consumerId, roleId);
if (createUserConsumerRole != null) {
return createUserConsumerRole;
}

String operator = userInfoHolder.getUser().getUserId();
ConsumerRole consumerRole = createConsumerRole(consumerId, roleId, operator);
return consumerRoleRepository.save(consumerRole);
}


@Transactional
public ConsumerRole assignAppRoleToConsumer(String token, String appId) {
Expand Down Expand Up @@ -436,15 +489,16 @@ public List<ConsumerInfo> findConsumerInfoList(Pageable page) {
List<Long> consumerIdList =
consumerList.stream().map(Consumer::getId).collect(Collectors.toList());
List<Boolean> allowCreateApplicationList = isAllowCreateApplication(consumerIdList);
List<Boolean> allowCreateUserList = isAllowCreateUser(consumerIdList);
List<Integer> rateLimitList = getRateLimit(consumerIdList);

List<ConsumerInfo> consumerInfoList = new ArrayList<>(consumerList.size());

for (int i = 0; i < consumerList.size(); i++) {
Consumer consumer = consumerList.get(i);
// without token
ConsumerInfo consumerInfo =
convert(consumer, null, allowCreateApplicationList.get(i), rateLimitList.get(i));
ConsumerInfo consumerInfo = convert(consumer, null, allowCreateApplicationList.get(i),
allowCreateUserList.get(i), rateLimitList.get(i));
consumerInfoList.add(consumerInfo);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/*
Comment thread
coderabbitai[bot] marked this conversation as resolved.
* Copyright 2025 Apollo Authors
*
* Licensed 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 com.ctrip.framework.apollo.openapi.v1.controller;

import com.ctrip.framework.apollo.audit.annotation.ApolloAuditLog;
import com.ctrip.framework.apollo.audit.annotation.OpType;
import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.core.utils.StringUtils;
import com.ctrip.framework.apollo.openapi.dto.OpenUserDTO;
import com.ctrip.framework.apollo.portal.entity.bo.UserInfo;
import com.ctrip.framework.apollo.portal.entity.po.UserPO;
import com.ctrip.framework.apollo.portal.spi.UserService;
import com.ctrip.framework.apollo.portal.spi.springsecurity.SpringSecurityUserService;
import com.ctrip.framework.apollo.portal.util.checker.AuthUserPasswordChecker;
import com.ctrip.framework.apollo.portal.util.checker.CheckResult;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import java.net.URI;
import java.util.List;

/**
* OpenAPI User Management Controller Provides RESTful APIs for user management operations through
* OpenAPI
*
* @author dreamweaver
*/
@RestController("openapiUserController")
@RequestMapping("/openapi/v1")
public class UserController {

private static final int DEFAULT_USER_ENABLED = 1;

private final UserService userService;
private final AuthUserPasswordChecker passwordChecker;

public UserController(final UserService userService,
final AuthUserPasswordChecker passwordChecker) {
this.userService = userService;
this.passwordChecker = passwordChecker;
}

/**
* Create a new user
*
* @param openUserDTO user information to create
* @return ResponseEntity with created user information
*/
@PreAuthorize(value = "@unifiedPermissionValidator.hasCreateUserPermission()")
@ApolloAuditLog(name = "OpenAPI.createUser", type = OpType.CREATE,
description = "Create user via OpenAPI")
@PostMapping("/users")
public ResponseEntity<UserInfo> createUser(@RequestBody OpenUserDTO openUserDTO) {
// Validate required fields
if (StringUtils.isContainEmpty(openUserDTO.getUsername(), openUserDTO.getPassword())) {
throw new BadRequestException("Username and password cannot be empty.");
}

if (StringUtils.isEmpty(openUserDTO.getEmail())) {
throw new BadRequestException("Email cannot be empty.");
}

// Check password strength
CheckResult pwdCheckRes = passwordChecker.checkWeakPassword(openUserDTO.getPassword());
if (!pwdCheckRes.isSuccess()) {
throw new BadRequestException(pwdCheckRes.getMessage());
}

// Check if UserService supports user creation
if (!(userService instanceof SpringSecurityUserService)) {
throw new UnsupportedOperationException(
"Create user operation is not supported with current user service implementation");
}

// Convert DTO to PO and set defaults
UserPO userPO = new UserPO();
userPO.setUsername(openUserDTO.getUsername());
userPO.setPassword(openUserDTO.getPassword());
userPO.setEmail(openUserDTO.getEmail());
userPO.setUserDisplayName(
openUserDTO.getUserDisplayName() != null ? openUserDTO.getUserDisplayName()
: openUserDTO.getUsername());
userPO.setEnabled(
openUserDTO.getEnabled() != null ? openUserDTO.getEnabled() : DEFAULT_USER_ENABLED);

// Create user
((SpringSecurityUserService) userService).create(userPO);
Comment on lines +107 to +128
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find and examine UserService interface definition
echo "=== Searching for UserService interface ==="
rg -n 'interface UserService' --type=java -A 20

echo -e "\n=== Classes implementing UserService ==="
rg -n 'implements UserService' --type=java

echo -e "\n=== Find UserService file location ==="
fd -n 'UserService.java' --type f

Repository: apolloconfig/apollo

Length of output: 1843


🏁 Script executed:

#!/bin/bash
# Get full UserService interface definition
echo "=== Full UserService interface ==="
cat -n apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/spi/UserService.java

echo -e "\n=== SpringSecurityUserService create method ==="
rg -n 'create\(' apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/spi/springsecurity/SpringSecurityUserService.java -A 5

echo -e "\n=== Check for exception handlers in UserController ==="
rg -n '@ExceptionHandler|@ControllerAdvice' apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/UserController.java

echo -e "\n=== Check UserController class definition for exception handling ==="
rg -n 'class UserController' apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/UserController.java -A 5

Repository: apolloconfig/apollo

Length of output: 2035


🏁 Script executed:

#!/bin/bash
# Check for global exception handling
echo "=== Search for exception handlers (RestControllerAdvice/ControllerAdvice) ==="
rg -n '@RestControllerAdvice|@ControllerAdvice' apollo-portal/src/main/java --type=java

echo -e "\n=== Check how UnsupportedOperationException is handled ==="
rg -n 'UnsupportedOperationException' apollo-portal/src/main/java --type=java

echo -e "\n=== Check searchUsers method in SpringSecurityUserService ==="
rg -n 'searchUsers' apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/spi/springsecurity/SpringSecurityUserService.java -A 15

echo -e "\n=== Check UserController searchUsers method (around lines 141-157) ==="
sed -n '135,160p' apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/UserController.java | cat -n

Repository: apolloconfig/apollo

Length of output: 4152


Tight coupling to SpringSecurityUserService via instanceof check and cast.

The controller depends on the UserService interface but then checks for and casts to the concrete SpringSecurityUserService to call create(). This defeats the purpose of the abstraction. If create is needed in the OpenAPI, it should be declared on the UserService SPI interface (or a dedicated sub-interface), so other implementations (e.g., LDAP-backed) can also support user creation.

Additionally, UnsupportedOperationException (Line 91) is not mapped by Spring's exception handling and will result in an HTTP 500 response. Consider throwing a BadRequestException or a custom exception that maps to a more appropriate status code (e.g., 501 Not Implemented).

Proposed approach

Add a create(UserPO user) method to the UserService interface (with a default throwing UnsupportedOperationException), then remove the instanceof check:

-    // Check if UserService supports user creation
-    if (!(userService instanceof SpringSecurityUserService)) {
-      throw new UnsupportedOperationException(
-          "Create user operation is not supported with current user service implementation");
-    }
-
     // Convert DTO to PO and set defaults
     ...
 
     // Create user
-    ((SpringSecurityUserService) userService).create(userPO);
+    userService.create(userPO);

In UserService interface:

default void create(UserPO user) {
    throw new UnsupportedOperationException(
        "Create user operation is not supported with current user service implementation");
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/UserController.java`
around lines 89 - 107, The controller currently checks and casts userService to
SpringSecurityUserService to call create(), tightly coupling UserController to
an implementation; instead add a create(UserPO) method to the UserService SPI
(e.g., default void create(UserPO user) { throw new
UnsupportedOperationException(...) }) so UserController can call
userService.create(userPO) without instanceof/cast, keep the DTO->PO mapping
(openUserDTO -> UserPO, DEFAULT_USER_ENABLED) and remove the
SpringSecurityUserService reference; also replace throwing raw
UnsupportedOperationException with an exception mapped to a proper HTTP status
(e.g., throw a BadRequestException or a custom exception that maps to 501 Not
Implemented) so callers get appropriate HTTP responses.


// Retrieve and return the created user information
UserInfo createdUser = userService.findByUserId(openUserDTO.getUsername());
URI location = ServletUriComponentsBuilder.fromCurrentRequest()
.replacePath("/openapi/v1/users/{userId}").buildAndExpand(createdUser.getUserId()).toUri();
return ResponseEntity.created(location).body(createdUser);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

/**
* Get user information by user ID
*
* @param userId the user ID to query
* @return UserInfo object
*/
@PreAuthorize(value = "@unifiedPermissionValidator.hasCreateUserPermission()")
@GetMapping("/users/{userId}")
Comment on lines +142 to +143
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for permission validator and user-related permissions
echo "=== UnifiedPermissionValidator class with permission methods ==="
rg -n 'class UnifiedPermissionValidator' --type=java -A 30

echo -e "\n=== All hasXxxUserPermission methods ==="
rg -n 'has.*UserPermission|hasPermission' --type=java | grep -i user

echo -e "\n=== UserController.java content around lines 126-127 and 145-146 ==="
fd 'UserController.java' --type=f -x cat -n {}

Repository: apolloconfig/apollo

Length of output: 22105


🏁 Script executed:

#!/bin/bash
# More targeted search for permission methods
echo "=== hasCreateUserPermission definition ==="
rg -n 'hasCreateUserPermission' --type=java -B 2 -A 8

echo -e "\n=== hasViewUserPermission or similar ==="
rg -n 'hasViewUser|hasReadUser|hasListUser|hasGetUser' --type=java

Repository: apolloconfig/apollo

Length of output: 19078


🏁 Script executed:

#!/bin/bash
# Check if there are other permission types defined (READ_USER, VIEW_USER, etc.)
echo "=== All PermissionType constants ==="
rg -n 'PermissionType\.' --type=java | grep -i user | head -20

echo -e "\n=== Check if other controllers have read/write permission separation ==="
rg -n '@GetMapping|@PostMapping' --type=java -B 2 | grep -A 2 'PreAuthorize' | head -40

Repository: apolloconfig/apollo

Length of output: 6148


GET endpoints getUserByUserId and searchUsers incorrectly gated behind hasCreateUserPermission().

Both read-only endpoints at lines 124 and 143 require hasCreateUserPermission(), which is semantically incorrect. This permission is for user creation (write/admin scope), not for user lookups (read). No dedicated read-user permission exists in the codebase. Either introduce a separate hasViewUserPermission() (or equivalent read permission) for these GET endpoints, or reconsider the authorization requirement if these lookups should be accessible to a broader set of users.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/UserController.java`
around lines 126 - 127, The two GET handlers in UserController (getUserByUserId
and searchUsers) are incorrectly protected by
`@PreAuthorize`("@unifiedPermissionValidator.hasCreateUserPermission()"); update
their authorization to a read-specific check by replacing
hasCreateUserPermission() with a new
unifiedPermissionValidator.hasViewUserPermission() (or equivalent read
permission) and wire that new method into unifiedPermissionValidator, or if the
endpoints should be public to broader read access, remove the `@PreAuthorize`
annotation from those methods; ensure changes target the `@PreAuthorize`
annotations on getUserByUserId and searchUsers and add backend validation logic
for hasViewUserPermission in unifiedPermissionValidator.

public ResponseEntity<UserInfo> getUserByUserId(@PathVariable String userId) {
UserInfo userInfo = userService.findByUserId(userId);
if (userInfo == null) {
throw new BadRequestException("User not found: " + userId);
}
return ResponseEntity.ok(userInfo);
}
Comment on lines +145 to +152
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

User not found should return HTTP 404, not 400.

BadRequestException maps to HTTP 400, but "user not found" is semantically a 404 Not Found. Using 400 here is misleading to API consumers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/UserController.java`
around lines 122 - 128, The getUserByUserId method currently throws
BadRequestException when userService.findByUserId(userId) returns null, which
yields HTTP 400; change this to return a 404 by either throwing a
NotFoundException or returning
ResponseEntity.status(HttpStatus.NOT_FOUND).build(); specifically update
getUserByUserId in UserController to replace the BadRequestException throw with
a NotFoundException("User not found: " + userId) or
ResponseEntity.notFound().build(), keeping the rest of the method (UserInfo
userInfo = userService.findByUserId(userId) and the successful
ResponseEntity.ok(userInfo)) unchanged.


/**
* Search users by keyword
*
* @param keyword keyword to search (searches in username and display name)
* @param includeInactiveUsers whether to include inactive users
* @param offset pagination offset
* @param limit pagination limit
* @return list of UserInfo objects
*/
@PreAuthorize(value = "@unifiedPermissionValidator.hasCreateUserPermission()")
@GetMapping("/users")
public ResponseEntity<List<UserInfo>> searchUsers(
@RequestParam(value = "keyword", required = false, defaultValue = "") String keyword,
@RequestParam(value = "includeInactiveUsers",
defaultValue = "false") boolean includeInactiveUsers,
@RequestParam(value = "offset", defaultValue = "0") int offset,
@RequestParam(value = "limit", defaultValue = "10") int limit) {

if (limit <= 0 || limit > 100) {
throw new BadRequestException("Limit must be between 1 and 100");
}

if (offset < 0) {
throw new BadRequestException("Offset must be non-negative");
}

List<UserInfo> users = userService.searchUsers(keyword, offset, limit, includeInactiveUsers);
return ResponseEntity.ok(users);
Comment on lines +166 to +185
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find SpringSecurityUserService class
find . -name "*SpringSecurityUserService*" -type f

Repository: apolloconfig/apollo

Length of output: 176


🏁 Script executed:

#!/bin/bash
# Read the SpringSecurityUserService file
cat -n ./apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/spi/springsecurity/SpringSecurityUserService.java

Repository: apolloconfig/apollo

Length of output: 6717


Pagination parameters offset and limit are accepted but never applied.

The searchUsers() method in SpringSecurityUserService accepts offset and limit parameters, but ignores them entirely. The method retrieves all matching users via findUsers() and returns the complete list without any slicing or pagination logic. This means the controller's validation of these parameters is meaningless — callers requesting paginated results will receive the entire dataset instead of the requested subset.

Implement actual pagination in SpringSecurityUserService.searchUsers() using Spring Data's Pageable, or apply subList slicing in the controller as an interim solution.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/UserController.java`
around lines 141 - 157, The controller validates offset/limit but
SpringSecurityUserService.searchUsers(...) ignores them and returns all matches;
update searchUsers(String keyword, int offset, int limit, boolean
includeInactiveUsers) to apply pagination: prefer creating a Pageable
(PageRequest.of(offset/limit semantics: page = offset/limit, size = limit) or
convert offset to page/time-based query) and call a paged repository method
(e.g., userRepository.findUsers(keyword, includeInactiveUsers, pageable)) so
only the requested slice is returned; if switching the repo to Pageable is not
feasible immediately, implement slicing inside
SpringSecurityUserService.searchUsers by computing fromIndex = Math.min(offset,
list.size()) and toIndex = Math.min(offset + limit, list.size()) and return
list.subList(fromIndex, toIndex) to honor controller parameters.

}
}
Loading
Loading