Skip to content

Commit 56c4e16

Browse files
authored
Revert "Fix checkRoleEscalation performance and bugs in access checking (#12973)"
This reverts commit 5dcb8ab.
1 parent 5dcb8ab commit 56c4e16

10 files changed

Lines changed: 28 additions & 393 deletions

File tree

api/src/main/java/org/apache/cloudstack/acl/APIChecker.java

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,16 @@
1717
package org.apache.cloudstack.acl;
1818

1919
import com.cloud.exception.PermissionDeniedException;
20-
import com.cloud.exception.RequestLimitException;
2120
import com.cloud.user.Account;
2221
import com.cloud.user.User;
2322
import com.cloud.utils.component.Adapter;
2423

25-
import java.util.ArrayList;
2624
import java.util.List;
2725

28-
import org.apache.logging.log4j.LogManager;
29-
import org.apache.logging.log4j.Logger;
30-
3126
/**
3227
* APICheckers is designed to verify the ownership of resources and to control the access to APIs.
3328
*/
3429
public interface APIChecker extends Adapter {
35-
Logger LOGGER = LogManager.getLogger(APIChecker.class);
3630
// Interface for checking access for a role using apiname
3731
// If true, apiChecker has checked the operation
3832
// If false, apiChecker is unable to handle the operation or not implemented
@@ -48,27 +42,5 @@ public interface APIChecker extends Adapter {
4842
* @return the list of allowed apis for the given user
4943
*/
5044
List<String> getApisAllowedToUser(Role role, User user, List<String> apiNames) throws PermissionDeniedException;
51-
52-
default List<String> getApisAllowedToAccount(Account account, List<String> apiNames) {
53-
List<String> allowedApis = new ArrayList<>();
54-
for (String apiName : apiNames) {
55-
try {
56-
checkAccess(account, apiName);
57-
allowedApis.add(apiName);
58-
} catch (RequestLimitException e) {
59-
// Non-ACL failure (e.g. rate limiting) should not be treated as simple "not allowed".
60-
// Propagate as unchecked so callers are aware of the failure.
61-
throw new RuntimeException("Failed to check access for API [" + apiName + "] due to request limits", e);
62-
} catch (PermissionDeniedException e) {
63-
LOGGER.trace("Account [" + account + "] is not allowed to access API [" + apiName + "]");
64-
}
65-
}
66-
return allowedApis;
67-
}
68-
6945
boolean isEnabled();
70-
71-
default void refreshRoleCacheOnPermissionsChange(Role role) {
72-
// Only applicable for dynamic role based checkers
73-
}
7446
}

plugins/acl/dynamic-role-based/src/main/java/org/apache/cloudstack/acl/DynamicRoleBasedAPIAccessChecker.java

Lines changed: 1 addition & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,6 @@ protected DynamicRoleBasedAPIAccessChecker() {
6060
}
6161
}
6262

