mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
Merge branch '4.20'
This commit is contained in:
commit
650b5ec3da
@ -18,6 +18,7 @@ package com.cloud.kubernetes.cluster;
|
|||||||
|
|
||||||
import org.apache.cloudstack.acl.ControlledEntity;
|
import org.apache.cloudstack.acl.ControlledEntity;
|
||||||
|
|
||||||
|
import com.cloud.user.Account;
|
||||||
import com.cloud.uservm.UserVm;
|
import com.cloud.uservm.UserVm;
|
||||||
import com.cloud.utils.component.Adapter;
|
import com.cloud.utils.component.Adapter;
|
||||||
|
|
||||||
@ -26,4 +27,5 @@ public interface KubernetesServiceHelper extends Adapter {
|
|||||||
ControlledEntity findByUuid(String uuid);
|
ControlledEntity findByUuid(String uuid);
|
||||||
ControlledEntity findByVmId(long vmId);
|
ControlledEntity findByVmId(long vmId);
|
||||||
void checkVmCanBeDestroyed(UserVm userVm);
|
void checkVmCanBeDestroyed(UserVm userVm);
|
||||||
|
void cleanupForAccount(Account account);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -71,6 +71,7 @@ public interface Account extends ControlledEntity, InternalIdentity, Identity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static final long ACCOUNT_ID_SYSTEM = 1;
|
public static final long ACCOUNT_ID_SYSTEM = 1;
|
||||||
|
public static final long ACCOUNT_ID_ADMIN = 2;
|
||||||
|
|
||||||
public String getAccountName();
|
public String getAccountName();
|
||||||
|
|
||||||
|
|||||||
@ -596,6 +596,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||||||
offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworks,
|
offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworks,
|
||||||
"Offering for Isolated VPC networks with Source Nat service enabled", TrafficType.Guest, null, false, Availability.Optional, null,
|
"Offering for Isolated VPC networks with Source Nat service enabled", TrafficType.Guest, null, false, Availability.Optional, null,
|
||||||
defaultVPCOffProviders, true, Network.GuestType.Isolated, false, null, true, null, false, false, null, false, null, true, true, false, false, null, null, null,true, null, null, false);
|
defaultVPCOffProviders, true, Network.GuestType.Isolated, false, null, true, null, false, false, null, false, null, true, true, false, false, null, null, null,true, null, null, false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//#6 - default vpc offering with no LB service
|
//#6 - default vpc offering with no LB service
|
||||||
|
|||||||
@ -16,6 +16,9 @@
|
|||||||
// under the License.
|
// under the License.
|
||||||
package com.cloud.user.dao;
|
package com.cloud.user.dao;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import com.cloud.user.Account;
|
import com.cloud.user.Account;
|
||||||
import com.cloud.user.AccountVO;
|
import com.cloud.user.AccountVO;
|
||||||
import com.cloud.user.User;
|
import com.cloud.user.User;
|
||||||
@ -23,9 +26,6 @@ import com.cloud.utils.Pair;
|
|||||||
import com.cloud.utils.db.Filter;
|
import com.cloud.utils.db.Filter;
|
||||||
import com.cloud.utils.db.GenericDao;
|
import com.cloud.utils.db.GenericDao;
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public interface AccountDao extends GenericDao<AccountVO, Long> {
|
public interface AccountDao extends GenericDao<AccountVO, Long> {
|
||||||
Pair<User, Account> findUserAccountByApiKey(String apiKey);
|
Pair<User, Account> findUserAccountByApiKey(String apiKey);
|
||||||
|
|
||||||
@ -33,6 +33,8 @@ public interface AccountDao extends GenericDao<AccountVO, Long> {
|
|||||||
|
|
||||||
Pair<List<AccountVO>, Integer> findAccountsLike(String accountName, Filter filter);
|
Pair<List<AccountVO>, Integer> findAccountsLike(String accountName, Filter filter);
|
||||||
|
|
||||||
|
List<AccountVO> findAccountsByName(String accountName);
|
||||||
|
|
||||||
List<AccountVO> findActiveAccounts(Long maxAccountId, Filter filter);
|
List<AccountVO> findActiveAccounts(Long maxAccountId, Filter filter);
|
||||||
|
|
||||||
List<AccountVO> findRecentlyDeletedAccounts(Long maxAccountId, Date earliestRemovedDate, Filter filter);
|
List<AccountVO> findRecentlyDeletedAccounts(Long maxAccountId, Date earliestRemovedDate, Filter filter);
|
||||||
|
|||||||
@ -16,6 +16,14 @@
|
|||||||
// under the License.
|
// under the License.
|
||||||
package com.cloud.user.dao;
|
package com.cloud.user.dao;
|
||||||
|
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import com.cloud.user.Account;
|
import com.cloud.user.Account;
|
||||||
import com.cloud.user.Account.State;
|
import com.cloud.user.Account.State;
|
||||||
import com.cloud.user.AccountVO;
|
import com.cloud.user.AccountVO;
|
||||||
@ -30,14 +38,7 @@ import com.cloud.utils.db.SearchBuilder;
|
|||||||
import com.cloud.utils.db.SearchCriteria;
|
import com.cloud.utils.db.SearchCriteria;
|
||||||
import com.cloud.utils.db.SearchCriteria.Func;
|
import com.cloud.utils.db.SearchCriteria.Func;
|
||||||
import com.cloud.utils.db.SearchCriteria.Op;
|
import com.cloud.utils.db.SearchCriteria.Op;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import com.cloud.utils.db.TransactionLegacy;
|
import com.cloud.utils.db.TransactionLegacy;
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class AccountDaoImpl extends GenericDaoBase<AccountVO, Long> implements AccountDao {
|
public class AccountDaoImpl extends GenericDaoBase<AccountVO, Long> implements AccountDao {
|
||||||
@ -190,6 +191,16 @@ public class AccountDaoImpl extends GenericDaoBase<AccountVO, Long> implements A
|
|||||||
return searchAndCount(sc, filter);
|
return searchAndCount(sc, filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<AccountVO> findAccountsByName(String accountName) {
|
||||||
|
SearchBuilder<AccountVO> sb = createSearchBuilder();
|
||||||
|
sb.and("accountName", sb.entity().getAccountName(), SearchCriteria.Op.EQ);
|
||||||
|
sb.done();
|
||||||
|
SearchCriteria<AccountVO> sc = sb.create();
|
||||||
|
sc.setParameters("accountName", accountName);
|
||||||
|
return search(sc, null);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Account findEnabledAccount(String accountName, Long domainId) {
|
public Account findEnabledAccount(String accountName, Long domainId) {
|
||||||
SearchCriteria<AccountVO> sc = AllFieldsSearch.create("accountName", accountName);
|
SearchCriteria<AccountVO> sc = AllFieldsSearch.create("accountName", accountName);
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
package org.apache.cloudstack.api.command;
|
package org.apache.cloudstack.api.command;
|
||||||
|
|
||||||
import com.cloud.utils.Pair;
|
import com.cloud.utils.Pair;
|
||||||
|
import org.apache.cloudstack.api.ACL;
|
||||||
import org.apache.cloudstack.api.APICommand;
|
import org.apache.cloudstack.api.APICommand;
|
||||||
import org.apache.cloudstack.api.ApiConstants;
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
import org.apache.cloudstack.api.BaseCmd;
|
import org.apache.cloudstack.api.BaseCmd;
|
||||||
@ -32,6 +33,7 @@ import javax.inject.Inject;
|
|||||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
|
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
|
||||||
public class QuotaConfigureEmailCmd extends BaseCmd {
|
public class QuotaConfigureEmailCmd extends BaseCmd {
|
||||||
|
|
||||||
|
@ACL
|
||||||
@Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, entityType = AccountResponse.class, required = true,
|
@Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, entityType = AccountResponse.class, required = true,
|
||||||
description = "Account ID for which to configure quota template email or min balance")
|
description = "Account ID for which to configure quota template email or min balance")
|
||||||
private long accountId;
|
private long accountId;
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
//under the License.
|
//under the License.
|
||||||
package org.apache.cloudstack.api.command;
|
package org.apache.cloudstack.api.command;
|
||||||
|
|
||||||
import com.cloud.user.Account;
|
import org.apache.cloudstack.api.ACL;
|
||||||
import org.apache.cloudstack.api.APICommand;
|
import org.apache.cloudstack.api.APICommand;
|
||||||
import org.apache.cloudstack.api.ApiConstants;
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
import org.apache.cloudstack.api.BaseCmd;
|
import org.apache.cloudstack.api.BaseCmd;
|
||||||
@ -32,6 +32,7 @@ import javax.inject.Inject;
|
|||||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
|
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
|
||||||
public class QuotaListEmailConfigurationCmd extends BaseCmd {
|
public class QuotaListEmailConfigurationCmd extends BaseCmd {
|
||||||
|
|
||||||
|
@ACL
|
||||||
@Parameter(name = ApiConstants.ACCOUNT_ID, type = BaseCmd.CommandType.UUID, entityType = AccountResponse.class, required = true,
|
@Parameter(name = ApiConstants.ACCOUNT_ID, type = BaseCmd.CommandType.UUID, entityType = AccountResponse.class, required = true,
|
||||||
description = "Account ID for which to list quota template email configurations")
|
description = "Account ID for which to list quota template email configurations")
|
||||||
private long accountId;
|
private long accountId;
|
||||||
@ -49,6 +50,6 @@ public class QuotaListEmailConfigurationCmd extends BaseCmd {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getEntityOwnerId() {
|
public long getEntityOwnerId() {
|
||||||
return Account.ACCOUNT_ID_SYSTEM;
|
return accountId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import com.cloud.agent.resource.virtualnetwork.VRScripts;
|
import com.cloud.agent.resource.virtualnetwork.VRScripts;
|
||||||
@ -247,7 +248,7 @@ public final class CitrixStartCommandWrapper extends CommandWrapper<StartCommand
|
|||||||
List<DiskTO> disks = new ArrayList<DiskTO>(vmSpec.getDisks().length);
|
List<DiskTO> disks = new ArrayList<DiskTO>(vmSpec.getDisks().length);
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (final DiskTO disk : vmSpec.getDisks()) {
|
for (final DiskTO disk : vmSpec.getDisks()) {
|
||||||
if (Volume.Type.ISO.equals(disk.getType())) {
|
if (Volume.Type.ISO.equals(disk.getType()) && Objects.nonNull(disk.getPath())) {
|
||||||
disks.add(0, disk);
|
disks.add(0, disk);
|
||||||
} else {
|
} else {
|
||||||
disks.add(index, disk);
|
disks.add(index, disk);
|
||||||
|
|||||||
@ -44,6 +44,11 @@ import javax.naming.ConfigurationException;
|
|||||||
import com.cloud.uservm.UserVm;
|
import com.cloud.uservm.UserVm;
|
||||||
import com.cloud.vm.UserVmService;
|
import com.cloud.vm.UserVmService;
|
||||||
import org.apache.cloudstack.acl.ControlledEntity;
|
import org.apache.cloudstack.acl.ControlledEntity;
|
||||||
|
import org.apache.cloudstack.acl.Role;
|
||||||
|
import org.apache.cloudstack.acl.RolePermissionEntity;
|
||||||
|
import org.apache.cloudstack.acl.RoleService;
|
||||||
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
|
import org.apache.cloudstack.acl.Rule;
|
||||||
import org.apache.cloudstack.acl.SecurityChecker;
|
import org.apache.cloudstack.acl.SecurityChecker;
|
||||||
import org.apache.cloudstack.annotation.AnnotationService;
|
import org.apache.cloudstack.annotation.AnnotationService;
|
||||||
import org.apache.cloudstack.annotation.dao.AnnotationDao;
|
import org.apache.cloudstack.annotation.dao.AnnotationDao;
|
||||||
@ -52,6 +57,14 @@ import org.apache.cloudstack.api.ApiConstants;
|
|||||||
import org.apache.cloudstack.api.ApiConstants.VMDetails;
|
import org.apache.cloudstack.api.ApiConstants.VMDetails;
|
||||||
import org.apache.cloudstack.api.BaseCmd;
|
import org.apache.cloudstack.api.BaseCmd;
|
||||||
import org.apache.cloudstack.api.ResponseObject.ResponseView;
|
import org.apache.cloudstack.api.ResponseObject.ResponseView;
|
||||||
|
import org.apache.cloudstack.api.command.user.address.AssociateIPAddrCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.address.DisassociateIPAddrCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.address.ListPublicIpAddressesCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.firewall.CreateFirewallRuleCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.firewall.DeleteFirewallRuleCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.firewall.ListFirewallRulesCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.firewall.UpdateFirewallRuleCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.job.QueryAsyncJobResultCmd;
|
||||||
import org.apache.cloudstack.api.command.user.kubernetes.cluster.AddVirtualMachinesToKubernetesClusterCmd;
|
import org.apache.cloudstack.api.command.user.kubernetes.cluster.AddVirtualMachinesToKubernetesClusterCmd;
|
||||||
import org.apache.cloudstack.api.command.user.kubernetes.cluster.CreateKubernetesClusterCmd;
|
import org.apache.cloudstack.api.command.user.kubernetes.cluster.CreateKubernetesClusterCmd;
|
||||||
import org.apache.cloudstack.api.command.user.kubernetes.cluster.DeleteKubernetesClusterCmd;
|
import org.apache.cloudstack.api.command.user.kubernetes.cluster.DeleteKubernetesClusterCmd;
|
||||||
@ -62,6 +75,18 @@ import org.apache.cloudstack.api.command.user.kubernetes.cluster.ScaleKubernetes
|
|||||||
import org.apache.cloudstack.api.command.user.kubernetes.cluster.StartKubernetesClusterCmd;
|
import org.apache.cloudstack.api.command.user.kubernetes.cluster.StartKubernetesClusterCmd;
|
||||||
import org.apache.cloudstack.api.command.user.kubernetes.cluster.StopKubernetesClusterCmd;
|
import org.apache.cloudstack.api.command.user.kubernetes.cluster.StopKubernetesClusterCmd;
|
||||||
import org.apache.cloudstack.api.command.user.kubernetes.cluster.UpgradeKubernetesClusterCmd;
|
import org.apache.cloudstack.api.command.user.kubernetes.cluster.UpgradeKubernetesClusterCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.loadbalancer.AssignToLoadBalancerRuleCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.loadbalancer.CreateLoadBalancerRuleCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.loadbalancer.DeleteLoadBalancerRuleCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.loadbalancer.ListLoadBalancerRuleInstancesCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.loadbalancer.ListLoadBalancerRulesCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.loadbalancer.RemoveFromLoadBalancerRuleCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.loadbalancer.UpdateLoadBalancerRuleCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.network.CreateNetworkACLCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.network.DeleteNetworkACLCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.network.ListNetworkACLsCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.network.ListNetworksCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.vm.ListVMsCmd;
|
||||||
import org.apache.cloudstack.api.response.KubernetesClusterConfigResponse;
|
import org.apache.cloudstack.api.response.KubernetesClusterConfigResponse;
|
||||||
import org.apache.cloudstack.api.response.KubernetesClusterResponse;
|
import org.apache.cloudstack.api.response.KubernetesClusterResponse;
|
||||||
import org.apache.cloudstack.api.response.ListResponse;
|
import org.apache.cloudstack.api.response.ListResponse;
|
||||||
@ -148,6 +173,8 @@ import com.cloud.offerings.dao.NetworkOfferingServiceMapDao;
|
|||||||
import com.cloud.org.Cluster;
|
import com.cloud.org.Cluster;
|
||||||
import com.cloud.org.Grouping;
|
import com.cloud.org.Grouping;
|
||||||
import com.cloud.projects.Project;
|
import com.cloud.projects.Project;
|
||||||
|
import com.cloud.projects.ProjectAccount;
|
||||||
|
import com.cloud.projects.ProjectManager;
|
||||||
import com.cloud.resource.ResourceManager;
|
import com.cloud.resource.ResourceManager;
|
||||||
import com.cloud.service.ServiceOfferingVO;
|
import com.cloud.service.ServiceOfferingVO;
|
||||||
import com.cloud.service.dao.ServiceOfferingDao;
|
import com.cloud.service.dao.ServiceOfferingDao;
|
||||||
@ -156,14 +183,17 @@ import com.cloud.storage.dao.VMTemplateDao;
|
|||||||
import com.cloud.user.Account;
|
import com.cloud.user.Account;
|
||||||
import com.cloud.user.AccountManager;
|
import com.cloud.user.AccountManager;
|
||||||
import com.cloud.user.AccountService;
|
import com.cloud.user.AccountService;
|
||||||
|
import com.cloud.user.AccountVO;
|
||||||
import com.cloud.user.SSHKeyPairVO;
|
import com.cloud.user.SSHKeyPairVO;
|
||||||
import com.cloud.user.User;
|
import com.cloud.user.User;
|
||||||
import com.cloud.user.UserAccount;
|
import com.cloud.user.UserAccount;
|
||||||
import com.cloud.user.UserVO;
|
import com.cloud.user.UserVO;
|
||||||
|
import com.cloud.user.dao.AccountDao;
|
||||||
import com.cloud.user.dao.SSHKeyPairDao;
|
import com.cloud.user.dao.SSHKeyPairDao;
|
||||||
import com.cloud.user.dao.UserDao;
|
import com.cloud.user.dao.UserDao;
|
||||||
import com.cloud.utils.Pair;
|
import com.cloud.utils.Pair;
|
||||||
import com.cloud.utils.Ternary;
|
import com.cloud.utils.Ternary;
|
||||||
|
import com.cloud.utils.UuidUtils;
|
||||||
import com.cloud.utils.component.ComponentContext;
|
import com.cloud.utils.component.ComponentContext;
|
||||||
import com.cloud.utils.component.ManagerBase;
|
import com.cloud.utils.component.ManagerBase;
|
||||||
import com.cloud.utils.concurrency.NamedThreadFactory;
|
import com.cloud.utils.concurrency.NamedThreadFactory;
|
||||||
@ -187,6 +217,32 @@ import org.apache.logging.log4j.Level;
|
|||||||
public class KubernetesClusterManagerImpl extends ManagerBase implements KubernetesClusterService {
|
public class KubernetesClusterManagerImpl extends ManagerBase implements KubernetesClusterService {
|
||||||
|
|
||||||
private static final String DEFAULT_NETWORK_OFFERING_FOR_KUBERNETES_SERVICE_NAME = "DefaultNetworkOfferingforKubernetesService";
|
private static final String DEFAULT_NETWORK_OFFERING_FOR_KUBERNETES_SERVICE_NAME = "DefaultNetworkOfferingforKubernetesService";
|
||||||
|
private static final List<Class<?>> PROJECT_KUBERNETES_ACCOUNT_ROLE_ALLOWED_APIS = Arrays.asList(
|
||||||
|
QueryAsyncJobResultCmd.class,
|
||||||
|
ListVMsCmd.class,
|
||||||
|
ListNetworksCmd.class,
|
||||||
|
ListPublicIpAddressesCmd.class,
|
||||||
|
AssociateIPAddrCmd.class,
|
||||||
|
DisassociateIPAddrCmd.class,
|
||||||
|
ListLoadBalancerRulesCmd.class,
|
||||||
|
CreateLoadBalancerRuleCmd.class,
|
||||||
|
UpdateLoadBalancerRuleCmd.class,
|
||||||
|
DeleteLoadBalancerRuleCmd.class,
|
||||||
|
AssignToLoadBalancerRuleCmd.class,
|
||||||
|
RemoveFromLoadBalancerRuleCmd.class,
|
||||||
|
ListLoadBalancerRuleInstancesCmd.class,
|
||||||
|
ListFirewallRulesCmd.class,
|
||||||
|
CreateFirewallRuleCmd.class,
|
||||||
|
UpdateFirewallRuleCmd.class,
|
||||||
|
DeleteFirewallRuleCmd.class,
|
||||||
|
ListNetworkACLsCmd.class,
|
||||||
|
CreateNetworkACLCmd.class,
|
||||||
|
DeleteNetworkACLCmd.class,
|
||||||
|
ListKubernetesClustersCmd.class,
|
||||||
|
ScaleKubernetesClusterCmd.class
|
||||||
|
);
|
||||||
|
private static final String PROJECT_KUBERNETES_ACCOUNT_FIRST_NAME = "Kubernetes";
|
||||||
|
private static final String PROJECT_KUBERNETES_ACCOUNT_LAST_NAME = "Service User";
|
||||||
private static final String DEFAULT_NETWORK_OFFERING_FOR_KUBERNETES_SERVICE_DISPLAY_TEXT = "Network Offering used for CloudStack Kubernetes service";
|
private static final String DEFAULT_NETWORK_OFFERING_FOR_KUBERNETES_SERVICE_DISPLAY_TEXT = "Network Offering used for CloudStack Kubernetes service";
|
||||||
private static final String DEFAULT_NSX_NETWORK_OFFERING_FOR_KUBERNETES_SERVICE_NAME = "DefaultNSXNetworkOfferingforKubernetesService";
|
private static final String DEFAULT_NSX_NETWORK_OFFERING_FOR_KUBERNETES_SERVICE_NAME = "DefaultNSXNetworkOfferingforKubernetesService";
|
||||||
private static final String DEFAULT_NSX_VPC_TIER_NETWORK_OFFERING_FOR_KUBERNETES_SERVICE_NAME = "DefaultNSXVPCNetworkOfferingforKubernetesService";
|
private static final String DEFAULT_NSX_VPC_TIER_NETWORK_OFFERING_FOR_KUBERNETES_SERVICE_NAME = "DefaultNSXVPCNetworkOfferingforKubernetesService";
|
||||||
@ -264,11 +320,16 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||||||
public SecurityGroupService securityGroupService;
|
public SecurityGroupService securityGroupService;
|
||||||
@Inject
|
@Inject
|
||||||
public NetworkHelper networkHelper;
|
public NetworkHelper networkHelper;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private UserVmService userVmService;
|
private UserVmService userVmService;
|
||||||
@Inject
|
@Inject
|
||||||
RoutedIpv4Manager routedIpv4Manager;
|
RoutedIpv4Manager routedIpv4Manager;
|
||||||
|
@Inject
|
||||||
|
public AccountDao accountDao;
|
||||||
|
@Inject
|
||||||
|
public ProjectManager projectManager;
|
||||||
|
@Inject
|
||||||
|
RoleService roleService;
|
||||||
|
|
||||||
private void logMessage(final Level logLevel, final String message, final Exception e) {
|
private void logMessage(final Level logLevel, final String message, final Exception e) {
|
||||||
if (logLevel == Level.WARN) {
|
if (logLevel == Level.WARN) {
|
||||||
@ -1381,23 +1442,31 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String[] getServiceUserKeys(KubernetesClusterVO kubernetesCluster) {
|
protected String[] createUserApiKeyAndSecretKey(long userId) {
|
||||||
Account owner = accountService.getActiveAccountById(kubernetesCluster.getAccountId());
|
CallContext.register(User.UID_SYSTEM, Account.ACCOUNT_ID_SYSTEM);
|
||||||
if (owner == null || owner.getType() == Account.Type.PROJECT) {
|
try {
|
||||||
owner = CallContext.current().getCallingAccount();
|
return accountService.createApiKeyAndSecretKey(userId);
|
||||||
|
} finally {
|
||||||
|
CallContext.unregister();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String[] getServiceUserKeys(Account owner) {
|
||||||
|
String username = owner.getAccountName();
|
||||||
|
if (!username.startsWith(KUBEADMIN_ACCOUNT_NAME + "-")) {
|
||||||
|
username += "-" + KUBEADMIN_ACCOUNT_NAME;
|
||||||
}
|
}
|
||||||
String username = owner.getAccountName() + "-" + KUBEADMIN_ACCOUNT_NAME;
|
|
||||||
UserAccount kubeadmin = accountService.getActiveUserAccount(username, owner.getDomainId());
|
UserAccount kubeadmin = accountService.getActiveUserAccount(username, owner.getDomainId());
|
||||||
String[] keys = null;
|
String[] keys;
|
||||||
if (kubeadmin == null) {
|
if (kubeadmin == null) {
|
||||||
User kube = userDao.persist(new UserVO(owner.getAccountId(), username, UUID.randomUUID().toString(), owner.getAccountName(),
|
User kube = userDao.persist(new UserVO(owner.getAccountId(), username, UUID.randomUUID().toString(), owner.getAccountName(),
|
||||||
KUBEADMIN_ACCOUNT_NAME, "kubeadmin", null, UUID.randomUUID().toString(), User.Source.UNKNOWN));
|
KUBEADMIN_ACCOUNT_NAME, "kubeadmin", null, UUID.randomUUID().toString(), User.Source.UNKNOWN));
|
||||||
keys = accountService.createApiKeyAndSecretKey(kube.getId());
|
keys = createUserApiKeyAndSecretKey(kube.getId());
|
||||||
} else {
|
} else {
|
||||||
String apiKey = kubeadmin.getApiKey();
|
String apiKey = kubeadmin.getApiKey();
|
||||||
String secretKey = kubeadmin.getSecretKey();
|
String secretKey = kubeadmin.getSecretKey();
|
||||||
if (StringUtils.isAnyEmpty(apiKey, secretKey)) {
|
if (StringUtils.isAnyEmpty(apiKey, secretKey)) {
|
||||||
keys = accountService.createApiKeyAndSecretKey(kubeadmin.getId());
|
keys = createUserApiKeyAndSecretKey(kubeadmin.getId());
|
||||||
} else {
|
} else {
|
||||||
keys = new String[]{apiKey, secretKey};
|
keys = new String[]{apiKey, secretKey};
|
||||||
}
|
}
|
||||||
@ -1405,6 +1474,76 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Role createProjectKubernetesAccountRole() {
|
||||||
|
Role role = roleService.createRole(PROJECT_KUBEADMIN_ACCOUNT_ROLE_NAME, RoleType.User,
|
||||||
|
PROJECT_KUBEADMIN_ACCOUNT_ROLE_NAME, false);
|
||||||
|
for (Class<?> allowedApi : PROJECT_KUBERNETES_ACCOUNT_ROLE_ALLOWED_APIS) {
|
||||||
|
final String apiName = BaseCmd.getCommandNameByClass(allowedApi);
|
||||||
|
roleService.createRolePermission(role, new Rule(apiName), RolePermissionEntity.Permission.ALLOW,
|
||||||
|
String.format("Allow %s", apiName));
|
||||||
|
}
|
||||||
|
roleService.createRolePermission(role, new Rule("*"), RolePermissionEntity.Permission.DENY,
|
||||||
|
"Deny all");
|
||||||
|
logger.debug(String.format("Created default role for Kubernetes service account in projects: %s", role));
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Role getProjectKubernetesAccountRole() {
|
||||||
|
List<Role> roles = roleService.findRolesByName(PROJECT_KUBEADMIN_ACCOUNT_ROLE_NAME);
|
||||||
|
if (CollectionUtils.isNotEmpty(roles)) {
|
||||||
|
Role role = roles.get(0);
|
||||||
|
logger.debug(String.format("Found default role for Kubernetes service account in projects: %s", role));
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
return createProjectKubernetesAccountRole();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Account createProjectKubernetesAccount(final Project project, final String accountName) {
|
||||||
|
CallContext.register(User.UID_SYSTEM, Account.ACCOUNT_ID_SYSTEM);
|
||||||
|
try {
|
||||||
|
Role role = getProjectKubernetesAccountRole();
|
||||||
|
UserAccount userAccount = accountService.createUserAccount(accountName,
|
||||||
|
UuidUtils.first(UUID.randomUUID().toString()), PROJECT_KUBERNETES_ACCOUNT_FIRST_NAME,
|
||||||
|
PROJECT_KUBERNETES_ACCOUNT_LAST_NAME, null, null, accountName, Account.Type.NORMAL, role.getId(),
|
||||||
|
project.getDomainId(), null, null, null, null, User.Source.NATIVE);
|
||||||
|
projectManager.assignAccountToProject(project, userAccount.getAccountId(), ProjectAccount.Role.Regular,
|
||||||
|
userAccount.getId(), null);
|
||||||
|
Account account = accountService.getAccount(userAccount.getAccountId());
|
||||||
|
logger.debug(String.format("Created Kubernetes service account in project %s: %s", project, account));
|
||||||
|
return account;
|
||||||
|
} finally {
|
||||||
|
CallContext.unregister();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Account getProjectKubernetesAccount(final Account callerAccount, final boolean create) {
|
||||||
|
Project project = ApiDBUtils.findProjectByProjectAccountId(callerAccount.getId());
|
||||||
|
final String accountName = String.format("%s-%s", KUBEADMIN_ACCOUNT_NAME, UuidUtils.first(project.getUuid()));
|
||||||
|
List<AccountVO> accounts = accountDao.findAccountsByName(accountName);
|
||||||
|
for (AccountVO account : accounts) {
|
||||||
|
if (projectManager.canAccessProjectAccount(account, project.getProjectAccountId())) {
|
||||||
|
logger.debug(String.format("Created Kubernetes service account in project %s: %s", project, account));
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return create ? createProjectKubernetesAccount(project, accountName) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Account getProjectKubernetesAccount(final Account callerAccount) {
|
||||||
|
return getProjectKubernetesAccount(callerAccount, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String[] getServiceUserKeys(KubernetesClusterVO kubernetesCluster) {
|
||||||
|
Account owner = accountService.getActiveAccountById(kubernetesCluster.getAccountId());
|
||||||
|
if (owner == null) {
|
||||||
|
owner = CallContext.current().getCallingAccount();
|
||||||
|
}
|
||||||
|
if (owner.getType() == Account.Type.PROJECT) {
|
||||||
|
owner = getProjectKubernetesAccount(owner);
|
||||||
|
}
|
||||||
|
return getServiceUserKeys(owner);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ActionEvent(eventType = KubernetesClusterEventTypes.EVENT_KUBERNETES_CLUSTER_STOP,
|
@ActionEvent(eventType = KubernetesClusterEventTypes.EVENT_KUBERNETES_CLUSTER_STOP,
|
||||||
eventDescription = "stopping Kubernetes cluster", async = true)
|
eventDescription = "stopping Kubernetes cluster", async = true)
|
||||||
@ -1449,15 +1588,13 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||||||
logAndThrow(Level.ERROR, "Kubernetes Service plugin is disabled");
|
logAndThrow(Level.ERROR, "Kubernetes Service plugin is disabled");
|
||||||
}
|
}
|
||||||
Long kubernetesClusterId = cmd.getId();
|
Long kubernetesClusterId = cmd.getId();
|
||||||
KubernetesClusterVO cluster = kubernetesClusterDao.findById(kubernetesClusterId);
|
final KubernetesClusterVO cluster = kubernetesClusterDao.findById(kubernetesClusterId);
|
||||||
if (cluster == null) {
|
if (cluster == null) {
|
||||||
throw new InvalidParameterValueException("Invalid cluster id specified");
|
throw new InvalidParameterValueException("Invalid cluster id specified");
|
||||||
}
|
}
|
||||||
accountManager.checkAccess(CallContext.current().getCallingAccount(), SecurityChecker.AccessType.OperateEntry, false, cluster);
|
accountManager.checkAccess(CallContext.current().getCallingAccount(), SecurityChecker.AccessType.OperateEntry, false, cluster);
|
||||||
if (cluster.getClusterType() == KubernetesCluster.ClusterType.CloudManaged) {
|
if (cluster.getClusterType() == KubernetesCluster.ClusterType.CloudManaged) {
|
||||||
KubernetesClusterDestroyWorker destroyWorker = new KubernetesClusterDestroyWorker(cluster, this);
|
return destroyKubernetesCluster(cluster);
|
||||||
destroyWorker = ComponentContext.inject(destroyWorker);
|
|
||||||
return destroyWorker.destroy();
|
|
||||||
} else {
|
} else {
|
||||||
boolean cleanup = cmd.getCleanup();
|
boolean cleanup = cmd.getCleanup();
|
||||||
boolean expunge = cmd.getExpunge();
|
boolean expunge = cmd.getExpunge();
|
||||||
@ -1484,13 +1621,14 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Transaction.execute(new TransactionCallback<Boolean>() {
|
return Transaction.execute((TransactionCallback<Boolean>) status -> {
|
||||||
@Override
|
kubernetesClusterDetailsDao.removeDetails(kubernetesClusterId);
|
||||||
public Boolean doInTransaction(TransactionStatus status) {
|
kubernetesClusterVmMapDao.removeByClusterId(kubernetesClusterId);
|
||||||
kubernetesClusterDetailsDao.removeDetails(kubernetesClusterId);
|
if (kubernetesClusterDao.remove(kubernetesClusterId)) {
|
||||||
kubernetesClusterVmMapDao.removeByClusterId(kubernetesClusterId);
|
deleteProjectKubernetesAccountIfNeeded(cluster);
|
||||||
return kubernetesClusterDao.remove(kubernetesClusterId);
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1753,6 +1891,66 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||||||
return responseList;
|
return responseList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void deleteProjectKubernetesAccount(Account projectAccount) {
|
||||||
|
CallContext.register(User.UID_SYSTEM, Account.ACCOUNT_ID_SYSTEM);
|
||||||
|
try {
|
||||||
|
Account serviceAccount = getProjectKubernetesAccount(projectAccount, false);
|
||||||
|
if (serviceAccount != null) {
|
||||||
|
accountManager.deleteAccount(accountDao.findById(serviceAccount.getId()), User.UID_SYSTEM,
|
||||||
|
accountService.getSystemAccount());
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
CallContext.unregister();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void deleteProjectKubernetesAccountIfNeeded(final KubernetesCluster kubernetesCluster) {
|
||||||
|
Account owner = accountService.getAccount(kubernetesCluster.getAccountId());
|
||||||
|
if (owner == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Account.Type.PROJECT.equals(owner.getType()) &&
|
||||||
|
kubernetesClusterDao.countNotForGCByAccount(owner.getAccountId()) == 0) {
|
||||||
|
deleteProjectKubernetesAccount(owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean destroyKubernetesCluster(KubernetesCluster kubernetesCluster, boolean deleteProjectAccount) {
|
||||||
|
KubernetesClusterDestroyWorker destroyWorker = new KubernetesClusterDestroyWorker(kubernetesCluster,
|
||||||
|
KubernetesClusterManagerImpl.this);
|
||||||
|
destroyWorker = ComponentContext.inject(destroyWorker);
|
||||||
|
boolean result = destroyWorker.destroy();
|
||||||
|
if (deleteProjectAccount) {
|
||||||
|
deleteProjectKubernetesAccountIfNeeded(kubernetesCluster);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean destroyKubernetesCluster(KubernetesCluster kubernetesCluster) {
|
||||||
|
return destroyKubernetesCluster(kubernetesCluster, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cleanupForAccount(Account account) {
|
||||||
|
List<KubernetesClusterVO> clusters = kubernetesClusterDao.listForCleanupByAccount(account.getId());
|
||||||
|
if (CollectionUtils.isEmpty(clusters)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
logger.debug(String.format("Cleaning up %d Kubernetes cluster for %s", clusters.size(), account));
|
||||||
|
for (KubernetesClusterVO cluster : clusters) {
|
||||||
|
try {
|
||||||
|
destroyKubernetesCluster(cluster, false);
|
||||||
|
} catch (CloudRuntimeException e) {
|
||||||
|
logger.warn(String.format("Failed to destroy Kubernetes cluster: %s during cleanup for %s",
|
||||||
|
cluster.getName(), account), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!Account.Type.PROJECT.equals(account.getType())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
deleteProjectKubernetesAccount(account);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Class<?>> getCommands() {
|
public List<Class<?>> getCommands() {
|
||||||
List<Class<?>> cmdList = new ArrayList<Class<?>>();
|
List<Class<?>> cmdList = new ArrayList<Class<?>>();
|
||||||
@ -1804,12 +2002,8 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||||||
logger.info("Running Kubernetes cluster garbage collector on Kubernetes cluster: {}", kubernetesCluster);
|
logger.info("Running Kubernetes cluster garbage collector on Kubernetes cluster: {}", kubernetesCluster);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
KubernetesClusterDestroyWorker destroyWorker = new KubernetesClusterDestroyWorker(kubernetesCluster, KubernetesClusterManagerImpl.this);
|
if (destroyKubernetesCluster(kubernetesCluster)) {
|
||||||
destroyWorker = ComponentContext.inject(destroyWorker);
|
logger.info("Garbage collection complete for Kubernetes cluster: {}", kubernetesCluster);
|
||||||
if (destroyWorker.destroy()) {
|
|
||||||
if (logger.isInfoEnabled()) {
|
|
||||||
logger.info("Garbage collection complete for Kubernetes cluster: {}", kubernetesCluster);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
logger.warn("Garbage collection failed for Kubernetes cluster : {}, it will be attempted to garbage collected in next run", kubernetesCluster);
|
logger.warn("Garbage collection failed for Kubernetes cluster : {}, it will be attempted to garbage collected in next run", kubernetesCluster);
|
||||||
}
|
}
|
||||||
@ -1932,9 +2126,7 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||||||
logger.info("Running Kubernetes cluster state scanner on Kubernetes cluster: {} for state: {}", kubernetesCluster, KubernetesCluster.State.Destroying.toString());
|
logger.info("Running Kubernetes cluster state scanner on Kubernetes cluster: {} for state: {}", kubernetesCluster, KubernetesCluster.State.Destroying.toString());
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
KubernetesClusterDestroyWorker destroyWorker = new KubernetesClusterDestroyWorker(kubernetesCluster, KubernetesClusterManagerImpl.this);
|
destroyKubernetesCluster(kubernetesCluster);
|
||||||
destroyWorker = ComponentContext.inject(destroyWorker);
|
|
||||||
destroyWorker.destroy();
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.warn("Failed to run Kubernetes cluster Destroying state scanner on Kubernetes cluster : {} status scanner", kubernetesCluster, e);
|
logger.warn("Failed to run Kubernetes cluster Destroying state scanner on Kubernetes cluster : {} status scanner", kubernetesCluster, e);
|
||||||
}
|
}
|
||||||
@ -1948,7 +2140,7 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||||||
}
|
}
|
||||||
|
|
||||||
// checks if Kubernetes cluster is in desired state
|
// checks if Kubernetes cluster is in desired state
|
||||||
boolean isClusterVMsInDesiredState(KubernetesCluster kubernetesCluster, VirtualMachine.State state) {
|
private boolean isClusterVMsInDesiredState(KubernetesCluster kubernetesCluster, VirtualMachine.State state) {
|
||||||
List<KubernetesClusterVmMapVO> clusterVMs = kubernetesClusterVmMapDao.listByClusterId(kubernetesCluster.getId());
|
List<KubernetesClusterVmMapVO> clusterVMs = kubernetesClusterVmMapDao.listByClusterId(kubernetesCluster.getId());
|
||||||
|
|
||||||
// check cluster is running at desired capacity include control nodes as well
|
// check cluster is running at desired capacity include control nodes as well
|
||||||
@ -1986,6 +2178,8 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||||||
createNetworkOfferingForKubernetes(DEFAULT_NSX_VPC_TIER_NETWORK_OFFERING_FOR_KUBERNETES_SERVICE_NAME,
|
createNetworkOfferingForKubernetes(DEFAULT_NSX_VPC_TIER_NETWORK_OFFERING_FOR_KUBERNETES_SERVICE_NAME,
|
||||||
DEFAULT_NSX_VPC_NETWORK_OFFERING_FOR_KUBERNETES_SERVICE_DISPLAY_TEXT , true, true);
|
DEFAULT_NSX_VPC_NETWORK_OFFERING_FOR_KUBERNETES_SERVICE_DISPLAY_TEXT , true, true);
|
||||||
|
|
||||||
|
getProjectKubernetesAccountRole();
|
||||||
|
|
||||||
_gcExecutor.scheduleWithFixedDelay(new KubernetesClusterGarbageCollector(), 300, 300, TimeUnit.SECONDS);
|
_gcExecutor.scheduleWithFixedDelay(new KubernetesClusterGarbageCollector(), 300, 300, TimeUnit.SECONDS);
|
||||||
_stateScanner.scheduleWithFixedDelay(new KubernetesClusterStatusScanner(), 300, 30, TimeUnit.SECONDS);
|
_stateScanner.scheduleWithFixedDelay(new KubernetesClusterStatusScanner(), 300, 30, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
|||||||
@ -16,6 +16,8 @@
|
|||||||
// under the License.
|
// under the License.
|
||||||
package com.cloud.kubernetes.cluster;
|
package com.cloud.kubernetes.cluster;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.cloudstack.api.command.user.kubernetes.cluster.AddVirtualMachinesToKubernetesClusterCmd;
|
import org.apache.cloudstack.api.command.user.kubernetes.cluster.AddVirtualMachinesToKubernetesClusterCmd;
|
||||||
import org.apache.cloudstack.api.command.user.kubernetes.cluster.CreateKubernetesClusterCmd;
|
import org.apache.cloudstack.api.command.user.kubernetes.cluster.CreateKubernetesClusterCmd;
|
||||||
import org.apache.cloudstack.api.command.user.kubernetes.cluster.DeleteKubernetesClusterCmd;
|
import org.apache.cloudstack.api.command.user.kubernetes.cluster.DeleteKubernetesClusterCmd;
|
||||||
@ -34,16 +36,16 @@ import org.apache.cloudstack.framework.config.ConfigKey;
|
|||||||
import org.apache.cloudstack.framework.config.Configurable;
|
import org.apache.cloudstack.framework.config.Configurable;
|
||||||
|
|
||||||
import com.cloud.network.Network;
|
import com.cloud.network.Network;
|
||||||
|
import com.cloud.user.Account;
|
||||||
import com.cloud.utils.component.PluggableService;
|
import com.cloud.utils.component.PluggableService;
|
||||||
import com.cloud.utils.exception.CloudRuntimeException;
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public interface KubernetesClusterService extends PluggableService, Configurable {
|
public interface KubernetesClusterService extends PluggableService, Configurable {
|
||||||
static final String MIN_KUBERNETES_VERSION_HA_SUPPORT = "1.16.0";
|
static final String MIN_KUBERNETES_VERSION_HA_SUPPORT = "1.16.0";
|
||||||
static final int MIN_KUBERNETES_CLUSTER_NODE_CPU = 2;
|
static final int MIN_KUBERNETES_CLUSTER_NODE_CPU = 2;
|
||||||
static final int MIN_KUBERNETES_CLUSTER_NODE_RAM_SIZE = 2048;
|
static final int MIN_KUBERNETES_CLUSTER_NODE_RAM_SIZE = 2048;
|
||||||
static final String KUBEADMIN_ACCOUNT_NAME = "kubeadmin";
|
static final String KUBEADMIN_ACCOUNT_NAME = "kubeadmin";
|
||||||
|
String PROJECT_KUBEADMIN_ACCOUNT_ROLE_NAME = "Project Kubernetes Service Role";
|
||||||
|
|
||||||
static final ConfigKey<Boolean> KubernetesServiceEnabled = new ConfigKey<Boolean>("Advanced", Boolean.class,
|
static final ConfigKey<Boolean> KubernetesServiceEnabled = new ConfigKey<Boolean>("Advanced", Boolean.class,
|
||||||
"cloud.kubernetes.service.enabled",
|
"cloud.kubernetes.service.enabled",
|
||||||
@ -125,4 +127,6 @@ public interface KubernetesClusterService extends PluggableService, Configurable
|
|||||||
List<RemoveVirtualMachinesFromKubernetesClusterResponse> removeVmsFromCluster(RemoveVirtualMachinesFromKubernetesClusterCmd cmd);
|
List<RemoveVirtualMachinesFromKubernetesClusterResponse> removeVmsFromCluster(RemoveVirtualMachinesFromKubernetesClusterCmd cmd);
|
||||||
|
|
||||||
boolean isDirectAccess(Network network);
|
boolean isDirectAccess(Network network);
|
||||||
|
|
||||||
|
void cleanupForAccount(Account account);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,6 +32,7 @@ import com.cloud.kubernetes.cluster.dao.KubernetesClusterDao;
|
|||||||
import com.cloud.kubernetes.cluster.dao.KubernetesClusterVmMapDao;
|
import com.cloud.kubernetes.cluster.dao.KubernetesClusterVmMapDao;
|
||||||
import com.cloud.kubernetes.version.KubernetesSupportedVersion;
|
import com.cloud.kubernetes.version.KubernetesSupportedVersion;
|
||||||
import com.cloud.kubernetes.version.KubernetesVersionEventTypes;
|
import com.cloud.kubernetes.version.KubernetesVersionEventTypes;
|
||||||
|
import com.cloud.user.Account;
|
||||||
import com.cloud.uservm.UserVm;
|
import com.cloud.uservm.UserVm;
|
||||||
import com.cloud.utils.component.AdapterBase;
|
import com.cloud.utils.component.AdapterBase;
|
||||||
import com.cloud.utils.exception.CloudRuntimeException;
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
@ -50,6 +51,8 @@ public class KubernetesServiceHelperImpl extends AdapterBase implements Kubernet
|
|||||||
private KubernetesClusterDao kubernetesClusterDao;
|
private KubernetesClusterDao kubernetesClusterDao;
|
||||||
@Inject
|
@Inject
|
||||||
private KubernetesClusterVmMapDao kubernetesClusterVmMapDao;
|
private KubernetesClusterVmMapDao kubernetesClusterVmMapDao;
|
||||||
|
@Inject
|
||||||
|
KubernetesClusterService kubernetesClusterService;
|
||||||
|
|
||||||
protected void setEventTypeEntityDetails(Class<?> eventTypeDefinedClass, Class<?> entityClass) {
|
protected void setEventTypeEntityDetails(Class<?> eventTypeDefinedClass, Class<?> entityClass) {
|
||||||
Field[] declaredFields = eventTypeDefinedClass.getDeclaredFields();
|
Field[] declaredFields = eventTypeDefinedClass.getDeclaredFields();
|
||||||
@ -106,6 +109,11 @@ public class KubernetesServiceHelperImpl extends AdapterBase implements Kubernet
|
|||||||
throw new CloudRuntimeException(msg);
|
throw new CloudRuntimeException(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cleanupForAccount(Account account) {
|
||||||
|
kubernetesClusterService.cleanupForAccount(account);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getConfigComponentName() {
|
public String getConfigComponentName() {
|
||||||
return KubernetesServiceHelper.class.getSimpleName();
|
return KubernetesServiceHelper.class.getSimpleName();
|
||||||
|
|||||||
@ -25,8 +25,8 @@ import com.cloud.utils.fsm.StateDao;
|
|||||||
|
|
||||||
public interface KubernetesClusterDao extends GenericDao<KubernetesClusterVO, Long>,
|
public interface KubernetesClusterDao extends GenericDao<KubernetesClusterVO, Long>,
|
||||||
StateDao<KubernetesCluster.State, KubernetesCluster.Event, KubernetesCluster> {
|
StateDao<KubernetesCluster.State, KubernetesCluster.Event, KubernetesCluster> {
|
||||||
|
List<KubernetesClusterVO> listForCleanupByAccount(long accountId);
|
||||||
List<KubernetesClusterVO> listByAccount(long accountId);
|
int countNotForGCByAccount(long accountId);
|
||||||
List<KubernetesClusterVO> findKubernetesClustersToGarbageCollect();
|
List<KubernetesClusterVO> findKubernetesClustersToGarbageCollect();
|
||||||
List<KubernetesClusterVO> findManagedKubernetesClustersInState(KubernetesCluster.State state);
|
List<KubernetesClusterVO> findManagedKubernetesClustersInState(KubernetesCluster.State state);
|
||||||
List<KubernetesClusterVO> listByNetworkId(long networkId);
|
List<KubernetesClusterVO> listByNetworkId(long networkId);
|
||||||
|
|||||||
@ -30,16 +30,25 @@ import com.cloud.utils.db.TransactionLegacy;
|
|||||||
@Component
|
@Component
|
||||||
public class KubernetesClusterDaoImpl extends GenericDaoBase<KubernetesClusterVO, Long> implements KubernetesClusterDao {
|
public class KubernetesClusterDaoImpl extends GenericDaoBase<KubernetesClusterVO, Long> implements KubernetesClusterDao {
|
||||||
|
|
||||||
private final SearchBuilder<KubernetesClusterVO> AccountIdSearch;
|
private final SearchBuilder<KubernetesClusterVO> CleanupAccountIdSearch;
|
||||||
|
private final SearchBuilder<KubernetesClusterVO> NotForGCByAccountIDCount;
|
||||||
private final SearchBuilder<KubernetesClusterVO> GarbageCollectedSearch;
|
private final SearchBuilder<KubernetesClusterVO> GarbageCollectedSearch;
|
||||||
private final SearchBuilder<KubernetesClusterVO> ManagedStateSearch;
|
private final SearchBuilder<KubernetesClusterVO> ManagedStateSearch;
|
||||||
private final SearchBuilder<KubernetesClusterVO> SameNetworkSearch;
|
private final SearchBuilder<KubernetesClusterVO> SameNetworkSearch;
|
||||||
private final SearchBuilder<KubernetesClusterVO> KubernetesVersionSearch;
|
private final SearchBuilder<KubernetesClusterVO> KubernetesVersionSearch;
|
||||||
|
|
||||||
public KubernetesClusterDaoImpl() {
|
public KubernetesClusterDaoImpl() {
|
||||||
AccountIdSearch = createSearchBuilder();
|
CleanupAccountIdSearch = createSearchBuilder();
|
||||||
AccountIdSearch.and("account", AccountIdSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
|
CleanupAccountIdSearch.and("account", CleanupAccountIdSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
|
||||||
AccountIdSearch.done();
|
CleanupAccountIdSearch.and("cluster_type", CleanupAccountIdSearch.entity().getClusterType(), SearchCriteria.Op.EQ);
|
||||||
|
CleanupAccountIdSearch.done();
|
||||||
|
|
||||||
|
NotForGCByAccountIDCount = createSearchBuilder();
|
||||||
|
NotForGCByAccountIDCount.and("gc", NotForGCByAccountIDCount.entity().isCheckForGc(), SearchCriteria.Op.EQ);
|
||||||
|
NotForGCByAccountIDCount.and("account", NotForGCByAccountIDCount.entity().getAccountId(), SearchCriteria.Op.EQ);
|
||||||
|
NotForGCByAccountIDCount.and("cluster_type", NotForGCByAccountIDCount.entity().getClusterType(), SearchCriteria.Op.EQ);
|
||||||
|
NotForGCByAccountIDCount.select(null, SearchCriteria.Func.COUNT, null);
|
||||||
|
NotForGCByAccountIDCount.done();
|
||||||
|
|
||||||
GarbageCollectedSearch = createSearchBuilder();
|
GarbageCollectedSearch = createSearchBuilder();
|
||||||
GarbageCollectedSearch.and("gc", GarbageCollectedSearch.entity().isCheckForGc(), SearchCriteria.Op.EQ);
|
GarbageCollectedSearch.and("gc", GarbageCollectedSearch.entity().isCheckForGc(), SearchCriteria.Op.EQ);
|
||||||
@ -62,10 +71,20 @@ public class KubernetesClusterDaoImpl extends GenericDaoBase<KubernetesClusterVO
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<KubernetesClusterVO> listByAccount(long accountId) {
|
public List<KubernetesClusterVO> listForCleanupByAccount(long accountId) {
|
||||||
SearchCriteria<KubernetesClusterVO> sc = AccountIdSearch.create();
|
SearchCriteria<KubernetesClusterVO> sc = CleanupAccountIdSearch.create();
|
||||||
|
sc.setParameters("cluster_type", KubernetesCluster.ClusterType.CloudManaged);
|
||||||
sc.setParameters("account", accountId);
|
sc.setParameters("account", accountId);
|
||||||
return listBy(sc, null);
|
return listBy(sc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int countNotForGCByAccount(long accountId) {
|
||||||
|
SearchCriteria<KubernetesClusterVO> sc = NotForGCByAccountIDCount.create();
|
||||||
|
sc.setParameters("cluster_type", KubernetesCluster.ClusterType.CloudManaged);
|
||||||
|
sc.setParameters("account", accountId);
|
||||||
|
sc.setParameters("gc", false);
|
||||||
|
return getCount(sc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -533,4 +533,8 @@ public class MockAccountManager extends ManagerBase implements AccountManager {
|
|||||||
public UserAccount clearUserTwoFactorAuthenticationInSetupStateOnLogin(UserAccount user) {
|
public UserAccount clearUserTwoFactorAuthenticationInSetupStateOnLogin(UserAccount user) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void verifyCallerPrivilegeForUserOrAccountOperations(Account userAccount) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -76,6 +76,9 @@ public class VxlanGuestNetworkGuru extends GuestNetworkGuru {
|
|||||||
network.setBroadcastUri(BroadcastDomainType.Vxlan.toUri(vxlan));
|
network.setBroadcastUri(BroadcastDomainType.Vxlan.toUri(vxlan));
|
||||||
}
|
}
|
||||||
network.setBroadcastDomainType(BroadcastDomainType.Vxlan);
|
network.setBroadcastDomainType(BroadcastDomainType.Vxlan);
|
||||||
|
|
||||||
|
getOrCreateIpv4SubnetForGuestNetwork(offering, network, userSpecified, owner);
|
||||||
|
|
||||||
return updateNetworkDesignForIPv6IfNeeded(network, userSpecified);
|
return updateNetworkDesignForIPv6IfNeeded(network, userSpecified);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3723,7 +3723,9 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
|||||||
|
|
||||||
SearchCriteria<DiskOfferingVO> sc = diskOfferingSearch.create();
|
SearchCriteria<DiskOfferingVO> sc = diskOfferingSearch.create();
|
||||||
sc.setParameters("computeOnly", false);
|
sc.setParameters("computeOnly", false);
|
||||||
sc.setParameters("activeState", DiskOffering.State.Active);
|
if (state != null) {
|
||||||
|
sc.setParameters("state", state);
|
||||||
|
}
|
||||||
|
|
||||||
sc.setJoinParameters("domainDetailsSearch", "domainId", domainId);
|
sc.setJoinParameters("domainDetailsSearch", "domainId", domainId);
|
||||||
|
|
||||||
@ -4870,7 +4872,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
|||||||
if (!permittedAccounts.isEmpty()) {
|
if (!permittedAccounts.isEmpty()) {
|
||||||
domain = _domainDao.findById(permittedAccounts.get(0).getDomainId());
|
domain = _domainDao.findById(permittedAccounts.get(0).getDomainId());
|
||||||
} else {
|
} else {
|
||||||
domain = _domainDao.findById(Domain.ROOT_DOMAIN);
|
domain = _domainDao.findById(caller.getDomainId());
|
||||||
}
|
}
|
||||||
|
|
||||||
setIdsListToSearchCriteria(sc, ids);
|
setIdsListToSearchCriteria(sc, ids);
|
||||||
|
|||||||
@ -50,7 +50,7 @@ import java.util.stream.Collectors;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.naming.ConfigurationException;
|
import javax.naming.ConfigurationException;
|
||||||
|
|
||||||
import com.cloud.resource.ResourceManager;
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
import org.apache.cloudstack.acl.SecurityChecker;
|
import org.apache.cloudstack.acl.SecurityChecker;
|
||||||
import org.apache.cloudstack.affinity.AffinityGroup;
|
import org.apache.cloudstack.affinity.AffinityGroup;
|
||||||
import org.apache.cloudstack.affinity.AffinityGroupService;
|
import org.apache.cloudstack.affinity.AffinityGroupService;
|
||||||
@ -254,6 +254,7 @@ import com.cloud.org.Grouping;
|
|||||||
import com.cloud.org.Grouping.AllocationState;
|
import com.cloud.org.Grouping.AllocationState;
|
||||||
import com.cloud.projects.Project;
|
import com.cloud.projects.Project;
|
||||||
import com.cloud.projects.ProjectManager;
|
import com.cloud.projects.ProjectManager;
|
||||||
|
import com.cloud.resource.ResourceManager;
|
||||||
import com.cloud.server.ConfigurationServer;
|
import com.cloud.server.ConfigurationServer;
|
||||||
import com.cloud.server.ManagementService;
|
import com.cloud.server.ManagementService;
|
||||||
import com.cloud.service.ServiceOfferingDetailsVO;
|
import com.cloud.service.ServiceOfferingDetailsVO;
|
||||||
@ -277,6 +278,7 @@ import com.cloud.user.Account;
|
|||||||
import com.cloud.user.AccountDetailVO;
|
import com.cloud.user.AccountDetailVO;
|
||||||
import com.cloud.user.AccountDetailsDao;
|
import com.cloud.user.AccountDetailsDao;
|
||||||
import com.cloud.user.AccountManager;
|
import com.cloud.user.AccountManager;
|
||||||
|
import com.cloud.user.AccountManagerImpl;
|
||||||
import com.cloud.user.AccountVO;
|
import com.cloud.user.AccountVO;
|
||||||
import com.cloud.user.ResourceLimitService;
|
import com.cloud.user.ResourceLimitService;
|
||||||
import com.cloud.user.User;
|
import com.cloud.user.User;
|
||||||
@ -484,6 +486,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||||||
private long _defaultPageSize = Long.parseLong(Config.DefaultPageSize.getDefaultValue());
|
private long _defaultPageSize = Long.parseLong(Config.DefaultPageSize.getDefaultValue());
|
||||||
private static final String DOMAIN_NAME_PATTERN = "^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\\.)+[A-Za-z]{1,63}$";
|
private static final String DOMAIN_NAME_PATTERN = "^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\\.)+[A-Za-z]{1,63}$";
|
||||||
private Set<String> configValuesForValidation = new HashSet<>();
|
private Set<String> configValuesForValidation = new HashSet<>();
|
||||||
|
private Set<String> configKeysAllowedOnlyForDefaultAdmin = new HashSet<>();
|
||||||
private Set<String> weightBasedParametersForValidation = new HashSet<>();
|
private Set<String> weightBasedParametersForValidation = new HashSet<>();
|
||||||
private Set<String> overprovisioningFactorsForValidation = new HashSet<>();
|
private Set<String> overprovisioningFactorsForValidation = new HashSet<>();
|
||||||
|
|
||||||
@ -548,6 +551,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||||||
populateConfigValuesForValidationSet();
|
populateConfigValuesForValidationSet();
|
||||||
weightBasedParametersForValidation();
|
weightBasedParametersForValidation();
|
||||||
overProvisioningFactorsForValidation();
|
overProvisioningFactorsForValidation();
|
||||||
|
populateConfigKeysAllowedOnlyForDefaultAdmin();
|
||||||
initMessageBusListener();
|
initMessageBusListener();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -612,6 +616,11 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||||||
overprovisioningFactorsForValidation.add(CapacityManager.StorageOverprovisioningFactor.key());
|
overprovisioningFactorsForValidation.add(CapacityManager.StorageOverprovisioningFactor.key());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void populateConfigKeysAllowedOnlyForDefaultAdmin() {
|
||||||
|
configKeysAllowedOnlyForDefaultAdmin.add(AccountManagerImpl.listOfRoleTypesAllowedForOperationsOfSameRoleType.key());
|
||||||
|
configKeysAllowedOnlyForDefaultAdmin.add(AccountManagerImpl.allowOperationsOnUsersInSameAccount.key());
|
||||||
|
}
|
||||||
|
|
||||||
private void initMessageBusListener() {
|
private void initMessageBusListener() {
|
||||||
messageBus.subscribe(EventTypes.EVENT_CONFIGURATION_VALUE_EDIT, new MessageSubscriber() {
|
messageBus.subscribe(EventTypes.EVENT_CONFIGURATION_VALUE_EDIT, new MessageSubscriber() {
|
||||||
@Override
|
@Override
|
||||||
@ -1227,6 +1236,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||||||
logger.error("Missing configuration variable " + name + " in configuration table");
|
logger.error("Missing configuration variable " + name + " in configuration table");
|
||||||
return "Invalid configuration variable.";
|
return "Invalid configuration variable.";
|
||||||
}
|
}
|
||||||
|
validateConfigurationAllowedOnlyForDefaultAdmin(name, value);
|
||||||
|
|
||||||
List<ConfigKey.Scope> configScope = cfg.getScopes();
|
List<ConfigKey.Scope> configScope = cfg.getScopes();
|
||||||
if (scope != null) {
|
if (scope != null) {
|
||||||
@ -1262,6 +1272,33 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||||||
return validateValueRange(name, value, type, configuration);
|
return validateValueRange(name, value, type, configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void validateConfigurationAllowedOnlyForDefaultAdmin(String configName, String value) {
|
||||||
|
if (configKeysAllowedOnlyForDefaultAdmin.contains(configName)) {
|
||||||
|
final Long userId = CallContext.current().getCallingUserId();
|
||||||
|
if (userId != User.UID_ADMIN) {
|
||||||
|
throw new CloudRuntimeException("Only default admin is allowed to change this setting");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AccountManagerImpl.listOfRoleTypesAllowedForOperationsOfSameRoleType.key().equals(configName)) {
|
||||||
|
if (value != null && !value.isBlank()) {
|
||||||
|
List<String> validRoleTypes = Arrays.stream(RoleType.values())
|
||||||
|
.map(Enum::name)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
boolean allValid = Arrays.stream(value.split(","))
|
||||||
|
.map(String::trim)
|
||||||
|
.allMatch(validRoleTypes::contains);
|
||||||
|
|
||||||
|
if (!allValid) {
|
||||||
|
throw new CloudRuntimeException("Invalid role types provided in value");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new CloudRuntimeException("Value for role types must not be empty");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether a value is valid for a configuration of the provided type.
|
* Returns whether a value is valid for a configuration of the provided type.
|
||||||
* Valid configuration values are:
|
* Valid configuration values are:
|
||||||
|
|||||||
@ -24,8 +24,6 @@ import javax.inject.Inject;
|
|||||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||||
import org.apache.cloudstack.context.CallContext;
|
import org.apache.cloudstack.context.CallContext;
|
||||||
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
||||||
import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMap;
|
|
||||||
import org.apache.cloudstack.network.RoutedIpv4Manager;
|
|
||||||
|
|
||||||
import com.cloud.dc.DataCenter;
|
import com.cloud.dc.DataCenter;
|
||||||
import com.cloud.dc.DataCenter.NetworkType;
|
import com.cloud.dc.DataCenter.NetworkType;
|
||||||
@ -37,7 +35,6 @@ import com.cloud.event.EventTypes;
|
|||||||
import com.cloud.event.EventVO;
|
import com.cloud.event.EventVO;
|
||||||
import com.cloud.exception.InsufficientAddressCapacityException;
|
import com.cloud.exception.InsufficientAddressCapacityException;
|
||||||
import com.cloud.exception.InsufficientVirtualNetworkCapacityException;
|
import com.cloud.exception.InsufficientVirtualNetworkCapacityException;
|
||||||
import com.cloud.exception.InvalidParameterValueException;
|
|
||||||
import com.cloud.network.IpAddressManager;
|
import com.cloud.network.IpAddressManager;
|
||||||
import com.cloud.network.Network;
|
import com.cloud.network.Network;
|
||||||
import com.cloud.network.Network.GuestType;
|
import com.cloud.network.Network.GuestType;
|
||||||
@ -90,8 +87,6 @@ public class ExternalGuestNetworkGuru extends GuestNetworkGuru {
|
|||||||
FirewallRulesDao _fwRulesDao;
|
FirewallRulesDao _fwRulesDao;
|
||||||
@Inject
|
@Inject
|
||||||
FirewallRulesCidrsDao _fwRulesCidrDao;
|
FirewallRulesCidrsDao _fwRulesCidrDao;
|
||||||
@Inject
|
|
||||||
RoutedIpv4Manager routedIpv4Manager;
|
|
||||||
|
|
||||||
public ExternalGuestNetworkGuru() {
|
public ExternalGuestNetworkGuru() {
|
||||||
super();
|
super();
|
||||||
@ -126,23 +121,9 @@ public class ExternalGuestNetworkGuru extends GuestNetworkGuru {
|
|||||||
/* In order to revert userSpecified network setup */
|
/* In order to revert userSpecified network setup */
|
||||||
config.setState(State.Allocated);
|
config.setState(State.Allocated);
|
||||||
}
|
}
|
||||||
if (NetworkOffering.NetworkMode.ROUTED.equals(offering.getNetworkMode()) && !offering.isForVpc()) {
|
|
||||||
if (userSpecified.getCidr() != null) {
|
getOrCreateIpv4SubnetForGuestNetwork(offering, config, userSpecified, owner);
|
||||||
routedIpv4Manager.getOrCreateIpv4SubnetForGuestNetwork(config, userSpecified.getCidr());
|
|
||||||
} else {
|
|
||||||
if (userSpecified.getNetworkCidrSize() == null) {
|
|
||||||
throw new InvalidParameterValueException("The network CIDR or CIDR size must be specified.");
|
|
||||||
}
|
|
||||||
Ipv4GuestSubnetNetworkMap subnet = routedIpv4Manager.getOrCreateIpv4SubnetForGuestNetwork(owner.getDomainId(), owner.getAccountId(), config.getDataCenterId(), userSpecified.getNetworkCidrSize());
|
|
||||||
if (subnet != null) {
|
|
||||||
final String[] cidrTuple = subnet.getSubnet().split("\\/");
|
|
||||||
config.setGateway(NetUtils.getIpRangeStartIpFromCidr(cidrTuple[0], Long.parseLong(cidrTuple[1])));
|
|
||||||
config.setCidr(subnet.getSubnet());
|
|
||||||
} else {
|
|
||||||
throw new InvalidParameterValueException("Failed to allocate a CIDR with requested size.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return updateNetworkDesignForIPv6IfNeeded(config, userSpecified);
|
return updateNetworkDesignForIPv6IfNeeded(config, userSpecified);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -28,6 +28,8 @@ import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationSe
|
|||||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||||
import org.apache.cloudstack.framework.config.Configurable;
|
import org.apache.cloudstack.framework.config.Configurable;
|
||||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||||
|
import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMap;
|
||||||
|
import org.apache.cloudstack.network.RoutedIpv4Manager;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import com.cloud.configuration.Config;
|
import com.cloud.configuration.Config;
|
||||||
@ -121,6 +123,8 @@ public abstract class GuestNetworkGuru extends AdapterBase implements NetworkGur
|
|||||||
Ipv6AddressManager ipv6AddressManager;
|
Ipv6AddressManager ipv6AddressManager;
|
||||||
@Inject
|
@Inject
|
||||||
DomainRouterDao domainRouterDao;
|
DomainRouterDao domainRouterDao;
|
||||||
|
@Inject
|
||||||
|
RoutedIpv4Manager routedIpv4Manager;
|
||||||
|
|
||||||
Random _rand = new Random(System.currentTimeMillis());
|
Random _rand = new Random(System.currentTimeMillis());
|
||||||
|
|
||||||
@ -571,4 +575,24 @@ public abstract class GuestNetworkGuru extends AdapterBase implements NetworkGur
|
|||||||
}
|
}
|
||||||
return network;
|
return network;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void getOrCreateIpv4SubnetForGuestNetwork(NetworkOffering offering, NetworkVO config, Network userSpecified, Account owner) {
|
||||||
|
if (NetworkOffering.NetworkMode.ROUTED.equals(offering.getNetworkMode()) && !offering.isForVpc()) {
|
||||||
|
if (userSpecified.getCidr() != null) {
|
||||||
|
routedIpv4Manager.getOrCreateIpv4SubnetForGuestNetwork(config, userSpecified.getCidr());
|
||||||
|
} else {
|
||||||
|
if (userSpecified.getNetworkCidrSize() == null) {
|
||||||
|
throw new InvalidParameterValueException("The network CIDR or CIDR size must be specified.");
|
||||||
|
}
|
||||||
|
Ipv4GuestSubnetNetworkMap subnet = routedIpv4Manager.getOrCreateIpv4SubnetForGuestNetwork(owner.getDomainId(), owner.getAccountId(), config.getDataCenterId(), userSpecified.getNetworkCidrSize());
|
||||||
|
if (subnet != null) {
|
||||||
|
final String[] cidrTuple = subnet.getSubnet().split("\\/");
|
||||||
|
config.setGateway(NetUtils.getIpRangeStartIpFromCidr(cidrTuple[0], Long.parseLong(cidrTuple[1])));
|
||||||
|
config.setCidr(subnet.getSubnet());
|
||||||
|
} else {
|
||||||
|
throw new InvalidParameterValueException("Failed to allocate a CIDR with requested size.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -964,6 +964,7 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
|
|||||||
} else {
|
} else {
|
||||||
_accountMgr.checkAccess(caller, null, true, account);
|
_accountMgr.checkAccess(caller, null, true, account);
|
||||||
}
|
}
|
||||||
|
_accountMgr.verifyCallerPrivilegeForUserOrAccountOperations(account);
|
||||||
|
|
||||||
ownerType = ResourceOwnerType.Account;
|
ownerType = ResourceOwnerType.Account;
|
||||||
ownerId = accountId;
|
ownerId = accountId;
|
||||||
@ -1133,6 +1134,11 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
|
|||||||
throw new InvalidParameterValueException("Please specify a valid domain ID.");
|
throw new InvalidParameterValueException("Please specify a valid domain ID.");
|
||||||
}
|
}
|
||||||
_accountMgr.checkAccess(callerAccount, domain);
|
_accountMgr.checkAccess(callerAccount, domain);
|
||||||
|
Account account = _entityMgr.findById(Account.class, accountId);
|
||||||
|
if (account == null) {
|
||||||
|
throw new InvalidParameterValueException("Unable to find account " + accountId);
|
||||||
|
}
|
||||||
|
_accountMgr.verifyCallerPrivilegeForUserOrAccountOperations(account);
|
||||||
|
|
||||||
if (resourceType != null) {
|
if (resourceType != null) {
|
||||||
resourceTypes.add(resourceType);
|
resourceTypes.add(resourceType);
|
||||||
|
|||||||
@ -206,4 +206,5 @@ public interface AccountManager extends AccountService, Configurable {
|
|||||||
|
|
||||||
UserAccount clearUserTwoFactorAuthenticationInSetupStateOnLogin(UserAccount user);
|
UserAccount clearUserTwoFactorAuthenticationInSetupStateOnLogin(UserAccount user);
|
||||||
|
|
||||||
|
void verifyCallerPrivilegeForUserOrAccountOperations(Account userAccount);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import java.net.InetAddress;
|
|||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -120,6 +121,7 @@ import com.cloud.exception.OperationTimedoutException;
|
|||||||
import com.cloud.exception.PermissionDeniedException;
|
import com.cloud.exception.PermissionDeniedException;
|
||||||
import com.cloud.exception.ResourceUnavailableException;
|
import com.cloud.exception.ResourceUnavailableException;
|
||||||
import com.cloud.host.dao.HostDao;
|
import com.cloud.host.dao.HostDao;
|
||||||
|
import com.cloud.kubernetes.cluster.KubernetesServiceHelper;
|
||||||
import com.cloud.network.IpAddress;
|
import com.cloud.network.IpAddress;
|
||||||
import com.cloud.network.IpAddressManager;
|
import com.cloud.network.IpAddressManager;
|
||||||
import com.cloud.network.Network;
|
import com.cloud.network.Network;
|
||||||
@ -350,6 +352,9 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
|
|
||||||
private List<UserTwoFactorAuthenticator> userTwoFactorAuthenticationProviders;
|
private List<UserTwoFactorAuthenticator> userTwoFactorAuthenticationProviders;
|
||||||
|
|
||||||
|
private long validUserLastAuthTimeDurationInMs = 0L;
|
||||||
|
private static final long DEFAULT_USER_AUTH_TIME_DURATION_MS = 350L;
|
||||||
|
|
||||||
public static ConfigKey<Boolean> enableUserTwoFactorAuthentication = new ConfigKey<>("Advanced",
|
public static ConfigKey<Boolean> enableUserTwoFactorAuthentication = new ConfigKey<>("Advanced",
|
||||||
Boolean.class,
|
Boolean.class,
|
||||||
"enable.user.2fa",
|
"enable.user.2fa",
|
||||||
@ -394,6 +399,22 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
true,
|
true,
|
||||||
ConfigKey.Scope.Domain);
|
ConfigKey.Scope.Domain);
|
||||||
|
|
||||||
|
public static ConfigKey<String> listOfRoleTypesAllowedForOperationsOfSameRoleType = new ConfigKey<>("Hidden",
|
||||||
|
String.class,
|
||||||
|
"role.types.allowed.for.operations.on.accounts.of.same.role.type",
|
||||||
|
"Admin, ResourceAdmin, DomainAdmin",
|
||||||
|
"Comma separated list of role types that are allowed to do operations on accounts or users of the same role type within a domain.",
|
||||||
|
true,
|
||||||
|
ConfigKey.Scope.Domain);
|
||||||
|
|
||||||
|
public static ConfigKey<Boolean> allowOperationsOnUsersInSameAccount = new ConfigKey<>("Hidden",
|
||||||
|
Boolean.class,
|
||||||
|
"allow.operations.on.users.in.same.account",
|
||||||
|
"true",
|
||||||
|
"Allow operations on users among them in the same account",
|
||||||
|
true,
|
||||||
|
ConfigKey.Scope.Domain);
|
||||||
|
|
||||||
protected AccountManagerImpl() {
|
protected AccountManagerImpl() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@ -884,6 +905,16 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
return cleanupAccount(account, callerUserId, caller);
|
return cleanupAccount(account, callerUserId, caller);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void cleanupPluginsResourcesIfNeeded(Account account) {
|
||||||
|
try {
|
||||||
|
KubernetesServiceHelper kubernetesServiceHelper =
|
||||||
|
ComponentContext.getDelegateComponentOfType(KubernetesServiceHelper.class);
|
||||||
|
kubernetesServiceHelper.cleanupForAccount(account);
|
||||||
|
} catch (NoSuchBeanDefinitionException ignored) {
|
||||||
|
logger.debug("No KubernetesServiceHelper bean found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected boolean cleanupAccount(AccountVO account, long callerUserId, Account caller) {
|
protected boolean cleanupAccount(AccountVO account, long callerUserId, Account caller) {
|
||||||
long accountId = account.getId();
|
long accountId = account.getId();
|
||||||
boolean accountCleanupNeeded = false;
|
boolean accountCleanupNeeded = false;
|
||||||
@ -965,6 +996,8 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cleanupPluginsResourcesIfNeeded(account);
|
||||||
|
|
||||||
// Destroy the account's VMs
|
// Destroy the account's VMs
|
||||||
List<UserVmVO> vms = _userVmDao.listByAccountId(accountId);
|
List<UserVmVO> vms = _userVmDao.listByAccountId(accountId);
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
@ -1388,7 +1421,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
/**
|
/**
|
||||||
* if there is any permission under the requested role that is not permitted for the caller, refuse
|
* if there is any permission under the requested role that is not permitted for the caller, refuse
|
||||||
*/
|
*/
|
||||||
private void checkRoleEscalation(Account caller, Account requested) {
|
protected void checkRoleEscalation(Account caller, Account requested) {
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug(String.format("Checking if user of account %s [%s] with role-id [%d] can create an account of type %s [%s] with role-id [%d]",
|
logger.debug(String.format("Checking if user of account %s [%s] with role-id [%d] can create an account of type %s [%s] with role-id [%d]",
|
||||||
caller.getAccountName(),
|
caller.getAccountName(),
|
||||||
@ -1494,6 +1527,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
assertUserNotAlreadyInAccount(duplicatedUser, account);
|
assertUserNotAlreadyInAccount(duplicatedUser, account);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
verifyCallerPrivilegeForUserOrAccountOperations(account);
|
||||||
UserVO user;
|
UserVO user;
|
||||||
user = createUser(account.getId(), userName, password, firstName, lastName, email, timeZone, userUUID, source);
|
user = createUser(account.getId(), userName, password, firstName, lastName, email, timeZone, userUUID, source);
|
||||||
return user;
|
return user;
|
||||||
@ -1510,11 +1544,15 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
@ActionEvent(eventType = EventTypes.EVENT_USER_UPDATE, eventDescription = "Updating User")
|
@ActionEvent(eventType = EventTypes.EVENT_USER_UPDATE, eventDescription = "Updating User")
|
||||||
public UserAccount updateUser(UpdateUserCmd updateUserCmd) {
|
public UserAccount updateUser(UpdateUserCmd updateUserCmd) {
|
||||||
UserVO user = retrieveAndValidateUser(updateUserCmd);
|
UserVO user = retrieveAndValidateUser(updateUserCmd);
|
||||||
|
Account account = retrieveAndValidateAccount(user);
|
||||||
|
User caller = CallContext.current().getCallingUser();
|
||||||
|
checkAccess(caller, account);
|
||||||
|
verifyCallerPrivilegeForUserOrAccountOperations(user);
|
||||||
|
|
||||||
logger.debug("Updating user {}", user);
|
logger.debug("Updating user {}", user);
|
||||||
|
|
||||||
validateAndUpdateApiAndSecretKeyIfNeeded(updateUserCmd, user);
|
validateAndUpdateApiAndSecretKeyIfNeeded(updateUserCmd, user);
|
||||||
validateAndUpdateUserApiKeyAccess(updateUserCmd, user);
|
validateAndUpdateUserApiKeyAccess(updateUserCmd, user);
|
||||||
Account account = retrieveAndValidateAccount(user);
|
|
||||||
|
|
||||||
validateAndUpdateFirstNameIfNeeded(updateUserCmd, user);
|
validateAndUpdateFirstNameIfNeeded(updateUserCmd, user);
|
||||||
validateAndUpdateLastNameIfNeeded(updateUserCmd, user);
|
validateAndUpdateLastNameIfNeeded(updateUserCmd, user);
|
||||||
@ -1537,6 +1575,85 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
return _userAccountDao.findById(user.getId());
|
return _userAccountDao.findById(user.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void verifyCallerPrivilegeForUserOrAccountOperations(Account userAccount) {
|
||||||
|
logger.debug(String.format("Verifying whether the caller has the correct privileges based on the user's role type and API permissions: %s", userAccount));
|
||||||
|
|
||||||
|
checkCallerRoleTypeAllowedForUserOrAccountOperations(userAccount, null);
|
||||||
|
checkCallerApiPermissionsForUserOrAccountOperations(userAccount);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void verifyCallerPrivilegeForUserOrAccountOperations(User user) {
|
||||||
|
logger.debug(String.format("Verifying whether the caller has the correct privileges based on the user's role type and API permissions: %s", user));
|
||||||
|
|
||||||
|
Account userAccount = getAccount(user.getAccountId());
|
||||||
|
checkCallerRoleTypeAllowedForUserOrAccountOperations(userAccount, user);
|
||||||
|
checkCallerApiPermissionsForUserOrAccountOperations(userAccount);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void checkCallerRoleTypeAllowedForUserOrAccountOperations(Account userAccount, User user) {
|
||||||
|
Account callingAccount = getCurrentCallingAccount();
|
||||||
|
RoleType callerRoleType = getRoleType(callingAccount);
|
||||||
|
RoleType userAccountRoleType = getRoleType(userAccount);
|
||||||
|
|
||||||
|
if (RoleType.Unknown == callerRoleType || RoleType.Unknown == userAccountRoleType) {
|
||||||
|
String errMsg = String.format("The role type of account [%s, %s] or [%s, %s] is unknown",
|
||||||
|
callingAccount.getName(), callingAccount.getUuid(), userAccount.getName(), userAccount.getUuid());
|
||||||
|
throw new PermissionDeniedException(errMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isCallerSystemOrDefaultAdmin = callingAccount.getId() == Account.ACCOUNT_ID_SYSTEM || callingAccount.getId() == Account.ACCOUNT_ID_ADMIN;
|
||||||
|
if (isCallerSystemOrDefaultAdmin) {
|
||||||
|
logger.trace(String.format("Admin account [%s, %s] performing this operation for user account [%s, %s] ", callingAccount.getName(), callingAccount.getUuid(), userAccount.getName(), userAccount.getUuid()));
|
||||||
|
} else if (callerRoleType.getId() < userAccountRoleType.getId()) {
|
||||||
|
logger.trace(String.format("The calling account [%s, %s] has a higher role type than the user account [%s, %s]",
|
||||||
|
callingAccount.getName(), callingAccount.getUuid(), userAccount.getName(), userAccount.getUuid()));
|
||||||
|
} else if (callerRoleType.getId() == userAccountRoleType.getId()) {
|
||||||
|
if (callingAccount.getId() != userAccount.getId()) {
|
||||||
|
String allowedRoleTypes = listOfRoleTypesAllowedForOperationsOfSameRoleType.valueInScope(ConfigKey.Scope.Domain, callingAccount.getDomainId());
|
||||||
|
boolean updateAllowed = allowedRoleTypes != null &&
|
||||||
|
Arrays.stream(allowedRoleTypes.split(","))
|
||||||
|
.map(String::trim)
|
||||||
|
.anyMatch(role -> role.equals(callerRoleType.toString()));
|
||||||
|
if (BooleanUtils.isFalse(updateAllowed)) {
|
||||||
|
String errMsg = String.format("The calling account [%s, %s] is not allowed to perform this operation on users from other accounts " +
|
||||||
|
"of the same role type within the domain", callingAccount.getName(), callingAccount.getUuid());
|
||||||
|
logger.error(errMsg);
|
||||||
|
throw new PermissionDeniedException(errMsg);
|
||||||
|
}
|
||||||
|
} else if ((callingAccount.getId() == userAccount.getId()) && user != null) {
|
||||||
|
Boolean allowOperationOnUsersinSameAccount = allowOperationsOnUsersInSameAccount.valueInScope(ConfigKey.Scope.Domain, callingAccount.getDomainId());
|
||||||
|
User callingUser = CallContext.current().getCallingUser();
|
||||||
|
if (callingUser.getId() != user.getId() && BooleanUtils.isFalse(allowOperationOnUsersinSameAccount)) {
|
||||||
|
String errMsg = "The user operations are not allowed by the users in the same account";
|
||||||
|
logger.error(errMsg);
|
||||||
|
throw new PermissionDeniedException(errMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String errMsg = String.format("The calling account [%s, %s] has a lower role type than the user account [%s, %s]",
|
||||||
|
callingAccount.getName(), callingAccount.getUuid(), userAccount.getName(), userAccount.getUuid());
|
||||||
|
throw new PermissionDeniedException(errMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void checkCallerApiPermissionsForUserOrAccountOperations(Account userAccount) {
|
||||||
|
Account callingAccount = getCurrentCallingAccount();
|
||||||
|
boolean isCallerRootAdmin = callingAccount.getId() == Account.ACCOUNT_ID_SYSTEM || isRootAdmin(callingAccount.getId());
|
||||||
|
|
||||||
|
if (isCallerRootAdmin) {
|
||||||
|
logger.trace(String.format("Admin account [%s, %s] performing this operation for user account [%s, %s] ", callingAccount.getName(), callingAccount.getUuid(), userAccount.getName(), userAccount.getUuid()));
|
||||||
|
} else if (isRootAdmin(userAccount.getAccountId())) {
|
||||||
|
String errMsg = String.format("Account [%s, %s] cannot perform this operation for user account [%s, %s] ", callingAccount.getName(), callingAccount.getUuid(), userAccount.getName(), userAccount.getUuid());
|
||||||
|
logger.error(errMsg);
|
||||||
|
throw new PermissionDeniedException(errMsg);
|
||||||
|
} else {
|
||||||
|
logger.debug(String.format("Checking calling account [%s, %s] permission to perform this operation for user account [%s, %s] ", callingAccount.getName(), callingAccount.getUuid(), userAccount.getName(), userAccount.getUuid()));
|
||||||
|
checkRoleEscalation(callingAccount, userAccount);
|
||||||
|
logger.debug(String.format("Calling account [%s, %s] is allowed to perform this operation for user account [%s, %s] ", callingAccount.getName(), callingAccount.getUuid(), userAccount.getName(), userAccount.getUuid()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the password in the user POJO if needed. If no password is provided, then the password is not updated.
|
* Updates the password in the user POJO if needed. If no password is provided, then the password is not updated.
|
||||||
* The following validations are executed if 'password' is not null. Admins (root admins or domain admins) can execute password updates without entering the current password.
|
* The following validations are executed if 'password' is not null. Admins (root admins or domain admins) can execute password updates without entering the current password.
|
||||||
@ -1599,7 +1716,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
for (UserAuthenticator userAuthenticator : _userPasswordEncoders) {
|
for (UserAuthenticator userAuthenticator : _userPasswordEncoders) {
|
||||||
Pair<Boolean, ActionOnFailedAuthentication> authenticationResult = userAuthenticator.authenticate(user.getUsername(), currentPassword, userAccount.getDomainId(), null);
|
Pair<Boolean, ActionOnFailedAuthentication> authenticationResult = userAuthenticator.authenticate(user.getUsername(), currentPassword, userAccount.getDomainId(), null);
|
||||||
if (authenticationResult == null) {
|
if (authenticationResult == null) {
|
||||||
logger.trace(String.format("Authenticator [%s] is returning null for the authenticate mehtod.", userAuthenticator.getClass()));
|
logger.trace(String.format("Authenticator [%s] is returning null for the authenticate method.", userAuthenticator.getClass()));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (BooleanUtils.toBoolean(authenticationResult.first())) {
|
if (BooleanUtils.toBoolean(authenticationResult.first())) {
|
||||||
@ -1816,6 +1933,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
}
|
}
|
||||||
|
|
||||||
checkAccess(caller, AccessType.OperateEntry, true, account);
|
checkAccess(caller, AccessType.OperateEntry, true, account);
|
||||||
|
verifyCallerPrivilegeForUserOrAccountOperations(user);
|
||||||
|
|
||||||
boolean success = doSetUserStatus(userId, State.DISABLED);
|
boolean success = doSetUserStatus(userId, State.DISABLED);
|
||||||
if (success) {
|
if (success) {
|
||||||
@ -1857,6 +1975,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
}
|
}
|
||||||
|
|
||||||
checkAccess(caller, AccessType.OperateEntry, true, account);
|
checkAccess(caller, AccessType.OperateEntry, true, account);
|
||||||
|
verifyCallerPrivilegeForUserOrAccountOperations(user);
|
||||||
|
|
||||||
boolean success = Transaction.execute(new TransactionCallback<>() {
|
boolean success = Transaction.execute(new TransactionCallback<>() {
|
||||||
@Override
|
@Override
|
||||||
@ -1909,6 +2028,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
}
|
}
|
||||||
|
|
||||||
checkAccess(caller, AccessType.OperateEntry, true, account);
|
checkAccess(caller, AccessType.OperateEntry, true, account);
|
||||||
|
verifyCallerPrivilegeForUserOrAccountOperations(user);
|
||||||
|
|
||||||
// make sure the account is enabled too
|
// make sure the account is enabled too
|
||||||
// if the user is either locked already or disabled already, don't change state...only lock currently enabled
|
// if the user is either locked already or disabled already, don't change state...only lock currently enabled
|
||||||
@ -1972,6 +2092,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
}
|
}
|
||||||
|
|
||||||
checkIfAccountManagesProjects(accountId);
|
checkIfAccountManagesProjects(accountId);
|
||||||
|
verifyCallerPrivilegeForUserOrAccountOperations(account);
|
||||||
|
|
||||||
CallContext.current().putContextParameter(Account.class, account.getUuid());
|
CallContext.current().putContextParameter(Account.class, account.getUuid());
|
||||||
|
|
||||||
@ -2034,6 +2155,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
// Check if user performing the action is allowed to modify this account
|
// Check if user performing the action is allowed to modify this account
|
||||||
Account caller = getCurrentCallingAccount();
|
Account caller = getCurrentCallingAccount();
|
||||||
checkAccess(caller, AccessType.OperateEntry, true, account);
|
checkAccess(caller, AccessType.OperateEntry, true, account);
|
||||||
|
verifyCallerPrivilegeForUserOrAccountOperations(account);
|
||||||
|
|
||||||
boolean success = enableAccount(account.getId());
|
boolean success = enableAccount(account.getId());
|
||||||
if (success) {
|
if (success) {
|
||||||
@ -2067,6 +2189,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
}
|
}
|
||||||
|
|
||||||
checkAccess(caller, AccessType.OperateEntry, true, account);
|
checkAccess(caller, AccessType.OperateEntry, true, account);
|
||||||
|
verifyCallerPrivilegeForUserOrAccountOperations(account);
|
||||||
|
|
||||||
if (lockAccount(account.getId())) {
|
if (lockAccount(account.getId())) {
|
||||||
CallContext.current().putContextParameter(Account.class, account.getUuid());
|
CallContext.current().putContextParameter(Account.class, account.getUuid());
|
||||||
@ -2097,6 +2220,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
}
|
}
|
||||||
|
|
||||||
checkAccess(caller, AccessType.OperateEntry, true, account);
|
checkAccess(caller, AccessType.OperateEntry, true, account);
|
||||||
|
verifyCallerPrivilegeForUserOrAccountOperations(account);
|
||||||
|
|
||||||
if (disableAccount(account.getId())) {
|
if (disableAccount(account.getId())) {
|
||||||
CallContext.current().putContextParameter(Account.class, account.getUuid());
|
CallContext.current().putContextParameter(Account.class, account.getUuid());
|
||||||
@ -2142,6 +2266,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
// Check if user performing the action is allowed to modify this account
|
// Check if user performing the action is allowed to modify this account
|
||||||
Account caller = getCurrentCallingAccount();
|
Account caller = getCurrentCallingAccount();
|
||||||
checkAccess(caller, _domainMgr.getDomain(account.getDomainId()));
|
checkAccess(caller, _domainMgr.getDomain(account.getDomainId()));
|
||||||
|
verifyCallerPrivilegeForUserOrAccountOperations(account);
|
||||||
|
|
||||||
validateAndUpdateAccountApiKeyAccess(cmd, acctForUpdate);
|
validateAndUpdateAccountApiKeyAccess(cmd, acctForUpdate);
|
||||||
|
|
||||||
@ -2233,6 +2358,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
|
|
||||||
// don't allow to delete the user from the account of type Project
|
// don't allow to delete the user from the account of type Project
|
||||||
checkAccountAndAccess(user, account);
|
checkAccountAndAccess(user, account);
|
||||||
|
verifyCallerPrivilegeForUserOrAccountOperations(user);
|
||||||
return _userDao.remove(id);
|
return _userDao.remove(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2243,6 +2369,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
UserVO user = getValidUserVO(id);
|
UserVO user = getValidUserVO(id);
|
||||||
Account oldAccount = _accountDao.findById(user.getAccountId());
|
Account oldAccount = _accountDao.findById(user.getAccountId());
|
||||||
checkAccountAndAccess(user, oldAccount);
|
checkAccountAndAccess(user, oldAccount);
|
||||||
|
verifyCallerPrivilegeForUserOrAccountOperations(user);
|
||||||
long domainId = oldAccount.getDomainId();
|
long domainId = oldAccount.getDomainId();
|
||||||
|
|
||||||
long newAccountId = getNewAccountId(domainId, cmd.getAccountName(), cmd.getAccountId());
|
long newAccountId = getNewAccountId(domainId, cmd.getAccountName(), cmd.getAccountId());
|
||||||
@ -2580,6 +2707,11 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!Account.Type.PROJECT.equals(accountType)) {
|
||||||
|
AccountVO newAccount = new AccountVO(accountName, domainId, networkDomain, accountType, roleId, uuid);
|
||||||
|
verifyCallerPrivilegeForUserOrAccountOperations(newAccount);
|
||||||
|
}
|
||||||
|
|
||||||
// Create the account
|
// Create the account
|
||||||
return Transaction.execute(new TransactionCallback<>() {
|
return Transaction.execute(new TransactionCallback<>() {
|
||||||
@Override
|
@Override
|
||||||
@ -2644,107 +2776,17 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserAccount authenticateUser(final String username, final String password, final Long domainId, final InetAddress loginIpAddress, final Map<String, Object[]> requestParameters) {
|
public UserAccount authenticateUser(final String username, final String password, final Long domainId, final InetAddress loginIpAddress, final Map<String, Object[]> requestParameters) {
|
||||||
|
long authStartTimeInMs = System.currentTimeMillis();
|
||||||
UserAccount user = null;
|
UserAccount user = null;
|
||||||
final String[] oAuthProviderArray = (String[])requestParameters.get(ApiConstants.PROVIDER);
|
final String[] oAuthProviderArray = (String[])requestParameters.get(ApiConstants.PROVIDER);
|
||||||
final String[] secretCodeArray = (String[])requestParameters.get(ApiConstants.SECRET_CODE);
|
final String[] secretCodeArray = (String[])requestParameters.get(ApiConstants.SECRET_CODE);
|
||||||
String oauthProvider = ((oAuthProviderArray == null) ? null : oAuthProviderArray[0]);
|
String oauthProvider = ((oAuthProviderArray == null) ? null : oAuthProviderArray[0]);
|
||||||
String secretCode = ((secretCodeArray == null) ? null : secretCodeArray[0]);
|
String secretCode = ((secretCodeArray == null) ? null : secretCodeArray[0]);
|
||||||
|
|
||||||
|
|
||||||
if ((password != null && !password.isEmpty()) || (oauthProvider != null && secretCode != null)) {
|
if ((password != null && !password.isEmpty()) || (oauthProvider != null && secretCode != null)) {
|
||||||
user = getUserAccount(username, password, domainId, requestParameters);
|
user = getUserAccount(username, password, domainId, requestParameters);
|
||||||
} else {
|
} else {
|
||||||
String key = _configDao.getValue("security.singlesignon.key");
|
user = getUserAccountForSSO(username, domainId, requestParameters);
|
||||||
if (key == null) {
|
|
||||||
// the SSO key is gone, don't authenticate
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
String singleSignOnTolerance = _configDao.getValue("security.singlesignon.tolerance.millis");
|
|
||||||
if (singleSignOnTolerance == null) {
|
|
||||||
// the SSO tolerance is gone (how much time before/after system time we'll allow the login request to be
|
|
||||||
// valid),
|
|
||||||
// don't authenticate
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
long tolerance = Long.parseLong(singleSignOnTolerance);
|
|
||||||
String signature = null;
|
|
||||||
long timestamp = 0L;
|
|
||||||
String unsignedRequest;
|
|
||||||
StringBuffer unsignedRequestBuffer = new StringBuffer();
|
|
||||||
|
|
||||||
// - build a request string with sorted params, make sure it's all lowercase
|
|
||||||
// - sign the request, verify the signature is the same
|
|
||||||
List<String> parameterNames = new ArrayList<>();
|
|
||||||
|
|
||||||
for (Object paramNameObj : requestParameters.keySet()) {
|
|
||||||
parameterNames.add((String)paramNameObj); // put the name in a list that we'll sort later
|
|
||||||
}
|
|
||||||
|
|
||||||
Collections.sort(parameterNames);
|
|
||||||
|
|
||||||
try {
|
|
||||||
for (String paramName : parameterNames) {
|
|
||||||
// parameters come as name/value pairs in the form String/String[]
|
|
||||||
String paramValue = ((String[])requestParameters.get(paramName))[0];
|
|
||||||
|
|
||||||
if ("signature".equalsIgnoreCase(paramName)) {
|
|
||||||
signature = paramValue;
|
|
||||||
} else {
|
|
||||||
if ("timestamp".equalsIgnoreCase(paramName)) {
|
|
||||||
String timestampStr = paramValue;
|
|
||||||
try {
|
|
||||||
// If the timestamp is in a valid range according to our tolerance, verify the request
|
|
||||||
// signature, otherwise return null to indicate authentication failure
|
|
||||||
timestamp = Long.parseLong(timestampStr);
|
|
||||||
long currentTime = System.currentTimeMillis();
|
|
||||||
if (Math.abs(currentTime - timestamp) > tolerance) {
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug("Expired timestamp passed in to login, current time = " + currentTime + ", timestamp = " + timestamp);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} catch (NumberFormatException nfe) {
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug("Invalid timestamp passed in to login: " + timestampStr);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unsignedRequestBuffer.length() != 0) {
|
|
||||||
unsignedRequestBuffer.append("&");
|
|
||||||
}
|
|
||||||
unsignedRequestBuffer.append(paramName).append("=").append(URLEncoder.encode(paramValue, "UTF-8"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((signature == null) || (timestamp == 0L)) {
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug("Missing parameters in login request, signature = " + signature + ", timestamp = " + timestamp);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsignedRequest = unsignedRequestBuffer.toString().toLowerCase().replaceAll("\\+", "%20");
|
|
||||||
|
|
||||||
Mac mac = Mac.getInstance("HmacSHA1");
|
|
||||||
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "HmacSHA1");
|
|
||||||
mac.init(keySpec);
|
|
||||||
mac.update(unsignedRequest.getBytes());
|
|
||||||
byte[] encryptedBytes = mac.doFinal();
|
|
||||||
String computedSignature = new String(Base64.encodeBase64(encryptedBytes));
|
|
||||||
boolean equalSig = ConstantTimeComparator.compareStrings(signature, computedSignature);
|
|
||||||
if (!equalSig) {
|
|
||||||
logger.info("User signature: " + signature + " is not equaled to computed signature: " + computedSignature);
|
|
||||||
} else {
|
|
||||||
user = _userAccountDao.getUserAccount(username, domainId);
|
|
||||||
}
|
|
||||||
} catch (Exception ex) {
|
|
||||||
logger.error("Exception authenticating user", ex);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
@ -2778,18 +2820,35 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ActionEventUtils.onActionEvent(user.getId(), user.getAccountId(), user.getDomainId(), EventTypes.EVENT_USER_LOGIN, "user has logged in from IP Address " + loginIpAddress, user.getId(), ApiCommandResourceType.User.toString());
|
||||||
|
|
||||||
|
validUserLastAuthTimeDurationInMs = System.currentTimeMillis() - authStartTimeInMs;
|
||||||
// Here all is fine!
|
// Here all is fine!
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("User: " + username + " in domain " + domainId + " has successfully logged in");
|
logger.debug(String.format("User: %s in domain %d has successfully logged in, auth time duration - %d ms", username, domainId, validUserLastAuthTimeDurationInMs));
|
||||||
}
|
}
|
||||||
|
|
||||||
ActionEventUtils.onActionEvent(user.getId(), user.getAccountId(), user.getDomainId(), EventTypes.EVENT_USER_LOGIN, "user has logged in from IP Address " + loginIpAddress, user.getId(), ApiCommandResourceType.User.toString());
|
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
} else {
|
} else {
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("User: " + username + " in domain " + domainId + " has failed to log in");
|
logger.debug("User: " + username + " in domain " + domainId + " has failed to log in");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long waitTimeDurationInMs;
|
||||||
|
long invalidUserAuthTimeDurationInMs = System.currentTimeMillis() - authStartTimeInMs;
|
||||||
|
if (validUserLastAuthTimeDurationInMs > 0) {
|
||||||
|
waitTimeDurationInMs = validUserLastAuthTimeDurationInMs - invalidUserAuthTimeDurationInMs;
|
||||||
|
} else {
|
||||||
|
waitTimeDurationInMs = DEFAULT_USER_AUTH_TIME_DURATION_MS - invalidUserAuthTimeDurationInMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (waitTimeDurationInMs > 0) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(waitTimeDurationInMs);
|
||||||
|
} catch (final InterruptedException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2827,7 +2886,6 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
boolean updateIncorrectLoginCount = actionsOnFailedAuthenticaion.contains(ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT);
|
boolean updateIncorrectLoginCount = actionsOnFailedAuthenticaion.contains(ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT);
|
||||||
|
|
||||||
if (authenticated) {
|
if (authenticated) {
|
||||||
|
|
||||||
Domain domain = _domainMgr.getDomain(domainId);
|
Domain domain = _domainMgr.getDomain(domainId);
|
||||||
String domainName = null;
|
String domainName = null;
|
||||||
if (domain != null) {
|
if (domain != null) {
|
||||||
@ -2869,6 +2927,99 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private UserAccount getUserAccountForSSO(String username, Long domainId, Map<String, Object[]> requestParameters) {
|
||||||
|
String key = _configDao.getValue("security.singlesignon.key");
|
||||||
|
if (key == null) {
|
||||||
|
// the SSO key is gone, don't authenticate
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String singleSignOnTolerance = _configDao.getValue("security.singlesignon.tolerance.millis");
|
||||||
|
if (singleSignOnTolerance == null) {
|
||||||
|
// the SSO tolerance is gone (how much time before/after system time we'll allow the login request to be
|
||||||
|
// valid),
|
||||||
|
// don't authenticate
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
UserAccount user = null;
|
||||||
|
long tolerance = Long.parseLong(singleSignOnTolerance);
|
||||||
|
String signature = null;
|
||||||
|
long timestamp = 0L;
|
||||||
|
String unsignedRequest;
|
||||||
|
StringBuffer unsignedRequestBuffer = new StringBuffer();
|
||||||
|
|
||||||
|
// - build a request string with sorted params, make sure it's all lowercase
|
||||||
|
// - sign the request, verify the signature is the same
|
||||||
|
List<String> parameterNames = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Object paramNameObj : requestParameters.keySet()) {
|
||||||
|
parameterNames.add((String)paramNameObj); // put the name in a list that we'll sort later
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.sort(parameterNames);
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (String paramName : parameterNames) {
|
||||||
|
// parameters come as name/value pairs in the form String/String[]
|
||||||
|
String paramValue = ((String[])requestParameters.get(paramName))[0];
|
||||||
|
|
||||||
|
if ("signature".equalsIgnoreCase(paramName)) {
|
||||||
|
signature = paramValue;
|
||||||
|
} else {
|
||||||
|
if ("timestamp".equalsIgnoreCase(paramName)) {
|
||||||
|
String timestampStr = paramValue;
|
||||||
|
try {
|
||||||
|
// If the timestamp is in a valid range according to our tolerance, verify the request
|
||||||
|
// signature, otherwise return null to indicate authentication failure
|
||||||
|
timestamp = Long.parseLong(timestampStr);
|
||||||
|
long currentTime = System.currentTimeMillis();
|
||||||
|
if (Math.abs(currentTime - timestamp) > tolerance) {
|
||||||
|
logger.debug("Expired timestamp passed in to login, current time = {}, timestamp = {}", currentTime, timestamp);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
logger.debug("Invalid timestamp passed in to login: {}", timestampStr);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unsignedRequestBuffer.length() != 0) {
|
||||||
|
unsignedRequestBuffer.append("&");
|
||||||
|
}
|
||||||
|
unsignedRequestBuffer.append(paramName).append("=").append(URLEncoder.encode(paramValue, "UTF-8"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((signature == null) || (timestamp == 0L)) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Missing parameters in login request, signature = " + signature + ", timestamp = " + timestamp);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsignedRequest = unsignedRequestBuffer.toString().toLowerCase().replaceAll("\\+", "%20");
|
||||||
|
|
||||||
|
Mac mac = Mac.getInstance("HmacSHA1");
|
||||||
|
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "HmacSHA1");
|
||||||
|
mac.init(keySpec);
|
||||||
|
mac.update(unsignedRequest.getBytes());
|
||||||
|
byte[] encryptedBytes = mac.doFinal();
|
||||||
|
String computedSignature = new String(Base64.encodeBase64(encryptedBytes));
|
||||||
|
boolean equalSig = ConstantTimeComparator.compareStrings(signature, computedSignature);
|
||||||
|
if (!equalSig) {
|
||||||
|
logger.info("User signature: " + signature + " is not equaled to computed signature: " + computedSignature);
|
||||||
|
} else {
|
||||||
|
user = _userAccountDao.getUserAccount(username, domainId);
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
logger.error("Exception authenticating user", ex);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
protected void updateLoginAttemptsWhenIncorrectLoginAttemptsEnabled(UserAccount account, boolean updateIncorrectLoginCount,
|
protected void updateLoginAttemptsWhenIncorrectLoginAttemptsEnabled(UserAccount account, boolean updateIncorrectLoginCount,
|
||||||
int allowedLoginAttempts) {
|
int allowedLoginAttempts) {
|
||||||
int attemptsMade = account.getLoginAttempts() + 1;
|
int attemptsMade = account.getLoginAttempts() + 1;
|
||||||
@ -2904,8 +3055,8 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
}
|
}
|
||||||
final Account account = getAccount(getUserAccountById(userId).getAccountId()); //Extracting the Account from the userID of the requested user.
|
final Account account = getAccount(getUserAccountById(userId).getAccountId()); //Extracting the Account from the userID of the requested user.
|
||||||
User caller = CallContext.current().getCallingUser();
|
User caller = CallContext.current().getCallingUser();
|
||||||
preventRootDomainAdminAccessToRootAdminKeys(caller, account);
|
|
||||||
checkAccess(caller, account);
|
checkAccess(caller, account);
|
||||||
|
verifyCallerPrivilegeForUserOrAccountOperations(user);
|
||||||
|
|
||||||
Map<String, String> keys = new HashMap<>();
|
Map<String, String> keys = new HashMap<>();
|
||||||
keys.put("apikey", user.getApiKey());
|
keys.put("apikey", user.getApiKey());
|
||||||
@ -2969,8 +3120,8 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
}
|
}
|
||||||
|
|
||||||
Account account = _accountDao.findById(user.getAccountId());
|
Account account = _accountDao.findById(user.getAccountId());
|
||||||
preventRootDomainAdminAccessToRootAdminKeys(user, account);
|
|
||||||
checkAccess(caller, null, true, account);
|
checkAccess(caller, null, true, account);
|
||||||
|
verifyCallerPrivilegeForUserOrAccountOperations(user);
|
||||||
|
|
||||||
// don't allow updating system user
|
// don't allow updating system user
|
||||||
if (user.getId() == User.UID_SYSTEM) {
|
if (user.getId() == User.UID_SYSTEM) {
|
||||||
@ -3426,7 +3577,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
public ConfigKey<?>[] getConfigKeys() {
|
public ConfigKey<?>[] getConfigKeys() {
|
||||||
return new ConfigKey<?>[] {UseSecretKeyInResponse, enableUserTwoFactorAuthentication,
|
return new ConfigKey<?>[] {UseSecretKeyInResponse, enableUserTwoFactorAuthentication,
|
||||||
userTwoFactorAuthenticationDefaultProvider, mandateUserTwoFactorAuthentication, userTwoFactorAuthenticationIssuer, apiKeyAccess,
|
userTwoFactorAuthenticationDefaultProvider, mandateUserTwoFactorAuthentication, userTwoFactorAuthenticationIssuer, apiKeyAccess,
|
||||||
userAllowMultipleAccounts};
|
userAllowMultipleAccounts, listOfRoleTypesAllowedForOperationsOfSameRoleType, allowOperationsOnUsersInSameAccount};
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<UserTwoFactorAuthenticator> getUserTwoFactorAuthenticationProviders() {
|
public List<UserTwoFactorAuthenticator> getUserTwoFactorAuthenticationProviders() {
|
||||||
|
|||||||
@ -45,10 +45,12 @@ import com.cloud.storage.StorageManager;
|
|||||||
import com.cloud.storage.dao.VMTemplateZoneDao;
|
import com.cloud.storage.dao.VMTemplateZoneDao;
|
||||||
import com.cloud.storage.dao.VolumeDao;
|
import com.cloud.storage.dao.VolumeDao;
|
||||||
import com.cloud.user.Account;
|
import com.cloud.user.Account;
|
||||||
|
import com.cloud.user.AccountManagerImpl;
|
||||||
import com.cloud.user.User;
|
import com.cloud.user.User;
|
||||||
import com.cloud.utils.Pair;
|
import com.cloud.utils.Pair;
|
||||||
import com.cloud.utils.db.EntityManager;
|
import com.cloud.utils.db.EntityManager;
|
||||||
import com.cloud.utils.db.SearchCriteria;
|
import com.cloud.utils.db.SearchCriteria;
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
import com.cloud.utils.net.NetUtils;
|
import com.cloud.utils.net.NetUtils;
|
||||||
import com.cloud.vm.dao.VMInstanceDao;
|
import com.cloud.vm.dao.VMInstanceDao;
|
||||||
import org.apache.cloudstack.annotation.dao.AnnotationDao;
|
import org.apache.cloudstack.annotation.dao.AnnotationDao;
|
||||||
@ -57,6 +59,7 @@ import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd;
|
|||||||
import org.apache.cloudstack.api.command.admin.offering.UpdateDiskOfferingCmd;
|
import org.apache.cloudstack.api.command.admin.offering.UpdateDiskOfferingCmd;
|
||||||
import org.apache.cloudstack.api.command.admin.zone.DeleteZoneCmd;
|
import org.apache.cloudstack.api.command.admin.zone.DeleteZoneCmd;
|
||||||
import org.apache.cloudstack.config.Configuration;
|
import org.apache.cloudstack.config.Configuration;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope;
|
import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope;
|
||||||
import org.apache.cloudstack.framework.config.ConfigDepot;
|
import org.apache.cloudstack.framework.config.ConfigDepot;
|
||||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||||
@ -192,6 +195,7 @@ public class ConfigurationManagerImplTest {
|
|||||||
configurationManagerImplSpy.populateConfigValuesForValidationSet();
|
configurationManagerImplSpy.populateConfigValuesForValidationSet();
|
||||||
configurationManagerImplSpy.weightBasedParametersForValidation();
|
configurationManagerImplSpy.weightBasedParametersForValidation();
|
||||||
configurationManagerImplSpy.overProvisioningFactorsForValidation();
|
configurationManagerImplSpy.overProvisioningFactorsForValidation();
|
||||||
|
configurationManagerImplSpy.populateConfigKeysAllowedOnlyForDefaultAdmin();
|
||||||
ReflectionTestUtils.setField(configurationManagerImplSpy, "templateZoneDao", vmTemplateZoneDao);
|
ReflectionTestUtils.setField(configurationManagerImplSpy, "templateZoneDao", vmTemplateZoneDao);
|
||||||
ReflectionTestUtils.setField(configurationManagerImplSpy, "annotationDao", annotationDao);
|
ReflectionTestUtils.setField(configurationManagerImplSpy, "annotationDao", annotationDao);
|
||||||
|
|
||||||
@ -884,4 +888,46 @@ public class ConfigurationManagerImplTest {
|
|||||||
Pair<Configuration, String> result = configurationManagerImplSpy.resetConfiguration(cmd);
|
Pair<Configuration, String> result = configurationManagerImplSpy.resetConfiguration(cmd);
|
||||||
Assert.assertEquals(".85", result.second());
|
Assert.assertEquals(".85", result.second());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateConfigurationAllowedOnlyForDefaultAdmin_withAdminUser_shouldNotThrowException() {
|
||||||
|
CallContext callContext = mock(CallContext.class);
|
||||||
|
when(callContext.getCallingUserId()).thenReturn(User.UID_ADMIN);
|
||||||
|
try (MockedStatic<CallContext> ignored = Mockito.mockStatic(CallContext.class)) {
|
||||||
|
when(CallContext.current()).thenReturn(callContext);
|
||||||
|
configurationManagerImplSpy.validateConfigurationAllowedOnlyForDefaultAdmin(AccountManagerImpl.listOfRoleTypesAllowedForOperationsOfSameRoleType.key(), "Admin");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateConfigurationAllowedOnlyForDefaultAdmin_withNonAdminUser_shouldThrowException() {
|
||||||
|
CallContext callContext = mock(CallContext.class);
|
||||||
|
when(callContext.getCallingUserId()).thenReturn(123L);
|
||||||
|
try (MockedStatic<CallContext> ignored = Mockito.mockStatic(CallContext.class)) {
|
||||||
|
when(CallContext.current()).thenReturn(callContext);
|
||||||
|
Assert.assertThrows(CloudRuntimeException.class, () ->
|
||||||
|
configurationManagerImplSpy.validateConfigurationAllowedOnlyForDefaultAdmin(AccountManagerImpl.allowOperationsOnUsersInSameAccount.key(), "Admin")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateConfigurationAllowedOnlyForDefaultAdmin_withNonRestrictedKey_shouldNotThrowException() {
|
||||||
|
CallContext callContext = mock(CallContext.class);
|
||||||
|
try (MockedStatic<CallContext> ignored = Mockito.mockStatic(CallContext.class)) {
|
||||||
|
when(CallContext.current()).thenReturn(callContext);
|
||||||
|
configurationManagerImplSpy.validateConfigurationAllowedOnlyForDefaultAdmin("some.other.config.key", "Admin");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = CloudRuntimeException.class)
|
||||||
|
public void testValidateConfigurationAllowedOnlyForDefaultAdmin_withValidConfigNameAndInvalidValue_shouldThrowException() {
|
||||||
|
CallContext callContext = mock(CallContext.class);
|
||||||
|
try (MockedStatic<CallContext> mockedCallContext = Mockito.mockStatic(CallContext.class)) {
|
||||||
|
mockedCallContext.when(CallContext::current).thenReturn(callContext);
|
||||||
|
when(callContext.getCallingUserId()).thenReturn(User.UID_ADMIN);
|
||||||
|
String invalidValue = "Admin, SuperUser";
|
||||||
|
configurationManagerImplSpy.validateConfigurationAllowedOnlyForDefaultAdmin(AccountManagerImpl.listOfRoleTypesAllowedForOperationsOfSameRoleType.key(), invalidValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
// under the License.
|
// under the License.
|
||||||
package com.cloud.user;
|
package com.cloud.user;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.nullable;
|
import static org.mockito.ArgumentMatchers.nullable;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
@ -126,6 +127,9 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
|
|||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
ConfigKey<Boolean> enableUserTwoFactorAuthenticationMock;
|
ConfigKey<Boolean> enableUserTwoFactorAuthenticationMock;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
ConfigKey<Boolean> allowOperationsOnUsersInSameAccountMock;
|
||||||
@Mock
|
@Mock
|
||||||
RoleService roleService;
|
RoleService roleService;
|
||||||
|
|
||||||
@ -133,6 +137,9 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
|
|||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
enableUserTwoFactorAuthenticationMock = Mockito.mock(ConfigKey.class);
|
enableUserTwoFactorAuthenticationMock = Mockito.mock(ConfigKey.class);
|
||||||
accountManagerImpl.enableUserTwoFactorAuthentication = enableUserTwoFactorAuthenticationMock;
|
accountManagerImpl.enableUserTwoFactorAuthentication = enableUserTwoFactorAuthenticationMock;
|
||||||
|
|
||||||
|
allowOperationsOnUsersInSameAccountMock = Mockito.mock(ConfigKey.class);
|
||||||
|
accountManagerImpl.allowOperationsOnUsersInSameAccount = allowOperationsOnUsersInSameAccountMock;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@ -143,6 +150,8 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
|
|||||||
Mockito.doReturn(accountMockId).when(userVoMock).getAccountId();
|
Mockito.doReturn(accountMockId).when(userVoMock).getAccountId();
|
||||||
|
|
||||||
Mockito.doReturn(userVoIdMock).when(userVoMock).getId();
|
Mockito.doReturn(userVoIdMock).when(userVoMock).getId();
|
||||||
|
|
||||||
|
Mockito.lenient().doNothing().when(accountManagerImpl).checkRoleEscalation(accountMock, accountMock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -193,6 +202,7 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
|
|||||||
Mockito.when(_sshKeyPairDao.remove(Mockito.anyLong())).thenReturn(true);
|
Mockito.when(_sshKeyPairDao.remove(Mockito.anyLong())).thenReturn(true);
|
||||||
Mockito.when(userDataDao.removeByAccountId(Mockito.anyLong())).thenReturn(222);
|
Mockito.when(userDataDao.removeByAccountId(Mockito.anyLong())).thenReturn(222);
|
||||||
Mockito.doNothing().when(accountManagerImpl).deleteWebhooksForAccount(Mockito.anyLong());
|
Mockito.doNothing().when(accountManagerImpl).deleteWebhooksForAccount(Mockito.anyLong());
|
||||||
|
Mockito.doNothing().when(accountManagerImpl).verifyCallerPrivilegeForUserOrAccountOperations((Account) any());
|
||||||
|
|
||||||
Assert.assertTrue(accountManagerImpl.deleteUserAccount(42l));
|
Assert.assertTrue(accountManagerImpl.deleteUserAccount(42l));
|
||||||
// assert that this was a clean delete
|
// assert that this was a clean delete
|
||||||
@ -213,6 +223,7 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
|
|||||||
Mockito.lenient().when(_domainMgr.getDomain(Mockito.anyLong())).thenReturn(domain);
|
Mockito.lenient().when(_domainMgr.getDomain(Mockito.anyLong())).thenReturn(domain);
|
||||||
Mockito.lenient().when(securityChecker.checkAccess(Mockito.any(Account.class), Mockito.any(Domain.class))).thenReturn(true);
|
Mockito.lenient().when(securityChecker.checkAccess(Mockito.any(Account.class), Mockito.any(Domain.class))).thenReturn(true);
|
||||||
Mockito.doNothing().when(accountManagerImpl).deleteWebhooksForAccount(Mockito.anyLong());
|
Mockito.doNothing().when(accountManagerImpl).deleteWebhooksForAccount(Mockito.anyLong());
|
||||||
|
Mockito.doNothing().when(accountManagerImpl).verifyCallerPrivilegeForUserOrAccountOperations((Account) any());
|
||||||
|
|
||||||
Assert.assertTrue(accountManagerImpl.deleteUserAccount(42l));
|
Assert.assertTrue(accountManagerImpl.deleteUserAccount(42l));
|
||||||
// assert that this was NOT a clean delete
|
// assert that this was NOT a clean delete
|
||||||
@ -247,6 +258,7 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
|
|||||||
Mockito.doReturn(2L).when(accountVoMock).getId();
|
Mockito.doReturn(2L).when(accountVoMock).getId();
|
||||||
Mockito.doReturn(true).when(accountManagerImpl).isDeleteNeeded(Mockito.any(), Mockito.anyLong(), Mockito.any());
|
Mockito.doReturn(true).when(accountManagerImpl).isDeleteNeeded(Mockito.any(), Mockito.anyLong(), Mockito.any());
|
||||||
Mockito.doReturn(new ArrayList<Long>()).when(_projectAccountDao).listAdministratedProjectIds(Mockito.anyLong());
|
Mockito.doReturn(new ArrayList<Long>()).when(_projectAccountDao).listAdministratedProjectIds(Mockito.anyLong());
|
||||||
|
Mockito.doNothing().when(accountManagerImpl).verifyCallerPrivilegeForUserOrAccountOperations((Account) any());
|
||||||
|
|
||||||
accountManagerImpl.deleteUserAccount(accountId);
|
accountManagerImpl.deleteUserAccount(accountId);
|
||||||
}
|
}
|
||||||
@ -284,6 +296,7 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
|
|||||||
Mockito.doReturn(2L).when(userVoMock).getId();
|
Mockito.doReturn(2L).when(userVoMock).getId();
|
||||||
|
|
||||||
Mockito.doNothing().when(accountManagerImpl).checkAccountAndAccess(Mockito.any(), Mockito.any());
|
Mockito.doNothing().when(accountManagerImpl).checkAccountAndAccess(Mockito.any(), Mockito.any());
|
||||||
|
Mockito.doNothing().when(accountManagerImpl).verifyCallerPrivilegeForUserOrAccountOperations(userVoMock);
|
||||||
accountManagerImpl.deleteUser(cmd);
|
accountManagerImpl.deleteUser(cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -350,7 +363,6 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
|
|||||||
|
|
||||||
// Queried account - admin account
|
// Queried account - admin account
|
||||||
AccountVO adminAccountMock = Mockito.mock(AccountVO.class);
|
AccountVO adminAccountMock = Mockito.mock(AccountVO.class);
|
||||||
Mockito.when(adminAccountMock.getAccountId()).thenReturn(2L);
|
|
||||||
Mockito.when(_accountDao.findByIdIncludingRemoved(2L)).thenReturn(adminAccountMock);
|
Mockito.when(_accountDao.findByIdIncludingRemoved(2L)).thenReturn(adminAccountMock);
|
||||||
Mockito.lenient().when(accountService.isRootAdmin(2L)).thenReturn(true);
|
Mockito.lenient().when(accountService.isRootAdmin(2L)).thenReturn(true);
|
||||||
Mockito.lenient().when(securityChecker.checkAccess(Mockito.any(Account.class),
|
Mockito.lenient().when(securityChecker.checkAccess(Mockito.any(Account.class),
|
||||||
@ -398,6 +410,12 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void updateUserTestTimeZoneAndEmailNull() {
|
public void updateUserTestTimeZoneAndEmailNull() {
|
||||||
|
Mockito.when(userVoMock.getAccountId()).thenReturn(10L);
|
||||||
|
Mockito.doReturn(accountMock).when(accountManagerImpl).getAccount(10L);
|
||||||
|
Mockito.when(accountMock.getAccountId()).thenReturn(10L);
|
||||||
|
Mockito.doReturn(false).when(accountManagerImpl).isRootAdmin(10L);
|
||||||
|
Mockito.lenient().when(accountManagerImpl.getRoleType(Mockito.eq(accountMock))).thenReturn(RoleType.User);
|
||||||
|
|
||||||
prepareMockAndExecuteUpdateUserTest(0);
|
prepareMockAndExecuteUpdateUserTest(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -405,6 +423,11 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
|
|||||||
public void updateUserTestTimeZoneAndEmailNotNull() {
|
public void updateUserTestTimeZoneAndEmailNotNull() {
|
||||||
Mockito.when(UpdateUserCmdMock.getEmail()).thenReturn("email");
|
Mockito.when(UpdateUserCmdMock.getEmail()).thenReturn("email");
|
||||||
Mockito.when(UpdateUserCmdMock.getTimezone()).thenReturn("timezone");
|
Mockito.when(UpdateUserCmdMock.getTimezone()).thenReturn("timezone");
|
||||||
|
Mockito.when(userVoMock.getAccountId()).thenReturn(10L);
|
||||||
|
Mockito.doReturn(accountMock).when(accountManagerImpl).getAccount(10L);
|
||||||
|
Mockito.when(accountMock.getAccountId()).thenReturn(10L);
|
||||||
|
Mockito.doReturn(false).when(accountManagerImpl).isRootAdmin(10L);
|
||||||
|
Mockito.lenient().when(accountManagerImpl.getRoleType(Mockito.eq(accountMock))).thenReturn(RoleType.User);
|
||||||
prepareMockAndExecuteUpdateUserTest(1);
|
prepareMockAndExecuteUpdateUserTest(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -422,14 +445,17 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
|
|||||||
|
|
||||||
Mockito.doReturn(true).when(userDaoMock).update(Mockito.anyLong(), Mockito.eq(userVoMock));
|
Mockito.doReturn(true).when(userDaoMock).update(Mockito.anyLong(), Mockito.eq(userVoMock));
|
||||||
Mockito.doReturn(Mockito.mock(UserAccountVO.class)).when(userAccountDaoMock).findById(Mockito.anyLong());
|
Mockito.doReturn(Mockito.mock(UserAccountVO.class)).when(userAccountDaoMock).findById(Mockito.anyLong());
|
||||||
|
Mockito.doNothing().when(accountManagerImpl).checkAccess(nullable(User.class), nullable(Account.class));
|
||||||
|
|
||||||
accountManagerImpl.updateUser(UpdateUserCmdMock);
|
accountManagerImpl.updateUser(UpdateUserCmdMock);
|
||||||
|
|
||||||
|
Mockito.lenient().doNothing().when(accountManagerImpl).checkRoleEscalation(accountMock, accountMock);
|
||||||
|
|
||||||
InOrder inOrder = Mockito.inOrder(userVoMock, accountManagerImpl, userDaoMock, userAccountDaoMock);
|
InOrder inOrder = Mockito.inOrder(userVoMock, accountManagerImpl, userDaoMock, userAccountDaoMock);
|
||||||
|
|
||||||
inOrder.verify(accountManagerImpl).retrieveAndValidateUser(UpdateUserCmdMock);
|
inOrder.verify(accountManagerImpl).retrieveAndValidateUser(UpdateUserCmdMock);
|
||||||
inOrder.verify(accountManagerImpl).validateAndUpdateApiAndSecretKeyIfNeeded(UpdateUserCmdMock, userVoMock);
|
|
||||||
inOrder.verify(accountManagerImpl).retrieveAndValidateAccount(userVoMock);
|
inOrder.verify(accountManagerImpl).retrieveAndValidateAccount(userVoMock);
|
||||||
|
inOrder.verify(accountManagerImpl).validateAndUpdateApiAndSecretKeyIfNeeded(UpdateUserCmdMock, userVoMock);
|
||||||
|
|
||||||
inOrder.verify(accountManagerImpl).validateAndUpdateFirstNameIfNeeded(UpdateUserCmdMock, userVoMock);
|
inOrder.verify(accountManagerImpl).validateAndUpdateFirstNameIfNeeded(UpdateUserCmdMock, userVoMock);
|
||||||
inOrder.verify(accountManagerImpl).validateAndUpdateLastNameIfNeeded(UpdateUserCmdMock, userVoMock);
|
inOrder.verify(accountManagerImpl).validateAndUpdateLastNameIfNeeded(UpdateUserCmdMock, userVoMock);
|
||||||
@ -1341,8 +1367,8 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
|
|||||||
accountManagerImpl.validateRoleChange(account, newRole, caller);
|
accountManagerImpl.validateRoleChange(account, newRole, caller);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void checkIfAccountManagesProjectsTestNotThrowExceptionWhenTheAccountIsNotAProjectAdministrator() {
|
public void checkIfAccountManagesProjectsTestNotThrowExceptionWhenTheAccountIsNotAProjectAdministrator() {
|
||||||
long accountId = 1L;
|
long accountId = 1L;
|
||||||
List<Long> managedProjectIds = new ArrayList<>();
|
List<Long> managedProjectIds = new ArrayList<>();
|
||||||
|
|
||||||
@ -1479,4 +1505,74 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
|
|||||||
|
|
||||||
accountManagerImpl.assertUserNotAlreadyInDomain(existingUser, originalAccount);
|
accountManagerImpl.assertUserNotAlreadyInDomain(existingUser, originalAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCheckCallerRoleTypeAllowedToUpdateUserSameAccount() {
|
||||||
|
Mockito.lenient().when(accountManagerImpl.getCurrentCallingAccount()).thenReturn(accountMock);
|
||||||
|
Mockito.lenient().when(accountManagerImpl.getRoleType(Mockito.eq(accountMock))).thenReturn(RoleType.DomainAdmin);
|
||||||
|
|
||||||
|
accountManagerImpl.checkCallerRoleTypeAllowedForUserOrAccountOperations(accountMock, userVoMock);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = PermissionDeniedException.class)
|
||||||
|
public void testCheckCallerRoleTypeAllowedToUpdateUserLowerAccountRoleType() {
|
||||||
|
Account callingAccount = Mockito.mock(Account.class);
|
||||||
|
Mockito.lenient().when(callingAccount.getAccountId()).thenReturn(2L);
|
||||||
|
Mockito.lenient().doReturn(callingAccount).when(accountManagerImpl).getAccount(2L);
|
||||||
|
Mockito.lenient().when(accountManagerImpl.getCurrentCallingAccount()).thenReturn(callingAccount);
|
||||||
|
Mockito.lenient().when(accountManagerImpl.getRoleType(Mockito.eq(callingAccount))).thenReturn(RoleType.DomainAdmin);
|
||||||
|
Mockito.lenient().when(accountManagerImpl.getRoleType(Mockito.eq(accountMock))).thenReturn(RoleType.Admin);
|
||||||
|
accountManagerImpl.checkCallerRoleTypeAllowedForUserOrAccountOperations(accountMock, userVoMock);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testcheckCallerApiPermissionsForUserOperationsRootAdminSameCaller() {
|
||||||
|
Mockito.lenient().when(accountManagerImpl.getCurrentCallingAccount()).thenReturn(accountMock);
|
||||||
|
Mockito.when(accountMock.getId()).thenReturn(2L);
|
||||||
|
Mockito.doReturn(true).when(accountManagerImpl).isRootAdmin(2L);
|
||||||
|
accountManagerImpl.checkCallerApiPermissionsForUserOrAccountOperations(accountMock);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = PermissionDeniedException.class)
|
||||||
|
public void testcheckCallerApiPermissionsForUserOperationsRootAdminDifferentAccount() {
|
||||||
|
Mockito.lenient().when(accountManagerImpl.getCurrentCallingAccount()).thenReturn(callingAccount);
|
||||||
|
Mockito.lenient().when(callingAccount.getAccountId()).thenReturn(3L);
|
||||||
|
Mockito.lenient().doReturn(callingAccount).when(accountManagerImpl).getAccount(3L);
|
||||||
|
Mockito.lenient().doReturn(false).when(accountManagerImpl).isRootAdmin(3L);
|
||||||
|
|
||||||
|
Mockito.when(accountMock.getAccountId()).thenReturn(2L);
|
||||||
|
Mockito.doReturn(true).when(accountManagerImpl).isRootAdmin(2L);
|
||||||
|
|
||||||
|
accountManagerImpl.checkCallerApiPermissionsForUserOrAccountOperations(accountMock);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testcheckCallerApiPermissionsForUserOperationsAllowedApis() {
|
||||||
|
Mockito.lenient().when(accountManagerImpl.getCurrentCallingAccount()).thenReturn(callingAccount);
|
||||||
|
Mockito.lenient().when(callingAccount.getAccountId()).thenReturn(3L);
|
||||||
|
Mockito.lenient().doReturn(callingAccount).when(accountManagerImpl).getAccount(3L);
|
||||||
|
Mockito.lenient().doReturn(false).when(accountManagerImpl).isRootAdmin(3L);
|
||||||
|
|
||||||
|
Mockito.when(accountMock.getAccountId()).thenReturn(2L);
|
||||||
|
Mockito.doReturn(false).when(accountManagerImpl).isRootAdmin(2L);
|
||||||
|
|
||||||
|
Mockito.lenient().doNothing().when(accountManagerImpl).checkRoleEscalation(callingAccount, accountMock);
|
||||||
|
|
||||||
|
accountManagerImpl.checkCallerApiPermissionsForUserOrAccountOperations(accountMock);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = PermissionDeniedException.class)
|
||||||
|
public void testcheckCallerApiPermissionsForUserOperationsNotAllowedApis() {
|
||||||
|
Mockito.lenient().when(accountManagerImpl.getCurrentCallingAccount()).thenReturn(callingAccount);
|
||||||
|
Mockito.lenient().when(callingAccount.getAccountId()).thenReturn(3L);
|
||||||
|
Mockito.lenient().doReturn(callingAccount).when(accountManagerImpl).getAccount(3L);
|
||||||
|
Mockito.lenient().doReturn(false).when(accountManagerImpl).isRootAdmin(3L);
|
||||||
|
|
||||||
|
Mockito.when(accountMock.getAccountId()).thenReturn(2L);
|
||||||
|
Mockito.doReturn(false).when(accountManagerImpl).isRootAdmin(2L);
|
||||||
|
|
||||||
|
Mockito.lenient().doThrow(PermissionDeniedException.class).when(accountManagerImpl).checkRoleEscalation(callingAccount, accountMock);
|
||||||
|
|
||||||
|
accountManagerImpl.checkCallerApiPermissionsForUserOrAccountOperations(accountMock);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -192,6 +192,7 @@ public class AccountManagerImplVolumeDeleteEventTest extends AccountManagetImplT
|
|||||||
public void destroyedVMRootVolumeUsageEvent()
|
public void destroyedVMRootVolumeUsageEvent()
|
||||||
throws SecurityException, IllegalArgumentException, ReflectiveOperationException, AgentUnavailableException, ConcurrentOperationException, CloudException {
|
throws SecurityException, IllegalArgumentException, ReflectiveOperationException, AgentUnavailableException, ConcurrentOperationException, CloudException {
|
||||||
lenient().doReturn(vm).when(_vmMgr).destroyVm(nullable(Long.class), nullable(Boolean.class));
|
lenient().doReturn(vm).when(_vmMgr).destroyVm(nullable(Long.class), nullable(Boolean.class));
|
||||||
|
Mockito.doNothing().when(accountManagerImpl).verifyCallerPrivilegeForUserOrAccountOperations((Account) any());
|
||||||
List<UsageEventVO> emittedEvents = deleteUserAccountRootVolumeUsageEvents(true);
|
List<UsageEventVO> emittedEvents = deleteUserAccountRootVolumeUsageEvents(true);
|
||||||
Assert.assertEquals(0, emittedEvents.size());
|
Assert.assertEquals(0, emittedEvents.size());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -210,6 +210,9 @@ public class AccountManagetImplTestBase {
|
|||||||
@Mock
|
@Mock
|
||||||
RoutedIpv4Manager routedIpv4Manager;
|
RoutedIpv4Manager routedIpv4Manager;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
Account accountMock;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setup() {
|
||||||
accountManagerImpl.setUserAuthenticators(Arrays.asList(userAuthenticator));
|
accountManagerImpl.setUserAuthenticators(Arrays.asList(userAuthenticator));
|
||||||
|
|||||||
@ -503,4 +503,8 @@ public class MockAccountManagerImpl extends ManagerBase implements Manager, Acco
|
|||||||
public UserAccount clearUserTwoFactorAuthenticationInSetupStateOnLogin(UserAccount user) {
|
public UserAccount clearUserTwoFactorAuthenticationInSetupStateOnLogin(UserAccount user) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void verifyCallerPrivilegeForUserOrAccountOperations(Account userAccount) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user