mirror of
https://github.com/apache/cloudstack
synced 2026-09-02 17:40:22 +00:00
API key pair restructure follow-ups (#13828)
This commit is contained in:
parent
d45d48180a
commit
4bfeb96c96
@ -185,4 +185,6 @@ public interface AccountService {
|
||||
String getAccessingApiKey(BaseCmd cmd);
|
||||
|
||||
List<RolePermissionEntity> getAllKeypairPermissions(String apiKey);
|
||||
|
||||
List<? extends ApiKeyPairPermission> getAllExplicitKeyPairPermissions(Long keyPairId);
|
||||
}
|
||||
|
||||
@ -20,6 +20,7 @@ import com.cloud.exception.PermissionDeniedException;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.utils.component.Adapter;
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPair;
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPairPermission;
|
||||
|
||||
import java.util.List;
|
||||
@ -32,8 +33,8 @@ public interface APIChecker extends Adapter {
|
||||
// If true, apiChecker has checked the operation
|
||||
// If false, apiChecker is unable to handle the operation or not implemented
|
||||
// On exception, checkAccess failed don't allow
|
||||
boolean checkAccess(User user, String apiCommandName, ApiKeyPairPermission... apiKeyPairPermissions) throws PermissionDeniedException;
|
||||
boolean checkAccess(Account account, String apiCommandName, ApiKeyPairPermission... apiKeyPairPermissions) throws PermissionDeniedException;
|
||||
boolean checkAccess(User user, String apiCommandName, ApiKeyPair keyPair, ApiKeyPairPermission... apiKeyPairPermissions) throws PermissionDeniedException;
|
||||
boolean checkAccess(Account account, String apiCommandName, ApiKeyPair keyPair, ApiKeyPairPermission... apiKeyPairPermissions) throws PermissionDeniedException;
|
||||
/**
|
||||
* Verifies if the account has permission for the given list of APIs and returns only the allowed ones.
|
||||
*
|
||||
|
||||
@ -28,6 +28,7 @@ import java.util.stream.Collectors;
|
||||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPair;
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPairPermission;
|
||||
import org.apache.cloudstack.acl.RolePermissionEntity.Permission;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
@ -141,7 +142,7 @@ public class DynamicRoleBasedAPIAccessChecker extends AdapterBase implements API
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkAccess(User user, String commandName, ApiKeyPairPermission ... apiKeyPairPermissions) throws PermissionDeniedException {
|
||||
public boolean checkAccess(User user, String commandName, ApiKeyPair keyPair, ApiKeyPairPermission... apiKeyPairPermissions) throws PermissionDeniedException {
|
||||
if (!isEnabled()) {
|
||||
return true;
|
||||
}
|
||||
@ -150,11 +151,11 @@ public class DynamicRoleBasedAPIAccessChecker extends AdapterBase implements API
|
||||
throw new PermissionDeniedException(String.format("Account for user with ID [%s] cannot be found", user.getUuid()));
|
||||
}
|
||||
|
||||
return checkAccess(account, commandName, apiKeyPairPermissions);
|
||||
return checkAccess(account, commandName, keyPair, apiKeyPairPermissions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkAccess(Account account, String commandName, ApiKeyPairPermission ... apiKeyPairPermissions) {
|
||||
public boolean checkAccess(Account account, String commandName, ApiKeyPair keyPair, ApiKeyPairPermission ... apiKeyPairPermissions) {
|
||||
Pair<Role, List<RolePermission>> roleAndPermissions = getRolePermissionsUsingCache(account.getRoleId());
|
||||
final Role accountRole = roleAndPermissions.first();
|
||||
if (accountRole == null) {
|
||||
@ -166,7 +167,8 @@ public class DynamicRoleBasedAPIAccessChecker extends AdapterBase implements API
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean considerKeyPairPermissions = apiKeyPairPermissions.length > 0;
|
||||
boolean keyPairHasExplicitPermissions = keyPair != null && !accountService.getAllExplicitKeyPairPermissions(keyPair.getId()).isEmpty();
|
||||
boolean considerKeyPairPermissions = apiKeyPairPermissions.length > 0 || keyPairHasExplicitPermissions;
|
||||
List<RolePermissionEntity> allRules = considerKeyPairPermissions ? Arrays.asList(apiKeyPairPermissions) : new ArrayList<>(roleAndPermissions.second());
|
||||
if (checkApiPermissionByRole(accountRole, commandName, allRules, considerKeyPairPermissions)) {
|
||||
return true;
|
||||
|
||||
@ -85,14 +85,14 @@ public class DynamicRoleBasedAPIAccessCheckerTest extends TestCase {
|
||||
|
||||
// Enabled plugin
|
||||
Mockito.doReturn(true).when(apiAccessCheckerSpy).isEnabled();
|
||||
Mockito.doCallRealMethod().when(apiAccessCheckerSpy).checkAccess(Mockito.any(User.class), Mockito.anyString());
|
||||
Mockito.doCallRealMethod().when(apiAccessCheckerSpy).checkAccess(Mockito.any(User.class), Mockito.anyString(), Mockito.any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidAccountCheckAccess() {
|
||||
Mockito.when(accountService.getAccount(Mockito.anyLong())).thenReturn(null);
|
||||
try {
|
||||
apiAccessCheckerSpy.checkAccess(getTestUser(), "someApi");
|
||||
apiAccessCheckerSpy.checkAccess(getTestUser(), "someApi", null);
|
||||
fail("Exception was expected");
|
||||
} catch (PermissionDeniedException ignored) {
|
||||
}
|
||||
@ -102,7 +102,7 @@ public class DynamicRoleBasedAPIAccessCheckerTest extends TestCase {
|
||||
public void testInvalidAccountRoleCheckAccess() {
|
||||
Mockito.when(roleServiceMock.findRole(Mockito.anyLong())).thenReturn(null);
|
||||
try {
|
||||
apiAccessCheckerSpy.checkAccess(getTestUser(), "someApi");
|
||||
apiAccessCheckerSpy.checkAccess(getTestUser(), "someApi", null);
|
||||
fail("Exception was expected");
|
||||
} catch (PermissionDeniedException ignored) {
|
||||
}
|
||||
@ -112,14 +112,14 @@ public class DynamicRoleBasedAPIAccessCheckerTest extends TestCase {
|
||||
public void testDefaultRootAdminAccess() {
|
||||
Mockito.when(accountService.getAccount(Mockito.anyLong())).thenReturn(new AccountVO("root admin", 1L, null, Account.Type.ADMIN, "some-uuid"));
|
||||
Mockito.when(roleServiceMock.findRole(Mockito.anyLong())).thenReturn(new RoleVO(1L, "SomeRole", RoleType.Admin, "default root admin role"));
|
||||
assertTrue(apiAccessCheckerSpy.checkAccess(getTestUser(), "anyApi"));
|
||||
assertTrue(apiAccessCheckerSpy.checkAccess(getTestUser(), "anyApi", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidRolePermissionsCheckAccess() {
|
||||
Mockito.when(roleServiceMock.findAllPermissionsBy(Mockito.anyLong())).thenReturn(Collections.<RolePermission>emptyList());
|
||||
try {
|
||||
apiAccessCheckerSpy.checkAccess(getTestUser(), "someApi");
|
||||
apiAccessCheckerSpy.checkAccess(getTestUser(), "someApi", null);
|
||||
fail("Exception was expected");
|
||||
} catch (PermissionDeniedException ignored) {
|
||||
}
|
||||
@ -130,7 +130,7 @@ public class DynamicRoleBasedAPIAccessCheckerTest extends TestCase {
|
||||
final String allowedApiName = "someAllowedApi";
|
||||
final RolePermission permission = new RolePermissionVO(1L, allowedApiName, Permission.ALLOW, null);
|
||||
Mockito.when(roleServiceMock.findAllPermissionsBy(Mockito.anyLong())).thenReturn(Collections.singletonList(permission));
|
||||
assertTrue(apiAccessCheckerSpy.checkAccess(getTestUser(), allowedApiName));
|
||||
assertTrue(apiAccessCheckerSpy.checkAccess(getTestUser(), allowedApiName, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -138,7 +138,7 @@ public class DynamicRoleBasedAPIAccessCheckerTest extends TestCase {
|
||||
final String allowedApiName = "someAllowedApi";
|
||||
final RolePermission permission = new RolePermissionVO(1L, "some*", Permission.ALLOW, null);
|
||||
Mockito.when(roleServiceMock.findAllPermissionsBy(Mockito.anyLong())).thenReturn(Collections.singletonList(permission));
|
||||
assertTrue(apiAccessCheckerSpy.checkAccess(getTestUser(), allowedApiName));
|
||||
assertTrue(apiAccessCheckerSpy.checkAccess(getTestUser(), allowedApiName, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -147,7 +147,7 @@ public class DynamicRoleBasedAPIAccessCheckerTest extends TestCase {
|
||||
final RolePermission permission = new RolePermissionVO(1L, denyApiName, Permission.DENY, null);
|
||||
Mockito.when(roleServiceMock.findAllPermissionsBy(Mockito.anyLong())).thenReturn(Collections.singletonList(permission));
|
||||
try {
|
||||
apiAccessCheckerSpy.checkAccess(getTestUser(), denyApiName);
|
||||
apiAccessCheckerSpy.checkAccess(getTestUser(), denyApiName, null);
|
||||
fail("Exception was expected");
|
||||
} catch (PermissionDeniedException ignored) {
|
||||
}
|
||||
@ -159,7 +159,7 @@ public class DynamicRoleBasedAPIAccessCheckerTest extends TestCase {
|
||||
final RolePermission permission = new RolePermissionVO(1L, "*Deny*", Permission.DENY, null);
|
||||
Mockito.when(roleServiceMock.findAllPermissionsBy(Mockito.anyLong())).thenReturn(Collections.singletonList(permission));
|
||||
try {
|
||||
apiAccessCheckerSpy.checkAccess(getTestUser(), denyApiName);
|
||||
apiAccessCheckerSpy.checkAccess(getTestUser(), denyApiName, null);
|
||||
fail("Exception was expected");
|
||||
} catch (PermissionDeniedException ignored) {
|
||||
}
|
||||
@ -169,7 +169,7 @@ public class DynamicRoleBasedAPIAccessCheckerTest extends TestCase {
|
||||
public void testAnnotationFallbackCheckAccess() {
|
||||
final String allowedApiName = "someApiWithAnnotations";
|
||||
apiAccessCheckerSpy.addApiToRoleBasedAnnotationsMap(getTestRole().getRoleType(), allowedApiName);
|
||||
assertTrue(apiAccessCheckerSpy.checkAccess(getTestUser(), allowedApiName));
|
||||
assertTrue(apiAccessCheckerSpy.checkAccess(getTestUser(), allowedApiName, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -202,21 +202,21 @@ public class DynamicRoleBasedAPIAccessCheckerTest extends TestCase {
|
||||
public void checkAccessTestInvalidApiKeyPairPermission() {
|
||||
final String api = "someDeniedApi";
|
||||
final ApiKeyPairPermission permission = new ApiKeyPairPermissionVO(1L, api, Permission.DENY, null);
|
||||
assertFalse(apiAccessCheckerSpy.checkAccess(getTestUser(), api, permission));
|
||||
assertFalse(apiAccessCheckerSpy.checkAccess(getTestUser(), api, null, permission));
|
||||
}
|
||||
|
||||
@Test(expected = UnavailableCommandException.class)
|
||||
public void checkAccessTestUnrelatedApiKeyPairPermission() {
|
||||
final String api = "someDeniedApi";
|
||||
final ApiKeyPairPermission permission = new ApiKeyPairPermissionVO(1L, "apiName", Permission.ALLOW, null);
|
||||
assertFalse(apiAccessCheckerSpy.checkAccess(getTestUser(), api, permission));
|
||||
assertFalse(apiAccessCheckerSpy.checkAccess(getTestUser(), api, null, permission));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkAccessTestValidApiKeyPairPermission() {
|
||||
final String api = "someAllowedApi";
|
||||
final ApiKeyPairPermission permission = new ApiKeyPairPermissionVO(1L, api, Permission.ALLOW, null);
|
||||
assertTrue(apiAccessCheckerSpy.checkAccess(getTestUser(), api, permission));
|
||||
assertTrue(apiAccessCheckerSpy.checkAccess(getTestUser(), api, null, permission));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -226,7 +226,7 @@ public class DynamicRoleBasedAPIAccessCheckerTest extends TestCase {
|
||||
new ApiKeyPairPermissionVO(1L, "someDeniedApi", Permission.DENY, null),
|
||||
new ApiKeyPairPermissionVO(1L, api, Permission.ALLOW, null)
|
||||
};
|
||||
assertTrue(apiAccessCheckerSpy.checkAccess(getTestUser(), api, permissions));
|
||||
assertTrue(apiAccessCheckerSpy.checkAccess(getTestUser(), api, null, permissions));
|
||||
}
|
||||
|
||||
@Test(expected = UnavailableCommandException.class)
|
||||
@ -236,7 +236,7 @@ public class DynamicRoleBasedAPIAccessCheckerTest extends TestCase {
|
||||
new ApiKeyPairPermissionVO(1L, "someAllowedApi", Permission.ALLOW, null),
|
||||
new ApiKeyPairPermissionVO(1L, api, Permission.DENY, null)
|
||||
};
|
||||
assertFalse(apiAccessCheckerSpy.checkAccess(getTestUser(), api, permissions));
|
||||
assertFalse(apiAccessCheckerSpy.checkAccess(getTestUser(), api, null, permissions));
|
||||
}
|
||||
|
||||
|
||||
@ -247,7 +247,7 @@ public class DynamicRoleBasedAPIAccessCheckerTest extends TestCase {
|
||||
final RolePermission permission = new RolePermissionVO(1L, api, Permission.ALLOW, null);
|
||||
Mockito.doReturn(Collections.singletonList(permission)).when(roleServiceMock).findAllPermissionsBy(Mockito.anyLong());
|
||||
|
||||
assertTrue(apiAccessCheckerSpy.checkAccess(getTestUser(), api, emptyPermissionArray));
|
||||
assertTrue(apiAccessCheckerSpy.checkAccess(getTestUser(), api, null, emptyPermissionArray));
|
||||
Mockito.verify(roleServiceMock).findAllPermissionsBy(Mockito.anyLong());
|
||||
}
|
||||
|
||||
@ -258,7 +258,7 @@ public class DynamicRoleBasedAPIAccessCheckerTest extends TestCase {
|
||||
final RolePermission permission = new RolePermissionVO(1L, api, Permission.DENY, null);
|
||||
Mockito.doReturn(Collections.singletonList(permission)).when(roleServiceMock).findAllPermissionsBy(Mockito.anyLong());
|
||||
|
||||
assertTrue(apiAccessCheckerSpy.checkAccess(getTestUser(), api, emptyPermissionArray));
|
||||
assertTrue(apiAccessCheckerSpy.checkAccess(getTestUser(), api, null, emptyPermissionArray));
|
||||
Mockito.verify(roleServiceMock, Mockito.times(1)).findAllPermissionsBy(Mockito.anyLong());
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,6 +23,7 @@ import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
import org.apache.cloudstack.acl.RolePermissionEntity.Permission;
|
||||
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPair;
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPairPermission;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
|
||||
@ -106,7 +107,7 @@ public class ProjectRoleBasedApiAccessChecker extends AdapterBase implements AP
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkAccess(User user, String apiCommandName, ApiKeyPairPermission... apiKeyPairPermissions) throws PermissionDeniedException {
|
||||
public boolean checkAccess(User user, String apiCommandName, ApiKeyPair keyPair, ApiKeyPairPermission... apiKeyPairPermissions) throws PermissionDeniedException {
|
||||
if (!isEnabled()) {
|
||||
return true;
|
||||
}
|
||||
@ -151,7 +152,7 @@ public class ProjectRoleBasedApiAccessChecker extends AdapterBase implements AP
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkAccess(Account account, String apiCommandName, ApiKeyPairPermission... apiKeyPairPermissions) throws PermissionDeniedException {
|
||||
public boolean checkAccess(Account account, String apiCommandName, ApiKeyPair keyPair, ApiKeyPairPermission... apiKeyPairPermissions) throws PermissionDeniedException {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -27,6 +27,7 @@ import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.exception.UnavailableCommandException;
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPair;
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPairPermission;
|
||||
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
@ -92,7 +93,7 @@ public class StaticRoleBasedAPIAccessChecker extends AdapterBase implements APIA
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkAccess(User user, String commandName, ApiKeyPairPermission... apiKeyPairPermissions) throws PermissionDeniedException {
|
||||
public boolean checkAccess(User user, String commandName, ApiKeyPair keyPair, ApiKeyPairPermission... apiKeyPairPermissions) throws PermissionDeniedException {
|
||||
if (!isEnabled()) {
|
||||
return true;
|
||||
}
|
||||
@ -102,11 +103,11 @@ public class StaticRoleBasedAPIAccessChecker extends AdapterBase implements APIA
|
||||
throw new PermissionDeniedException(String.format("The account with id [%s] for user with uuid [%s] is null.", user.getAccountId(), user.getUuid()));
|
||||
}
|
||||
|
||||
return checkAccess(account, commandName);
|
||||
return checkAccess(account, commandName, keyPair);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkAccess(Account account, String commandName, ApiKeyPairPermission... apiKeyPairPermissions) {
|
||||
public boolean checkAccess(Account account, String commandName, ApiKeyPair keyPair, ApiKeyPairPermission... apiKeyPairPermissions) {
|
||||
if (!isEnabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -252,7 +252,7 @@ public class ApiDiscoveryServiceImpl extends ComponentLifecycleBase implements A
|
||||
boolean isAllowed = true;
|
||||
for (APIChecker apiChecker : _apiAccessCheckers) {
|
||||
try {
|
||||
apiChecker.checkAccess(account, apiName);
|
||||
apiChecker.checkAccess(account, apiName, null);
|
||||
} catch (Exception ex) {
|
||||
isAllowed = false;
|
||||
}
|
||||
@ -287,7 +287,7 @@ public class ApiDiscoveryServiceImpl extends ComponentLifecycleBase implements A
|
||||
|
||||
for (APIChecker apiChecker : _apiAccessCheckers) {
|
||||
try {
|
||||
apiChecker.checkAccess(user, name);
|
||||
apiChecker.checkAccess(user, name, null);
|
||||
} catch (Exception ex) {
|
||||
logger.error(String.format("API discovery access check failed for [%s] with error [%s].", name, ex.getMessage()), ex);
|
||||
return null;
|
||||
|
||||
@ -29,6 +29,7 @@ import net.sf.ehcache.CacheManager;
|
||||
import org.apache.cloudstack.acl.Role;
|
||||
import org.apache.cloudstack.acl.RolePermissionEntity;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPair;
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPairPermission;
|
||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
@ -164,17 +165,17 @@ public class ApiRateLimitServiceImpl extends AdapterBase implements APIChecker,
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkAccess(User user, String apiCommandName, ApiKeyPairPermission ... apiKeyPairPermissions) throws PermissionDeniedException {
|
||||
public boolean checkAccess(User user, String apiCommandName, ApiKeyPair keyPair, ApiKeyPairPermission ... apiKeyPairPermissions) throws PermissionDeniedException {
|
||||
if (!isEnabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Account account = _accountService.getAccount(user.getAccountId());
|
||||
return checkAccess(account, apiCommandName, apiKeyPairPermissions);
|
||||
return checkAccess(account, apiCommandName, keyPair, apiKeyPairPermissions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkAccess(Account account, String commandName, ApiKeyPairPermission ... apiKeyPairPermissions) {
|
||||
public boolean checkAccess(Account account, String commandName, ApiKeyPair keyPair, ApiKeyPairPermission ... apiKeyPairPermissions) {
|
||||
Long accountId = account.getAccountId();
|
||||
if (_accountService.isRootAdmin(accountId)) {
|
||||
logger.info(String.format("Account [%s] is Root Admin, in this case, API limit does not apply.",
|
||||
|
||||
@ -89,7 +89,7 @@ public static void setUp() throws ConfigurationException {
|
||||
|
||||
private boolean isUnderLimit(User key) {
|
||||
try {
|
||||
s_limitService.checkAccess(key, null);
|
||||
s_limitService.checkAccess(key, null, null);
|
||||
return true;
|
||||
} catch (RequestLimitException ex) {
|
||||
return false;
|
||||
|
||||
@ -632,4 +632,9 @@ public class MockAccountManager extends ManagerBase implements AccountManager {
|
||||
public Account getAccountByUuid(String accountUuid) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends ApiKeyPairPermission> getAllExplicitKeyPairPermissions(Long keyPairId) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1007,7 +1007,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
||||
// if userId not null, that mean that user is logged in
|
||||
if (userId != null) {
|
||||
final User user = ApiDBUtils.findUserById(userId);
|
||||
return commandAvailable(remoteAddress, commandName, user);
|
||||
return commandAvailable(remoteAddress, commandName, user, null);
|
||||
} else {
|
||||
if (commandName.equalsIgnoreCase(ListGuiThemesCmd.class.getAnnotation(APICommand.class).name())) {
|
||||
return true;
|
||||
@ -1120,7 +1120,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!commandAvailable(remoteAddress, commandName, user)) {
|
||||
if (!commandAvailable(remoteAddress, commandName, user, null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1156,7 +1156,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
||||
CallContext.register(user, account);
|
||||
|
||||
List<ApiKeyPairPermission> keyPairPermissions = keyPairManager.findAllPermissionsByKeyPairId(keyPair.getId(), account.getRoleId());
|
||||
if (commandAvailable(remoteAddress, commandName, user, keyPairPermissions.toArray(new ApiKeyPairPermission[0]))) {
|
||||
if (commandAvailable(remoteAddress, commandName, user, keyPair, keyPairPermissions.toArray(new ApiKeyPairPermission[0]))) {
|
||||
logger.info("API accessed through API Key Pair. API Key: [{}].", keyPair.getApiKey());
|
||||
return true;
|
||||
}
|
||||
@ -1170,9 +1170,9 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean commandAvailable(final InetAddress remoteAddress, final String commandName, final User user, ApiKeyPairPermission... rolePermissions) {
|
||||
private boolean commandAvailable(final InetAddress remoteAddress, final String commandName, final User user, ApiKeyPair keyPair, ApiKeyPairPermission... rolePermissions) {
|
||||
try {
|
||||
checkCommandAvailable(user, commandName, remoteAddress, rolePermissions);
|
||||
checkCommandAvailable(user, commandName, remoteAddress, keyPair, rolePermissions);
|
||||
} catch (final RequestLimitException ex) {
|
||||
logger.debug(ex.getMessage());
|
||||
throw new ServerApiException(ApiErrorCode.API_LIMIT_EXCEED, ex.getMessage());
|
||||
@ -1465,7 +1465,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
||||
return domainIdArr[0];
|
||||
}
|
||||
|
||||
private void checkCommandAvailable(final User user, final String commandName, final InetAddress remoteAddress, ApiKeyPairPermission ... apiKeyPairPermissions) throws PermissionDeniedException {
|
||||
private void checkCommandAvailable(final User user, final String commandName, final InetAddress remoteAddress, ApiKeyPair keyPair, ApiKeyPairPermission... apiKeyPairPermissions) throws PermissionDeniedException {
|
||||
if (user == null) {
|
||||
throw new PermissionDeniedException("User is null for role based API access check for command" + commandName);
|
||||
}
|
||||
@ -1483,7 +1483,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
||||
}
|
||||
|
||||
for (final APIChecker apiChecker : apiAccessCheckers) {
|
||||
apiChecker.checkAccess(user, commandName, apiKeyPairPermissions);
|
||||
apiChecker.checkAccess(user, commandName, keyPair, apiKeyPairPermissions);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -45,12 +45,14 @@ import javax.crypto.spec.SecretKeySpec;
|
||||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.serializer.GsonHelper;
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import com.cloud.projects.dao.ProjectInvitationDao;
|
||||
import com.cloud.user.dao.AccountDao;
|
||||
import com.cloud.user.dao.SSHKeyPairDao;
|
||||
import com.cloud.user.dao.UserAccountDao;
|
||||
import com.cloud.user.dao.UserDao;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.cloud.utils.db.TransactionCallbackWithException;
|
||||
import org.apache.cloudstack.acl.APIChecker;
|
||||
import org.apache.cloudstack.acl.ApiKeyPairManagerImpl;
|
||||
@ -104,6 +106,7 @@ import org.apache.cloudstack.dns.DnsZone;
|
||||
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO;
|
||||
import org.apache.cloudstack.framework.messagebus.MessageBus;
|
||||
import org.apache.cloudstack.framework.messagebus.PublishScope;
|
||||
import org.apache.cloudstack.kms.KMSManager;
|
||||
@ -1506,7 +1509,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
||||
List<APIChecker> apiCheckers = getEnabledApiCheckers();
|
||||
for (String command : apiNameList) {
|
||||
try {
|
||||
checkApiAccess(apiCheckers, requested, command);
|
||||
checkApiAccess(apiCheckers, requested, command, null);
|
||||
} catch (PermissionDeniedException pde) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format(
|
||||
@ -1525,7 +1528,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
||||
logger.trace(String.format("permission to \"%s\" is requested",
|
||||
command));
|
||||
}
|
||||
checkApiAccess(apiCheckers, caller, command);
|
||||
checkApiAccess(apiCheckers, caller, command, null);
|
||||
} catch (PermissionDeniedException pde) {
|
||||
String msg = String.format("User of Account %s and domain %s can not create an account with access to more privileges they have themself.",
|
||||
caller, _domainMgr.getDomain(caller.getDomainId()));
|
||||
@ -1535,9 +1538,9 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
||||
}
|
||||
}
|
||||
|
||||
private void checkApiAccess(List<APIChecker> apiCheckers, Account caller, String command, ApiKeyPairPermission... apiKeyPairPermissions) {
|
||||
private void checkApiAccess(List<APIChecker> apiCheckers, Account caller, String command, ApiKeyPair keyPair, ApiKeyPairPermission... apiKeyPairPermissions) {
|
||||
for (final APIChecker apiChecker : apiCheckers) {
|
||||
apiChecker.checkAccess(caller, command, apiKeyPairPermissions);
|
||||
apiChecker.checkAccess(caller, command, keyPair, apiKeyPairPermissions);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1546,20 +1549,22 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
||||
List<APIChecker> apiCheckers = getEnabledApiCheckers();
|
||||
|
||||
List<ApiKeyPairPermission> keyPairPermissions = new ArrayList<>();
|
||||
ApiKeyPair keyPair = null;
|
||||
if (apiKey != null) {
|
||||
Ternary<User, Account, ApiKeyPair> keyPairTernary = findUserByApiKey(apiKey);
|
||||
if (keyPairTernary != null) {
|
||||
keyPairPermissions = keyPairManager.findAllPermissionsByKeyPairId(keyPairTernary.third().getId(), caller.getRoleId());
|
||||
keyPair = keyPairTernary.third();
|
||||
}
|
||||
}
|
||||
|
||||
checkApiAccess(apiCheckers, caller, command, keyPairPermissions.toArray(new ApiKeyPairPermission[0]));
|
||||
checkApiAccess(apiCheckers, caller, command, keyPair, keyPairPermissions.toArray(new ApiKeyPairPermission[0]));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkApiAccess(Account caller, String command) {
|
||||
List<APIChecker> apiCheckers = getEnabledApiCheckers();
|
||||
checkApiAccess(apiCheckers, caller, command);
|
||||
checkApiAccess(apiCheckers, caller, command, null);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@ -3385,24 +3390,29 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
||||
@Override
|
||||
public String getAccessingApiKey(BaseCmd cmd) {
|
||||
try {
|
||||
if (cmd instanceof BaseAsyncCmd && ((BaseAsyncCmd) cmd).getJob().toString().contains("\"signature\"")) {
|
||||
return parseApiKeyFromAsyncJob((BaseAsyncCmd) cmd);
|
||||
Map<String, String> requestPayload = cmd.getFullUrlParams();
|
||||
|
||||
if (cmd instanceof BaseAsyncCmd && ((BaseAsyncCmd) cmd).getJob() instanceof AsyncJobVO) {
|
||||
String asyncJobPayload = ((AsyncJobVO) ((BaseAsyncCmd) cmd).getJob()).getCmdInfo();
|
||||
requestPayload = GsonHelper.getGson().fromJson(asyncJobPayload, new TypeToken<HashMap<String, String>>() {}.getType());
|
||||
}
|
||||
boolean accessedByApiKey = cmd.getFullUrlParams().containsKey(ApiConstants.SIGNATURE);
|
||||
String accessingApiKey = cmd.getFullUrlParams().get("apiKey");
|
||||
|
||||
boolean accessedByApiKey = requestPayload.keySet().stream().anyMatch(ApiConstants.SIGNATURE::equalsIgnoreCase);
|
||||
if (accessedByApiKey) {
|
||||
return accessingApiKey;
|
||||
String apiKey = requestPayload.entrySet().stream()
|
||||
.filter(e -> ApiConstants.API_KEY.equalsIgnoreCase(e.getKey()))
|
||||
.map(Map.Entry::getValue).findFirst().orElse(null);
|
||||
if (apiKey != null) {
|
||||
logger.info("Request's API key is [{}].", apiKey);
|
||||
return apiKey;
|
||||
}
|
||||
}
|
||||
} catch (NullPointerException e) {
|
||||
logger.info("Accessing API through session.");
|
||||
logger.warn("Unable to identify request API key due to: {}.", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String parseApiKeyFromAsyncJob(BaseAsyncCmd cmd) {
|
||||
String jobString = cmd.getJob().toString();
|
||||
int indexOfApiKey = jobString.indexOf("apiKey") + 9;
|
||||
return jobString.substring(indexOfApiKey, jobString.indexOf("\"", indexOfApiKey));
|
||||
logger.info("Request's signature or API key were not identified; assuming it has been authenticated via session.");
|
||||
return null;
|
||||
}
|
||||
|
||||
private Boolean isApiKeySupersetOfPermission(List<RolePermissionEntity> baseKeyPairPermissions, List<RolePermissionEntity> comparedPermissions) {
|
||||
@ -3453,8 +3463,13 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
||||
internalDeleteApiKey(keyPair);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends ApiKeyPairPermission> getAllExplicitKeyPairPermissions(Long keyPairId) {
|
||||
return apiKeyPairPermissionsDao.findAllByApiKeyPairId(keyPairId);
|
||||
}
|
||||
|
||||
private void internalDeleteApiKey(ApiKeyPair keyPair) {
|
||||
List<ApiKeyPairPermissionVO> permissions = apiKeyPairPermissionsDao.findAllByApiKeyPairId(keyPair.getId());
|
||||
List<? extends ApiKeyPairPermission> permissions = getAllExplicitKeyPairPermissions(keyPair.getId());
|
||||
for (ApiKeyPairPermission permission : permissions) {
|
||||
apiKeyPairPermissionsDao.remove(permission.getId());
|
||||
}
|
||||
@ -3622,6 +3637,14 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
||||
permissions.add(new ApiKeyPairPermissionVO(0, rule, rulePermission, ruleDescription));
|
||||
}
|
||||
|
||||
if (permissions.isEmpty() && accessingApiKey != null && doesKeyPairHaveExplicitPermissions(accessingApiKey)) {
|
||||
logger.debug("No rules were specified for the new API key pair. Since the accessing API key [{}]" +
|
||||
" has explicit permissions, these permissions will be defined as the rule set for the new pair.", accessingApiKey);
|
||||
permissions = allPermissions.stream().map(permission -> (
|
||||
new ApiKeyPairPermissionVO(0, permission.getRule().getRuleString(), permission.getPermission(), permission.getDescription())
|
||||
)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
if (!isApiKeySupersetOfPermission(allPermissions, permissions)) {
|
||||
throw new InvalidParameterValueException(String.format("The key pair being created has a bigger set of permissions than the account [%s] " +
|
||||
"that owns it. This is not allowed.", account.getUuid()));
|
||||
@ -3636,6 +3659,16 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
||||
return savedApiKeyPair;
|
||||
}
|
||||
|
||||
private boolean doesKeyPairHaveExplicitPermissions(String apiKey) {
|
||||
ApiKeyPair apiKeyPair = keyPairManager.findByApiKey(apiKey);
|
||||
if (apiKeyPair == null) {
|
||||
logger.info("Unable to find API key pair entity with the API key [{}].", apiKey);
|
||||
return false;
|
||||
}
|
||||
|
||||
return !getAllExplicitKeyPairPermissions(apiKeyPair.getId()).isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RolePermissionEntity> getAllKeypairPermissions(String apiKey) {
|
||||
if (apiKey == null) {
|
||||
|
||||
@ -3683,7 +3683,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
||||
if (checkExpunge && expunge) {
|
||||
String jobParamsString = ((AsyncJobVO) cmd.getJob()).getCmdInfo();
|
||||
HashMap<String,String> jobParams = GsonHelper.getGson().fromJson(jobParamsString, jobParamsType);
|
||||
String apiKey = jobParams.get("apiKey");
|
||||
String apiKey = jobParams.entrySet().stream()
|
||||
.filter(e -> ApiConstants.API_KEY.equalsIgnoreCase(e.getKey()))
|
||||
.map(Map.Entry::getValue).findFirst().orElse(null);
|
||||
checkExpungeVmPermission(ctx.getCallingAccount(), apiKey);
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user