63-
@Override
64-
public void refreshRoleCacheOnPermissionsChange(Role role) {
65-
invalidateRolePermissionsCache(role.getId());
66-
}
67-
6863
@Override
6964
public List<String> getApisAllowedToUser(Role role, User user, List<String> apiNames) throws PermissionDeniedException {
7065
if (!isEnabled()) {
@@ -81,29 +76,6 @@ public List<String> getApisAllowedToUser(Role role, User user, List<String> apiN
8176
return allowedApis;
8277
}
8378

84-
@Override
85-
public List<String> getApisAllowedToAccount(Account account, List<String> apiNames) {
86-
if (!isEnabled()) {
87-
return apiNames;
88-
}
89-
Pair<Role, List<RolePermission>> roleAndPermissions = getRolePermissionsUsingCache(account.getRoleId());
90-
final Role accountRole = roleAndPermissions.first();
91-
if (accountRole == null) {
92-
throw new PermissionDeniedException("The account [" + account + "] has role null or unknown.");
93-
}
94-
if (accountRole.getRoleType() == RoleType.Admin && accountRole.getId() == RoleType.Admin.getId()) {
95-
return apiNames;
96-
}
97-
List<RolePermission> allPermissions = roleAndPermissions.second();
98-
List<String> allowedApis = new ArrayList<>();
99-
for (String api : apiNames) {
100-
if (checkApiPermissionByRole(accountRole, api, allPermissions)) {
101-
allowedApis.add(api);
102-
}
103-
}
104-
return allowedApis;
105-
}
106-
10779
/**
10880
* Checks if the given Role of an Account has the allowed permission for the given API.
10981
*
@@ -148,12 +120,6 @@ protected Pair<Role, List<RolePermission>> getRolePermissions(long roleId) {
148120
return new Pair<>(accountRole, roleService.findAllPermissionsBy(accountRole.getId()));
149121
}
150122

151-
protected void invalidateRolePermissionsCache(long roleId) {
152-
if (cachePeriod > 0) {
153-
rolePermissionsCache.invalidate(roleId);
154-
}
155-
}
156-
157123
protected Pair<Role, List<RolePermission>> getRolePermissionsUsingCache(long roleId) {
158124
if (cachePeriod > 0) {
159125
return rolePermissionsCache.get(roleId);
@@ -207,7 +173,7 @@ public boolean checkAccess(Account account, String commandName) {
207173
return true;
208174
}
209175

210-
List<RolePermission> allPermissions = roleAndPermissions.second();
176+
List<RolePermission> allPermissions = roleService.findAllPermissionsBy(accountRole.getId());
211177
if (checkApiPermissionByRole(accountRole, commandName, allPermissions)) {
212178
return true;
213179
}

plugins/acl/dynamic-role-based/src/test/java/org/apache/cloudstack/acl/DynamicRoleBasedAPIAccessCheckerTest.java

Lines changed: 0 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@
4040
import com.cloud.user.UserVO;
4141

4242
import org.apache.cloudstack.acl.RolePermissionEntity.Permission;
43-
import org.apache.cloudstack.utils.cache.LazyCache;
44-
import com.cloud.utils.Pair;
4543

4644
import junit.framework.TestCase;
4745

@@ -197,134 +195,4 @@ public void getApisAllowedToUserTestPermissionDenyForGivenApiShouldReturnEmptyLi
197195
List<String> apisReceived = apiAccessCheckerSpy.getApisAllowedToUser(getTestRole(), getTestUser(), apiNames);
198196
Assert.assertEquals(0, apisReceived.size());
199197
}
200-
201-
// --- Tests for checkAccess(Account, String) ---
202-
203-
@Test(expected = PermissionDeniedException.class)
204-
public void testCheckAccessAccountNullRoleShouldThrow() {
205-
Mockito.when(roleServiceMock.findRole(Mockito.anyLong())).thenReturn(null);
206-
apiAccessCheckerSpy.checkAccess(getTestAccount(), "someApi");
207-
}
208-
209-
@Test
210-
public void testCheckAccessAccountAdminShouldAllow() {
211-
Account adminAccount = new AccountVO("root admin", 1L, null, Account.Type.ADMIN, "admin-uuid");
212-
Mockito.when(roleServiceMock.findRole(Mockito.anyLong())).thenReturn(new RoleVO(1L, "Admin", RoleType.Admin, "default admin role"));
213-
assertTrue(apiAccessCheckerSpy.checkAccess(adminAccount, "anyApi"));
214-
}
215-
216-
@Test
217-
public void testCheckAccessAccountAllowedApi() {
218-
final String allowedApiName = "someAllowedApi";
219-
final RolePermission permission = new RolePermissionVO(1L, allowedApiName, Permission.ALLOW, null);
220-
Mockito.when(roleServiceMock.findAllPermissionsBy(Mockito.anyLong())).thenReturn(Collections.singletonList(permission));
221-
assertTrue(apiAccessCheckerSpy.checkAccess(getTestAccount(), allowedApiName));
222-
}
223-
224-
@Test(expected = PermissionDeniedException.class)
225-
public void testCheckAccessAccountDeniedApi() {
226-
final String deniedApiName = "someDeniedApi";
227-
final RolePermission permission = new RolePermissionVO(1L, deniedApiName, Permission.DENY, null);
228-
Mockito.when(roleServiceMock.findAllPermissionsBy(Mockito.anyLong())).thenReturn(Collections.singletonList(permission));
229-
apiAccessCheckerSpy.checkAccess(getTestAccount(), deniedApiName);
230-
}
231-
232-
@Test
233-
public void testCheckAccessAccountUsesCachedPermissions() throws Exception {
234-
// Enable caching by setting a positive cachePeriod
235-
Field cachePeriodField = DynamicRoleBasedAPIAccessChecker.class.getDeclaredField("cachePeriod");
236-
cachePeriodField.setAccessible(true);
237-
cachePeriodField.set(apiAccessCheckerSpy, 1);
238-
239-
Field rpCacheField = DynamicRoleBasedAPIAccessChecker.class.getDeclaredField("rolePermissionsCache");
240-
rpCacheField.setAccessible(true);
241-
rpCacheField.set(apiAccessCheckerSpy, new LazyCache<Long, Pair<Role, List<RolePermission>>>(32, 1, apiAccessCheckerSpy::getRolePermissions));
242-
243-
final String allowedApiName = "someAllowedApi";
244-
final RolePermission permission = new RolePermissionVO(1L, allowedApiName, Permission.ALLOW, null);
245-
Mockito.when(roleServiceMock.findAllPermissionsBy(Mockito.anyLong())).thenReturn(Collections.singletonList(permission));
246-
247-
// First call should populate the cache
248-
apiAccessCheckerSpy.checkAccess(getTestAccount(), allowedApiName);
249-
// Second call should use cached permissions and not hit the DAO again
250-
apiAccessCheckerSpy.checkAccess(getTestAccount(), allowedApiName);
251-
252-
Mockito.verify(roleServiceMock, Mockito.times(1)).findAllPermissionsBy(Mockito.anyLong());
253-
}
254-
255-
// --- Tests for getApisAllowedToAccount ---
256-
257-
@Test
258-
public void testGetApisAllowedToAccountDisabledShouldReturnAll() {
259-
Mockito.doReturn(false).when(apiAccessCheckerSpy).isEnabled();
260-
List<String> input = new ArrayList<>(Arrays.asList("api1", "api2", "api3"));
261-
List<String> result = apiAccessCheckerSpy.getApisAllowedToAccount(getTestAccount(), input);
262-
Assert.assertEquals(3, result.size());
263-
}
264-
265-
@Test(expected = PermissionDeniedException.class)
266-
public void testGetApisAllowedToAccountNullRoleShouldThrow() {
267-
Mockito.when(roleServiceMock.findRole(Mockito.anyLong())).thenReturn(null);
268-
apiAccessCheckerSpy.getApisAllowedToAccount(getTestAccount(), new ArrayList<>(Arrays.asList("api1")));
269-
}
270-
271-
@Test
272-
public void testGetApisAllowedToAccountAdminShouldReturnAll() {
273-
Account adminAccount = new AccountVO("root admin", 1L, null, Account.Type.ADMIN, "admin-uuid");
274-
Mockito.when(roleServiceMock.findRole(Mockito.anyLong())).thenReturn(new RoleVO(1L, "Admin", RoleType.Admin, "default admin role"));
275-
List<String> input = new ArrayList<>(Arrays.asList("api1", "api2", "api3"));
276-
List<String> result = apiAccessCheckerSpy.getApisAllowedToAccount(adminAccount, input);
277-
Assert.assertEquals(3, result.size());
278-
Assert.assertEquals(input, result);
279-
}
280-
281-
@Test
282-
public void testGetApisAllowedToAccountFiltersCorrectly() {
283-
final RolePermission allowPermission = new RolePermissionVO(1L, "allowedApi", Permission.ALLOW, null);
284-
final RolePermission denyPermission = new RolePermissionVO(1L, "deniedApi", Permission.DENY, null);
285-
Mockito.when(roleServiceMock.findAllPermissionsBy(Mockito.anyLong())).thenReturn(Arrays.asList(allowPermission, denyPermission));
286-
List<String> input = new ArrayList<>(Arrays.asList("allowedApi", "deniedApi", "unknownApi"));
287-
List<String> result = apiAccessCheckerSpy.getApisAllowedToAccount(getTestAccount(), input);
288-
Assert.assertEquals(1, result.size());
289-
Assert.assertEquals("allowedApi", result.get(0));
290-
}
291-
292-
@Test
293-
public void testGetApisAllowedToAccountAnnotationFallback() {
294-
Mockito.when(roleServiceMock.findAllPermissionsBy(Mockito.anyLong())).thenReturn(Collections.emptyList());
295-
apiAccessCheckerSpy.addApiToRoleBasedAnnotationsMap(RoleType.User, "annotatedApi");
296-
List<String> input = new ArrayList<>(Arrays.asList("annotatedApi", "unknownApi"));
297-
List<String> result = apiAccessCheckerSpy.getApisAllowedToAccount(getTestAccount(), input);
298-
Assert.assertEquals(1, result.size());
299-
Assert.assertEquals("annotatedApi", result.get(0));
300-
}
301-
302-
@Test
303-
public void testGetApisAllowedToAccountUsesCachedPermissions() {
304-
try {
305-
// Ensure caching is enabled by setting a positive cachePeriod
306-
Field cachePeriodField = DynamicRoleBasedAPIAccessChecker.class.getDeclaredField("cachePeriod");
307-
cachePeriodField.setAccessible(true);
308-
cachePeriodField.set(apiAccessCheckerSpy, 1);
309-
310-
Field rpCacheField = DynamicRoleBasedAPIAccessChecker.class.getDeclaredField("rolePermissionsCache");
311-
rpCacheField.setAccessible(true);
312-
rpCacheField.set(apiAccessCheckerSpy, new LazyCache<Long, Pair<Role, List<RolePermission>>>(32, 1, apiAccessCheckerSpy::getRolePermissions));
313-
314-
final RolePermission permission = new RolePermissionVO(1L, "api1", Permission.ALLOW, null);
315-
Mockito.when(roleServiceMock.findAllPermissionsBy(Mockito.anyLong())).thenReturn(Collections.singletonList(permission));
316-
317-
Account account = getTestAccount();
318-
List<String> apis = new ArrayList<>(Arrays.asList("api1"));
319-
320-
// First call should load permissions from the DAO and populate the cache
321-
apiAccessCheckerSpy.getApisAllowedToAccount(account, apis);
322-
// Second call should use cached permissions and not hit the DAO again
323-
apiAccessCheckerSpy.getApisAllowedToAccount(account, apis);
324-
325-
Mockito.verify(roleServiceMock, Mockito.times(1)).findAllPermissionsBy(Mockito.anyLong());
326-
} catch (NoSuchFieldException | IllegalAccessException e) {
327-
Assert.fail("Failed to set cachePeriod for test: " + e.getMessage());
328-
}
329-
}
330198
}

plugins/acl/project-role-based/src/main/java/org/apache/cloudstack/acl/ProjectRoleBasedApiAccessChecker.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -154,11 +154,6 @@ public boolean checkAccess(Account account, String apiCommandName) throws Permis
154154
return true;
155155
}
156156

157-
@Override
158-
public List<String> getApisAllowedToAccount(Account account, List<String> apiNames) {
159-
return apiNames;
160-
}
161-
162157
public boolean isPermitted(Project project, ProjectAccount projectUser, String ... apiCommandNames) {
163158
ProjectRole projectRole = null;
164159
if(projectUser.getProjectRoleId() != null) {

plugins/acl/static-role-based/src/main/java/org/apache/cloudstack/acl/StaticRoleBasedAPIAccessChecker.java

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
// under the License.
1717
package org.apache.cloudstack.acl;
1818

19-
import java.util.ArrayList;
2019
import java.util.HashMap;
2120
import java.util.HashSet;
2221
import java.util.List;
@@ -122,21 +121,6 @@ public boolean checkAccess(Account account, String commandName) {
122121
}
123122
}
124123

125-
@Override
126-
public List<String> getApisAllowedToAccount(Account account, List<String> apiNames) {
127-
if (!isEnabled()) {
128-
return apiNames;
129-
}
130-
RoleType roleType = accountService.getRoleType(account);
131-
List<String> allowedApis = new ArrayList<>();
132-
for (String apiName : apiNames) {
133-
if (isApiAllowed(apiName, roleType)) {
134-
allowedApis.add(apiName);
135-
}
136-
}
137-
return allowedApis;
138-
}
139-
140124
/**
141125
* Verifies if the API is allowed for the given RoleType.
142126
*

plugins/network-elements/juniper-contrail/src/test/java/org/apache/cloudstack/network/contrail/management/MockAccountManager.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import javax.naming.ConfigurationException;
2626

2727
import com.cloud.api.auth.SetupUserTwoFactorAuthenticationCmd;
28-
import org.apache.cloudstack.acl.Role;
2928
import org.apache.cloudstack.api.command.admin.account.CreateAccountCmd;
3029
import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd;
3130
import org.apache.cloudstack.api.command.admin.user.MoveUserCmd;
@@ -544,8 +543,4 @@ public UserAccount clearUserTwoFactorAuthenticationInSetupStateOnLogin(UserAccou
544543
@Override
545544
public void verifyCallerPrivilegeForUserOrAccountOperations(Account userAccount) {
546545
}
547-
548-
@Override
549-
public void refreshRoleCheckersCacheOnPermissionsChange(Role role) {
550-
}
551546
}

server/src/main/java/com/cloud/user/AccountManager.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import java.util.Map;
2222

2323
import org.apache.cloudstack.acl.ControlledEntity;
24-
import org.apache.cloudstack.acl.Role;
2524
import org.apache.cloudstack.api.command.admin.account.UpdateAccountCmd;
2625
import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd;
2726
import org.apache.cloudstack.api.command.admin.user.MoveUserCmd;
@@ -208,6 +207,4 @@ void buildACLViewSearchCriteria(SearchCriteria<? extends ControlledViewEntity> s
208207
UserAccount clearUserTwoFactorAuthenticationInSetupStateOnLogin(UserAccount user);
209208

210209
void verifyCallerPrivilegeForUserOrAccountOperations(Account userAccount);
211-
212-
void refreshRoleCheckersCacheOnPermissionsChange(Role role);
213210
}

0 commit comments

Comments
 (0)