// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.user;
import java.net.URLEncoder;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.ejb.Local;
import javax.naming.ConfigurationException;
import org.apache.commons.codec.binary.Base64;
import org.apache.log4j.Logger;
import com.cloud.acl.ControlledEntity;
import com.cloud.acl.SecurityChecker;
import com.cloud.acl.SecurityChecker.AccessType;
import com.cloud.api.ApiDBUtils;
import com.cloud.api.commands.DeleteUserCmd;
import com.cloud.api.commands.ListAccountsCmd;
import com.cloud.api.commands.ListUsersCmd;
import com.cloud.api.commands.RegisterCmd;
import com.cloud.api.commands.UpdateAccountCmd;
import com.cloud.api.commands.UpdateUserCmd;
import com.cloud.configuration.Config;
import com.cloud.configuration.ConfigurationManager;
import com.cloud.configuration.ResourceLimit;
import com.cloud.configuration.dao.ConfigurationDao;
import com.cloud.configuration.dao.ResourceCountDao;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.domain.Domain;
import com.cloud.domain.DomainVO;
import com.cloud.domain.dao.DomainDao;
import com.cloud.event.ActionEvent;
import com.cloud.event.EventTypes;
import com.cloud.event.EventUtils;
import com.cloud.exception.AgentUnavailableException;
import com.cloud.exception.CloudAuthenticationException;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.OperationTimedoutException;
import com.cloud.exception.PermissionDeniedException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.network.IPAddressVO;
import com.cloud.network.IpAddress;
import com.cloud.network.NetworkManager;
import com.cloud.network.NetworkVO;
import com.cloud.network.RemoteAccessVpnVO;
import com.cloud.network.VpnUserVO;
import com.cloud.network.dao.IPAddressDao;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.RemoteAccessVpnDao;
import com.cloud.network.dao.VpnUserDao;
import com.cloud.network.security.SecurityGroupManager;
import com.cloud.network.security.dao.SecurityGroupDao;
import com.cloud.network.vpc.Vpc;
import com.cloud.network.vpc.VpcManager;
import com.cloud.network.vpn.RemoteAccessVpnService;
import com.cloud.network.vpn.Site2SiteVpnManager;
import com.cloud.projects.Project;
import com.cloud.projects.Project.ListProjectResourcesCriteria;
import com.cloud.projects.ProjectInvitationVO;
import com.cloud.projects.ProjectManager;
import com.cloud.projects.ProjectVO;
import com.cloud.projects.dao.ProjectAccountDao;
import com.cloud.projects.dao.ProjectDao;
import com.cloud.server.auth.UserAuthenticator;
import com.cloud.storage.StorageManager;
import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.Volume;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.SnapshotDao;
import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.storage.snapshot.SnapshotManager;
import com.cloud.template.TemplateManager;
import com.cloud.template.VirtualMachineTemplate;
import com.cloud.user.Account.State;
import com.cloud.user.dao.AccountDao;
import com.cloud.user.dao.UserAccountDao;
import com.cloud.user.dao.UserDao;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.Pair;
import com.cloud.utils.Ternary;
import com.cloud.utils.component.Adapters;
import com.cloud.utils.component.ComponentLocator;
import com.cloud.utils.component.Inject;
import com.cloud.utils.component.Manager;
import com.cloud.utils.concurrency.NamedThreadFactory;
import com.cloud.utils.db.DB;
import com.cloud.utils.db.Filter;
import com.cloud.utils.db.GlobalLock;
import com.cloud.utils.db.JoinBuilder;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.net.NetUtils;
import com.cloud.vm.InstanceGroupVO;
import com.cloud.vm.ReservationContext;
import com.cloud.vm.ReservationContextImpl;
import com.cloud.vm.UserVmManager;
import com.cloud.vm.UserVmVO;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VirtualMachine.Type;
import com.cloud.vm.VirtualMachineManager;
import com.cloud.vm.dao.DomainRouterDao;
import com.cloud.vm.dao.InstanceGroupDao;
import com.cloud.vm.dao.UserVmDao;
import com.cloud.vm.dao.VMInstanceDao;
@Local(value = { AccountManager.class, AccountService.class })
public class AccountManagerImpl implements AccountManager, AccountService, Manager {
public static final Logger s_logger = Logger.getLogger(AccountManagerImpl.class);
private String _name;
@Inject
private AccountDao _accountDao;
@Inject
ConfigurationDao _configDao;
@Inject
private ResourceCountDao _resourceCountDao;
@Inject
private UserDao _userDao;
@Inject
private InstanceGroupDao _vmGroupDao;
@Inject
private UserAccountDao _userAccountDao;
@Inject
private VolumeDao _volumeDao;
@Inject
private UserVmDao _userVmDao;
@Inject
private VMTemplateDao _templateDao;
@Inject
private NetworkDao _networkDao;
@Inject
private SecurityGroupDao _securityGroupDao;
@Inject
private VMInstanceDao _vmDao;
@Inject
protected SnapshotDao _snapshotDao;
@Inject
protected VMTemplateDao _vmTemplateDao;
@Inject
private SecurityGroupManager _networkGroupMgr;
@Inject
private NetworkManager _networkMgr;
@Inject
private SnapshotManager _snapMgr;
@Inject
private UserVmManager _vmMgr;
@Inject
private StorageManager _storageMgr;
@Inject
private TemplateManager _tmpltMgr;
@Inject
private ConfigurationManager _configMgr;
@Inject
private VirtualMachineManager _itMgr;
@Inject
private RemoteAccessVpnDao _remoteAccessVpnDao;
@Inject
private RemoteAccessVpnService _remoteAccessVpnMgr;
@Inject
private VpnUserDao _vpnUser;
@Inject
private DataCenterDao _dcDao;
@Inject
private DomainManager _domainMgr;
@Inject
private ProjectManager _projectMgr;
@Inject
private ProjectDao _projectDao;
@Inject
private AccountDetailsDao _accountDetailsDao;
@Inject
private DomainDao _domainDao;
@Inject
private ProjectAccountDao _projectAccountDao;
@Inject
private IPAddressDao _ipAddressDao;
@Inject
private VpcManager _vpcMgr;
@Inject
private DomainRouterDao _routerDao;
@Inject
Site2SiteVpnManager _vpnMgr;
private Adapters<UserAuthenticator> _userAuthenticators;
private final ScheduledExecutorService _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("AccountChecker"));
int _allowedLoginAttempts;
UserVO _systemUser;
AccountVO _systemAccount;
@Inject(adapter = SecurityChecker.class)
Adapters<SecurityChecker> _securityCheckers;
int _cleanupInterval;
@Override
public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException {
_name = name;
_systemAccount = _accountDao.findById(AccountVO.ACCOUNT_ID_SYSTEM);
if (_systemAccount == null) {
throw new ConfigurationException("Unable to find the system account using " + Account.ACCOUNT_ID_SYSTEM);
}
_systemUser = _userDao.findById(UserVO.UID_SYSTEM);
if (_systemUser == null) {
throw new ConfigurationException("Unable to find the system user using " + User.UID_SYSTEM);
}
ComponentLocator locator = ComponentLocator.getCurrentLocator();
ConfigurationDao configDao = locator.getDao(ConfigurationDao.class);
Map<String, String> configs = configDao.getConfiguration(params);
String loginAttempts = configs.get(Config.IncorrectLoginAttemptsAllowed.key());
_allowedLoginAttempts = NumbersUtil.parseInt(loginAttempts, 5);
String value = configs.get(Config.AccountCleanupInterval.key());
_cleanupInterval = NumbersUtil.parseInt(value, 60 * 60 * 24); // 1 day.
_userAuthenticators = locator.getAdapters(UserAuthenticator.class);
if (_userAuthenticators == null || !_userAuthenticators.isSet()) {
s_logger.error("Unable to find an user authenticator.");
}
return true;
}
@Override
public UserVO getSystemUser() {
return _systemUser;
}
@Override
public String getName() {
return _name;
}
@Override
public boolean start() {
_executor.scheduleAtFixedRate(new AccountCleanupTask(), _cleanupInterval, _cleanupInterval, TimeUnit.SECONDS);
return true;
}
@Override
public boolean stop() {
return true;
}
public AccountVO getSystemAccount() {
if (_systemAccount == null) {
_systemAccount = _accountDao.findById(Account.ACCOUNT_ID_SYSTEM);
}
return _systemAccount;
}
@Override
public boolean isAdmin(short accountType) {
return ((accountType == Account.ACCOUNT_TYPE_ADMIN) || (accountType == Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN)
|| (accountType == Account.ACCOUNT_TYPE_DOMAIN_ADMIN) || (accountType == Account.ACCOUNT_TYPE_READ_ONLY_ADMIN));
}
@Override
public boolean isRootAdmin(short accountType) {
return (accountType == Account.ACCOUNT_TYPE_ADMIN);
}
public boolean isResourceDomainAdmin(short accountType) {
return (accountType == Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN);
}
public boolean isInternalAccount(short accountType) {
if (isRootAdmin(accountType) || (accountType == Account.ACCOUNT_ID_SYSTEM)) {
return true;
}
return false;
}
@Override
public void checkAccess(Account caller, Domain domain) throws PermissionDeniedException {
for (SecurityChecker checker : _securityCheckers) {
if (checker.checkAccess(caller, domain)) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Access granted to " + caller + " to " + domain + " by " + checker.getName());
}
return;
}
}
assert false : "How can all of the security checkers pass on checking this caller?";
throw new PermissionDeniedException("There's no way to confirm " + caller + " has access to " + domain);
}
@Override
public void checkAccess(Account caller, AccessType accessType, boolean sameOwner, ControlledEntity... entities) {
if (caller.getId() == Account.ACCOUNT_ID_SYSTEM || isRootAdmin(caller.getType())) {
// no need to make permission checks if the system/root admin makes the call
if (s_logger.isTraceEnabled()) {
s_logger.trace("No need to make permission check for System/RootAdmin account, returning true");
}
return;
}
HashMap<Long, List<ControlledEntity>> domains = new HashMap<Long, List<ControlledEntity>>();
Long ownerId = null;
ControlledEntity prevEntity = null;
for (ControlledEntity entity : entities) {
long domainId = entity.getDomainId();
if (entity.getAccountId() != -1 && domainId == -1) { // If account exists domainId should too so calculate
// it. This condition might be hit for templates or entities which miss domainId in their tables
Account account = ApiDBUtils.findAccountById(entity.getAccountId());
domainId = account != null ? account.getDomainId() : -1;
}
if (entity.getAccountId() != -1 && domainId != -1 && !(entity instanceof VirtualMachineTemplate) && !(accessType != null && accessType == AccessType.UseNetwork)) {
List<ControlledEntity> toBeChecked = domains.get(entity.getDomainId());
// for templates, we don't have to do cross domains check
if (toBeChecked == null) {
toBeChecked = new ArrayList<ControlledEntity>();
domains.put(domainId, toBeChecked);
}
toBeChecked.add(entity);
}
boolean granted = false;
for (SecurityChecker checker : _securityCheckers) {
if (checker.checkAccess(caller, entity, accessType)) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Access to " + entity + " granted to " + caller + " by " + checker.getName());
}
granted = true;
break;
}
}
if (sameOwner) {
if (ownerId == null) {
ownerId = entity.getAccountId();
} else if (ownerId.longValue() != entity.getAccountId()) {
throw new PermissionDeniedException("Entity " + entity + " and entity " + prevEntity + " belong to different accounts");
}
prevEntity = entity;
}
if (!granted) {
assert false : "How can all of the security checkers pass on checking this check: " + entity;
throw new PermissionDeniedException("There's no way to confirm " + caller + " has access to " + entity);
}
}
for (Map.Entry<Long, List<ControlledEntity>> domain : domains.entrySet()) {
for (SecurityChecker checker : _securityCheckers) {
Domain d = _domainMgr.getDomain(domain.getKey());
if (d == null || d.getRemoved() != null) {
throw new PermissionDeniedException("Domain is not found.", caller, domain.getValue());
}
try {
checker.checkAccess(caller, d);
} catch (PermissionDeniedException e) {
e.addDetails(caller, domain.getValue());
throw e;
}
}
}
// check that resources belong to the same account
}
@Override
public Long checkAccessAndSpecifyAuthority(Account caller, Long zoneId) {
// We just care for resource domain admin for now. He should be permitted to see only his zone.
if (isResourceDomainAdmin(caller.getType())) {
if (zoneId == null)
return getZoneIdForAccount(caller);
else if (zoneId.compareTo(getZoneIdForAccount(caller)) != 0)
throw new PermissionDeniedException("Caller " + caller + "is not allowed to access the zone " + zoneId);
else
return zoneId;
}
else
return zoneId;
}
private Long getZoneIdForAccount(Account account) {
// Currently just for resource domain admin
List<DataCenterVO> dcList = _dcDao.findZonesByDomainId(account.getDomainId());
if (dcList != null && dcList.size() != 0)
return dcList.get(0).getId();
else
throw new CloudRuntimeException("Failed to find any private zone for Resource domain admin.");
}
@DB
public void updateLoginAttempts(Long id, int attempts, boolean toDisable) {
Transaction txn = Transaction.currentTxn();
txn.start();
try {
UserAccountVO user = null;
user = _userAccountDao.lockRow(id, true);
user.setLoginAttempts(attempts);
if(toDisable) {
user.setState(State.disabled.toString());
}
_userAccountDao.update(id, user);
txn.commit();
} catch (Exception e) {
s_logger.error("Failed to update login attempts for user with id " + id );
}
txn.close();
}
private boolean doSetUserStatus(long userId, State state) {
UserVO userForUpdate = _userDao.createForUpdate();
userForUpdate.setState(state);
return _userDao.update(Long.valueOf(userId), userForUpdate);
}
@Override
public boolean enableAccount(long accountId) {
boolean success = false;
AccountVO acctForUpdate = _accountDao.createForUpdate();
acctForUpdate.setState(State.enabled);
acctForUpdate.setNeedsCleanup(false);
success = _accountDao.update(Long.valueOf(accountId), acctForUpdate);
return success;
}
@Override
public boolean lockAccount(long accountId) {
boolean success = false;
Account account = _accountDao.findById(accountId);
if (account != null) {
if (account.getState().equals(State.locked)) {
return true; // already locked, no-op
} else if (account.getState().equals(State.enabled)) {
AccountVO acctForUpdate = _accountDao.createForUpdate();
acctForUpdate.setState(State.locked);
success = _accountDao.update(Long.valueOf(accountId), acctForUpdate);
} else {
if (s_logger.isInfoEnabled()) {
s_logger.info("Attempting to lock a non-enabled account, current state is " + account.getState() + " (accountId: " + accountId + "), locking failed.");
}
}
} else {
s_logger.warn("Failed to lock account " + accountId + ", account not found.");
}
return success;
}
@Override
public boolean deleteAccount(AccountVO account, long callerUserId, Account caller) {
long accountId = account.getId();
//delete the account record
if (!_accountDao.remove(accountId)) {
s_logger.error("Unable to delete account " + accountId);
return false;
}
if (s_logger.isDebugEnabled()) {
s_logger.debug("Removed account " + accountId);
}
return cleanupAccount(account, callerUserId, caller);
}
@Override
public boolean cleanupAccount(AccountVO account, long callerUserId, Account caller) {
long accountId = account.getId();
boolean accountCleanupNeeded = false;
try {
//cleanup the users from the account
List<UserVO> users = _userDao.listByAccount(accountId);
for (UserVO user : users) {
if (!_userDao.remove(user.getId())) {
s_logger.error("Unable to delete user: " + user + " as a part of account " + account + " cleanup");
accountCleanupNeeded = true;
}
}
//delete the account from project accounts
_projectAccountDao.removeAccountFromProjects(accountId);
// delete all vm groups belonging to accont
List<InstanceGroupVO> groups = _vmGroupDao.listByAccountId(accountId);
for (InstanceGroupVO group : groups) {
if (!_vmMgr.deleteVmGroup(group.getId())) {
s_logger.error("Unable to delete group: " + group.getId());
accountCleanupNeeded = true;
}
}
// Delete the snapshots dir for the account. Have to do this before destroying the VMs.
boolean success = _snapMgr.deleteSnapshotDirsForAccount(accountId);
if (success) {
s_logger.debug("Successfully deleted snapshots directories for all volumes under account " + accountId + " across all zones");
}
// clean up templates
List<VMTemplateVO> userTemplates = _templateDao.listByAccountId(accountId);
boolean allTemplatesDeleted = true;
for (VMTemplateVO template : userTemplates) {
if (template.getRemoved() == null) {
try {
allTemplatesDeleted = _tmpltMgr.delete(callerUserId, template.getId(), null);
} catch (Exception e) {
s_logger.warn("Failed to delete template while removing account: " + template.getName() + " due to: ", e);
allTemplatesDeleted = false;
}
}
}
if (!allTemplatesDeleted) {
s_logger.warn("Failed to delete templates while removing account id=" + accountId);
accountCleanupNeeded = true;
}
// Destroy the account's VMs
List<UserVmVO> vms = _userVmDao.listByAccountId(accountId);
if (s_logger.isDebugEnabled()) {
s_logger.debug("Expunging # of vms (accountId=" + accountId + "): " + vms.size());
}
// no need to catch exception at this place as expunging vm should pass in order to perform further cleanup
for (UserVmVO vm : vms) {
if (!_vmMgr.expunge(vm, callerUserId, caller)) {
s_logger.error("Unable to expunge vm: " + vm.getId());
accountCleanupNeeded = true;
}
}
// Mark the account's volumes as destroyed
List<VolumeVO> volumes = _volumeDao.findDetachedByAccount(accountId);
for (VolumeVO volume : volumes) {
if (!volume.getState().equals(Volume.State.Destroy)) {
try {
_storageMgr.deleteVolume(volume.getId());
} catch (Exception ex) {
s_logger.warn("Failed to cleanup volumes as a part of account id=" + accountId + " cleanup due to Exception: ", ex);
accountCleanupNeeded = true;
}
}
}
// delete remote access vpns and associated users
List<RemoteAccessVpnVO> remoteAccessVpns = _remoteAccessVpnDao.findByAccount(accountId);
List<VpnUserVO> vpnUsers = _vpnUser.listByAccount(accountId);
for (VpnUserVO vpnUser : vpnUsers) {
_remoteAccessVpnMgr.removeVpnUser(accountId, vpnUser.getUsername());
}
try {
for (RemoteAccessVpnVO vpn : remoteAccessVpns) {
_remoteAccessVpnMgr.destroyRemoteAccessVpn(vpn.getServerAddressId());
}
} catch (ResourceUnavailableException ex) {
s_logger.warn("Failed to cleanup remote access vpn resources as a part of account id=" + accountId + " cleanup due to Exception: ", ex);
accountCleanupNeeded = true;
}
// Cleanup security groups
int numRemoved = _securityGroupDao.removeByAccountId(accountId);
s_logger.info("deleteAccount: Deleted " + numRemoved + " network groups for account " + accountId);
// Delete all the networks
boolean networksDeleted = true;
s_logger.debug("Deleting networks for account " + account.getId());
List<NetworkVO> networks = _networkDao.listByOwner(accountId);
if (networks != null) {
for (NetworkVO network : networks) {
ReservationContext context = new ReservationContextImpl(null, null, getActiveUser(callerUserId), account);
if (!_networkMgr.destroyNetwork(network.getId(), context)) {
s_logger.warn("Unable to destroy network " + network + " as a part of account id=" + accountId + " cleanup.");
accountCleanupNeeded = true;
networksDeleted = false;
} else {
s_logger.debug("Network " + network.getId() + " successfully deleted as a part of account id=" + accountId + " cleanup.");
}
}
}
//Delete all VPCs
boolean vpcsDeleted = true;
s_logger.debug("Deleting vpcs for account " + account.getId());
List<? extends Vpc> vpcs = _vpcMgr.getVpcsForAccount(account.getId());
for (Vpc vpc : vpcs) {
if (!_vpcMgr.destroyVpc(vpc)) {
s_logger.warn("Unable to destroy VPC " + vpc + " as a part of account id=" + accountId + " cleanup.");
accountCleanupNeeded = true;
vpcsDeleted = false;
} else {
s_logger.debug("VPC " + vpc.getId() + " successfully deleted as a part of account id=" + accountId + " cleanup.");
}
}
if (vpcsDeleted) {
// release ip addresses belonging to the account
List<? extends IpAddress> ipsToRelease = _ipAddressDao.listByAccount(accountId);
for (IpAddress ip : ipsToRelease) {
s_logger.debug("Releasing ip " + ip + " as a part of account id=" + accountId + " cleanup");
if (!_networkMgr.disassociatePublicIpAddress(ip.getId(), callerUserId, caller)) {
s_logger.warn("Failed to release ip address " + ip + " as a part of account id=" + accountId + " clenaup");
accountCleanupNeeded = true;
}
}
}
// Delete Site 2 Site VPN customer gateway
s_logger.debug("Deleting site-to-site VPN customer gateways for account " + accountId);
if (!_vpnMgr.deleteCustomerGatewayByAccount(accountId)) {
s_logger.warn("Fail to delete site-to-site VPN customer gateways for account " + accountId);
}
// delete account specific Virtual vlans (belong to system Public Network) - only when networks are cleaned
// up successfully
if (networksDeleted) {
if (!_configMgr.deleteAccountSpecificVirtualRanges(accountId)) {
accountCleanupNeeded = true;
} else {
s_logger.debug("Account specific Virtual IP ranges " + " are successfully deleted as a part of account id=" + accountId + " cleanup.");
}
}
return true;
} catch (Exception ex) {
s_logger.warn("Failed to cleanup account " + account + " due to ", ex);
accountCleanupNeeded = true;
return true;
} finally {
s_logger.info("Cleanup for account " + account.getId() + (accountCleanupNeeded ? " is needed." : " is not needed."));
if (accountCleanupNeeded) {
_accountDao.markForCleanup(accountId);
} else {
account.setNeedsCleanup(false);
_accountDao.update(accountId, account);
}
}
}
@Override
public boolean disableAccount(long accountId) throws ConcurrentOperationException, ResourceUnavailableException {
boolean success = false;
if (accountId <= 2) {
if (s_logger.isInfoEnabled()) {
s_logger.info("disableAccount -- invalid account id: " + accountId);
}
return false;
}
AccountVO account = _accountDao.findById(accountId);
if ((account == null) || (account.getState().equals(State.disabled) && !account.getNeedsCleanup())) {
success = true;
} else {
AccountVO acctForUpdate = _accountDao.createForUpdate();
acctForUpdate.setState(State.disabled);
success = _accountDao.update(Long.valueOf(accountId), acctForUpdate);
if (success) {
boolean disableAccountResult = false;
try {
disableAccountResult = doDisableAccount(accountId);
} finally {
if (!disableAccountResult) {
s_logger.warn("Failed to disable account " + account + " resources as a part of disableAccount call, marking the account for cleanup");
_accountDao.markForCleanup(accountId);
} else {
acctForUpdate = _accountDao.createForUpdate();
account.setNeedsCleanup(false);
_accountDao.update(accountId, account);
}
}
}
}
return success;
}
private boolean doDisableAccount(long accountId) throws ConcurrentOperationException, ResourceUnavailableException {
List<VMInstanceVO> vms = _vmDao.listByAccountId(accountId);
boolean success = true;
for (VMInstanceVO vm : vms) {
try {
try {
if (vm.getType() == Type.User) {
success = (success && _itMgr.advanceStop(_userVmDao.findById(vm.getId()), false, getSystemUser(), getSystemAccount()));
} else if (vm.getType() == Type.DomainRouter) {
success = (success && _itMgr.advanceStop(_routerDao.findById(vm.getId()), false, getSystemUser(), getSystemAccount()));
} else {
success = (success && _itMgr.advanceStop(vm, false, getSystemUser(), getSystemAccount()));
}
} catch (OperationTimedoutException ote) {
s_logger.warn("Operation for stopping vm timed out, unable to stop vm " + vm.getHostName(), ote);
success = false;
}
} catch (AgentUnavailableException aue) {
s_logger.warn("Agent running on host " + vm.getHostId() + " is unavailable, unable to stop vm " + vm.getHostName(), aue);
success = false;
}
}
return success;
}
// ///////////////////////////////////////////////////
// ////////////// API commands /////////////////////
// ///////////////////////////////////////////////////
@Override
@DB
@ActionEvent(eventType = EventTypes.EVENT_ACCOUNT_CREATE, eventDescription = "creating Account")
public UserAccount createUserAccount(String userName, String password, String firstName, String lastName, String email, String timezone, String accountName, short accountType, Long domainId, String networkDomain,
Map<String, String> details) {
if (accountName == null) {
accountName = userName;
}
if (domainId == null) {
domainId = DomainVO.ROOT_DOMAIN;
}
if (userName.isEmpty()) {
throw new InvalidParameterValueException("Username is empty");
}
if (firstName.isEmpty()) {
throw new InvalidParameterValueException("Firstname is empty");
}
if (lastName.isEmpty()) {
throw new InvalidParameterValueException("Lastname is empty");
}
// Validate domain
Domain domain = _domainMgr.getDomain(domainId);
if (domain == null) {
throw new InvalidParameterValueException("The domain " + domainId + " does not exist; unable to create account");
}
// Check permissions
checkAccess(UserContext.current().getCaller(), domain);
if (!_userAccountDao.validateUsernameInDomain(userName, domainId)) {
throw new InvalidParameterValueException("The user " + userName + " already exists in domain " + domainId);
}
if (networkDomain != null) {
if (!NetUtils.verifyDomainName(networkDomain)) {
throw new InvalidParameterValueException(
"Invalid network domain. Total length shouldn't exceed 190 chars. Each domain label must be between 1 and 63 characters long, can contain ASCII letters 'a' through 'z', the digits '0' through '9', "
+ "and the hyphen ('-'); can't start or end with \"-\"");
}
}
Transaction txn = Transaction.currentTxn();
txn.start();
// create account
Account account = createAccount(accountName, accountType, domainId, networkDomain, details);
long accountId = account.getId();
// create the first user for the account
UserVO user = createUser(accountId, userName, password, firstName, lastName, email, timezone);
if (accountType == Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN) {
// set registration token
byte[] bytes = (domainId + accountName + userName + System.currentTimeMillis()).getBytes();
String registrationToken = UUID.nameUUIDFromBytes(bytes).toString();
user.setRegistrationToken(registrationToken);
}
txn.commit();
return _userAccountDao.findById(user.getId());
}
@Override
public UserVO createUser(String userName, String password, String firstName, String lastName, String email, String timeZone, String accountName, Long domainId) {
// default domain to ROOT if not specified
if (domainId == null) {
domainId = Domain.ROOT_DOMAIN;
}
Domain domain = _domainMgr.getDomain(domainId);
if (domain == null) {
throw new CloudRuntimeException("The domain " + domainId + " does not exist; unable to create user");
} else if (domain.getState().equals(Domain.State.Inactive)) {
throw new CloudRuntimeException("The user cannot be created as domain " + domain.getName() + " is being deleted");
}
checkAccess(UserContext.current().getCaller(), domain);
Account account = _accountDao.findEnabledAccount(accountName, domainId);
if (account == null || account.getType() == Account.ACCOUNT_TYPE_PROJECT) {
throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain id=" + domainId + " to create user");
}
if (account.getId() == Account.ACCOUNT_ID_SYSTEM) {
throw new PermissionDeniedException("Account id : " + account.getId() + " is a system account, can't add a user to it");
}
if (!_userAccountDao.validateUsernameInDomain(userName, domainId)) {
throw new CloudRuntimeException("The user " + userName + " already exists in domain " + domainId);
}
UserVO user = createUser(account.getId(), userName, password, firstName, lastName, email, timeZone);
return user;
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_USER_UPDATE, eventDescription = "updating User")
public UserAccount updateUser(UpdateUserCmd cmd) {
Long id = cmd.getId();
String apiKey = cmd.getApiKey();
String firstName = cmd.getFirstname();
String email = cmd.getEmail();
String lastName = cmd.getLastname();
String password = cmd.getPassword();
String secretKey = cmd.getSecretKey();
String timeZone = cmd.getTimezone();
String userName = cmd.getUsername();
// Input validation
UserVO user = _userDao.getUser(id);
if (user == null) {
throw new InvalidParameterValueException("unable to find user by id");
}
if ((apiKey == null && secretKey != null) || (apiKey != null && secretKey == null)) {
throw new InvalidParameterValueException("Please provide an userApiKey/userSecretKey pair");
}
// If the account is an admin type, return an error. We do not allow this
Account account = _accountDao.findById(user.getAccountId());
// don't allow updating project account
if (account.getType() == Account.ACCOUNT_TYPE_PROJECT) {
throw new InvalidParameterValueException("unable to find user by id");
}
//don't allow updating system account
if (account != null && (account.getId() == Account.ACCOUNT_ID_SYSTEM)) {
throw new PermissionDeniedException("user id : " + id + " is system account, update is not allowed");
}
checkAccess(UserContext.current().getCaller(), null, true, account);
if (firstName != null) {
if (firstName.isEmpty()) {
throw new InvalidParameterValueException("Firstname is empty");
}
user.setFirstname(firstName);
}
if (lastName != null) {
if (lastName.isEmpty()) {
throw new InvalidParameterValueException("Lastname is empty");
}
user.setLastname(lastName);
}
if (userName != null) {
if (userName.isEmpty()) {
throw new InvalidParameterValueException("Username is empty");
}
// don't allow to have same user names in the same domain
List<UserVO> duplicatedUsers = _userDao.findUsersByName(userName);
for (UserVO duplicatedUser : duplicatedUsers) {
if (duplicatedUser.getId() != user.getId()) {
Account duplicatedUserAccount = _accountDao.findById(duplicatedUser.getAccountId());
if (duplicatedUserAccount.getDomainId() == account.getDomainId()) {
throw new InvalidParameterValueException("User with name " + userName + " already exists in domain " + duplicatedUserAccount.getDomainId());
}
}
}
user.setUsername(userName);
}
if (password != null) {
user.setPassword(password);
}
if (email != null) {
user.setEmail(email);
}
if (timeZone != null) {
user.setTimezone(timeZone);
}
if (apiKey != null) {
user.setApiKey(apiKey);
}
if (secretKey != null) {
user.setSecretKey(secretKey);
}
if (s_logger.isDebugEnabled()) {
s_logger.debug("updating user with id: " + id);
}
try {
// check if the apiKey and secretKey are globally unique
if (apiKey != null && secretKey != null) {
Pair<User, Account> apiKeyOwner = _accountDao.findUserAccountByApiKey(apiKey);
if (apiKeyOwner != null) {
User usr = apiKeyOwner.first();
if (usr.getId() != id) {
throw new InvalidParameterValueException("The api key:" + apiKey + " exists in the system for user id:" + id + " ,please provide a unique key");
} else {
// allow the updation to take place
}
}
}
_userDao.update(id, user);
} catch (Throwable th) {
s_logger.error("error updating user", th);
throw new CloudRuntimeException("Unable to update user " + id);
}
return _userAccountDao.findById(id);
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_USER_DISABLE, eventDescription = "disabling User", async = true)
public UserAccount disableUser(long userId) {
Account caller = UserContext.current().getCaller();
// Check if user exists in the system
User user = _userDao.findById(userId);
if (user == null || user.getRemoved() != null) {
throw new InvalidParameterValueException("Unable to find active user by id " + userId);
}
Account account = _accountDao.findById(user.getAccountId());
// don't allow disabling user belonging to project's account
if (account.getType() == Account.ACCOUNT_TYPE_PROJECT) {
throw new InvalidParameterValueException("Unable to find active user by id " + userId);
}
// If the user is a System user, return an error
if (account.getId() == Account.ACCOUNT_ID_SYSTEM) {
throw new InvalidParameterValueException("User id : " + userId + " is a system user, disabling is not allowed");
}
checkAccess(caller, null, true, account);
boolean success = doSetUserStatus(userId, State.disabled);
if (success) {
// user successfully disabled
return _userAccountDao.findById(userId);
} else {
throw new CloudRuntimeException("Unable to disable user " + userId);
}
}
@Override
@DB
@ActionEvent(eventType = EventTypes.EVENT_USER_ENABLE, eventDescription = "enabling User")
public UserAccount enableUser(long userId) {
Account caller = UserContext.current().getCaller();
// Check if user exists in the system
User user = _userDao.findById(userId);
if (user == null || user.getRemoved() != null) {
throw new InvalidParameterValueException("Unable to find active user by id " + userId);
}
Account account = _accountDao.findById(user.getAccountId());
if (account.getType() == Account.ACCOUNT_TYPE_PROJECT) {
throw new InvalidParameterValueException("Unable to find active user by id " + userId);
}
// If the user is a System user, return an error
if (account.getId() == Account.ACCOUNT_ID_SYSTEM) {
throw new InvalidParameterValueException("User id : " + userId + " is a system user, enabling is not allowed");
}
checkAccess(caller, null, true, account);
Transaction txn = Transaction.currentTxn();
txn.start();
boolean success = doSetUserStatus(userId, State.enabled);
// make sure the account is enabled too
success = success && enableAccount(user.getAccountId());
txn.commit();
if (success) {
// whenever the user is successfully enabled, reset the login attempts to zero
updateLoginAttempts(userId, 0, false);
return _userAccountDao.findById(userId);
} else {
throw new CloudRuntimeException("Unable to enable user " + userId);
}
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_USER_LOCK, eventDescription = "locking User")
public UserAccount lockUser(long userId) {
Account caller = UserContext.current().getCaller();
// Check if user with id exists in the system
User user = _userDao.findById(userId);
if (user == null || user.getRemoved() != null) {
throw new InvalidParameterValueException("Unable to find user by id");
}
Account account = _accountDao.findById(user.getAccountId());
// don't allow to lock user of the account of type Project
if (account.getType() == Account.ACCOUNT_TYPE_PROJECT) {
throw new InvalidParameterValueException("Unable to find user by id");
}
// If the user is a System user, return an error. We do not allow this
if (account.getId() == Account.ACCOUNT_ID_SYSTEM) {
throw new PermissionDeniedException("user id : " + userId + " is a system user, locking is not allowed");
}
checkAccess(caller, null, true, account);
// 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
// users
boolean success = true;
if (user.getState().equals(State.locked)) {
// already locked...no-op
return _userAccountDao.findById(userId);
} else if (user.getState().equals(State.enabled)) {
success = doSetUserStatus(user.getId(), State.locked);
boolean lockAccount = true;
List<UserVO> allUsersByAccount = _userDao.listByAccount(user.getAccountId());
for (UserVO oneUser : allUsersByAccount) {
if (oneUser.getState().equals(State.enabled)) {
lockAccount = false;
break;
}
}
if (lockAccount) {
success = (success && lockAccount(user.getAccountId()));
}
} else {
if (s_logger.isInfoEnabled()) {
s_logger.info("Attempting to lock a non-enabled user, current state is " + user.getState() + " (userId: " + user.getId() + "), locking failed.");
}
success = false;
}
if (success) {
return _userAccountDao.findById(userId);
} else {
throw new CloudRuntimeException("Unable to lock user " + userId);
}
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_ACCOUNT_DELETE, eventDescription = "deleting account", async = true)
// This method deletes the account
public boolean deleteUserAccount(long accountId) {
UserContext ctx = UserContext.current();
long callerUserId = ctx.getCallerUserId();
Account caller = ctx.getCaller();
// If the user is a System user, return an error. We do not allow this
AccountVO account = _accountDao.findById(accountId);
if (account.getRemoved() != null) {
s_logger.info("The account:" + account.getAccountName() + " is already removed");
return true;
}
// don't allow removing Project account
if (account == null || account.getType() == Account.ACCOUNT_TYPE_PROJECT) {
throw new InvalidParameterValueException("The specified account does not exist in the system");
}
checkAccess(caller, null, true, account);
if (account.getId() == Account.ACCOUNT_ID_SYSTEM) {
throw new PermissionDeniedException("Account id : " + accountId + " is a system account, delete is not allowed");
}
// Account that manages project(s) can't be removed
List<Long> managedProjectIds = _projectAccountDao.listAdministratedProjectIds(accountId);
if (!managedProjectIds.isEmpty()) {
StringBuilder projectIds = new StringBuilder();
for (Long projectId : managedProjectIds) {
projectIds.append(projectId + ", ");
}
throw new InvalidParameterValueException("The account id=" + accountId + " manages project(s) with ids " + projectIds + "and can't be removed");
}
return deleteAccount(account, callerUserId, caller);
}
@Override
public AccountVO enableAccount(String accountName, Long domainId, Long accountId) {
// Check if account exists
Account account = null;
if (accountId != null) {
account = _accountDao.findById(accountId);
} else {
account = _accountDao.findActiveAccount(accountName, domainId);
}
if (account == null || account.getType() == Account.ACCOUNT_TYPE_PROJECT) {
throw new InvalidParameterValueException("Unable to find account by accountId: " + accountId + " OR by name: " + accountName + " in domain " + domainId);
}
if (account.getId() == Account.ACCOUNT_ID_SYSTEM) {
throw new PermissionDeniedException("Account id : " + accountId + " is a system account, enable is not allowed");
}
// Check if user performing the action is allowed to modify this account
Account caller = UserContext.current().getCaller();
checkAccess(caller, null, true, account);
boolean success = enableAccount(account.getId());
if (success) {
return _accountDao.findById(account.getId());
} else {
throw new CloudRuntimeException("Unable to enable account by accountId: " + accountId + " OR by name: " + accountName + " in domain " + domainId);
}
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_ACCOUNT_DISABLE, eventDescription = "locking account", async = true)
public AccountVO lockAccount(String accountName, Long domainId, Long accountId) {
Account caller = UserContext.current().getCaller();
Account account = null;
if (accountId != null) {
account = _accountDao.findById(accountId);
} else {
account = _accountDao.findActiveAccount(accountName, domainId);
}
if (account == null || account.getType() == Account.ACCOUNT_TYPE_PROJECT) {
throw new InvalidParameterValueException("Unable to find active account by accountId: " + accountId + " OR by name: " + accountName + " in domain " + domainId);
}
if (account.getId() == Account.ACCOUNT_ID_SYSTEM) {
throw new PermissionDeniedException("Account id : " + accountId + " is a system account, lock is not allowed");
}
checkAccess(caller, null, true, account);
if (lockAccount(account.getId())) {
return _accountDao.findById(account.getId());
} else {
throw new CloudRuntimeException("Unable to lock account by accountId: " + accountId + " OR by name: " + accountName + " in domain " + domainId);
}
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_ACCOUNT_DISABLE, eventDescription = "disabling account", async = true)
public AccountVO disableAccount(String accountName, Long domainId, Long accountId) throws ConcurrentOperationException, ResourceUnavailableException {
Account caller = UserContext.current().getCaller();
Account account = null;
if (accountId != null) {
account = _accountDao.findById(accountId);
} else {
account = _accountDao.findActiveAccount(accountName, domainId);
}
if (account == null || account.getType() == Account.ACCOUNT_TYPE_PROJECT) {
throw new InvalidParameterValueException("Unable to find account by accountId: " + accountId + " OR by name: " + accountName + " in domain " + domainId);
}
if (account.getId() == Account.ACCOUNT_ID_SYSTEM) {
throw new PermissionDeniedException("Account id : " + accountId + " is a system account, disable is not allowed");
}
checkAccess(caller, null, true, account);
if (disableAccount(account.getId())) {
return _accountDao.findById(account.getId());
} else {
throw new CloudRuntimeException("Unable to update account by accountId: " + accountId + " OR by name: " + accountName + " in domain " + domainId);
}
}
@Override
@DB
public AccountVO updateAccount(UpdateAccountCmd cmd) {
Long accountId = cmd.getId();
Long domainId = cmd.getDomainId();
String accountName = cmd.getAccountName();
String newAccountName = cmd.getNewName();
String networkDomain = cmd.getNetworkDomain();
Map<String, String> details = cmd.getDetails();
boolean success = false;
Account account = null;
if (accountId != null) {
account = _accountDao.findById(accountId);
} else {
account = _accountDao.findEnabledAccount(accountName, domainId);
}
// Check if account exists
if (account == null || account.getType() == Account.ACCOUNT_TYPE_PROJECT) {
s_logger.error("Unable to find account by accountId: " + accountId + " OR by name: " + accountName + " in domain " + domainId);
throw new InvalidParameterValueException("Unable to find account by accountId: " + accountId + " OR by name: " + accountName + " in domain " + domainId);
}
// Don't allow to modify system account
if (account.getId() == Account.ACCOUNT_ID_SYSTEM) {
throw new InvalidParameterValueException("Can not modify system account");
}
// Check if user performing the action is allowed to modify this account
checkAccess(UserContext.current().getCaller(), _domainMgr.getDomain(account.getDomainId()));
// check if the given account name is unique in this domain for updating
Account duplicateAcccount = _accountDao.findActiveAccount(newAccountName, domainId);
if (duplicateAcccount != null && duplicateAcccount.getId() != account.getId()) {// allow
// same
// account
// to
// update
// itself
throw new InvalidParameterValueException("There already exists an account with the name:" + newAccountName + " in the domain:" + domainId + " with existing account id:"
+ duplicateAcccount.getId());
}
if (networkDomain != null && !networkDomain.isEmpty()) {
if (!NetUtils.verifyDomainName(networkDomain)) {
throw new InvalidParameterValueException(
"Invalid network domain. Total length shouldn't exceed 190 chars. Each domain label must be between 1 and 63 characters long, can contain ASCII letters 'a' through 'z', the digits '0' through '9', "
+ "and the hyphen ('-'); can't start or end with \"-\"");
}
}
AccountVO acctForUpdate = _accountDao.findById(account.getId());
acctForUpdate.setAccountName(newAccountName);
if (networkDomain != null) {
if (networkDomain.isEmpty()) {
acctForUpdate.setNetworkDomain(null);
} else {
acctForUpdate.setNetworkDomain(networkDomain);
}
}
Transaction txn = Transaction.currentTxn();
txn.start();
success = _accountDao.update(account.getId(), acctForUpdate);
if (details != null && success) {
_accountDetailsDao.update(account.getId(), details);
}
txn.commit();
if (success) {
return _accountDao.findById(account.getId());
} else {
throw new CloudRuntimeException("Unable to update account by accountId: " + accountId + " OR by name: " + accountName + " in domain " + domainId);
}
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_USER_DELETE, eventDescription = "deleting User")
public boolean deleteUser(DeleteUserCmd deleteUserCmd) {
long id = deleteUserCmd.getId();
UserVO user = _userDao.findById(id);
if (user == null) {
throw new InvalidParameterValueException("The specified user doesn't exist in the system");
}
Account account = _accountDao.findById(user.getAccountId());
// don't allow to delete the user from the account of type Project
if (account.getType() == Account.ACCOUNT_TYPE_PROJECT) {
throw new InvalidParameterValueException("The specified user doesn't exist in the system");
}
if (account.getId() == Account.ACCOUNT_ID_SYSTEM) {
throw new InvalidParameterValueException("Account id : " + user.getAccountId() + " is a system account, delete for user associated with this account is not allowed");
}
checkAccess(UserContext.current().getCaller(), null, true, account);
return _userDao.remove(id);
}
public class ResourceCountCalculateTask implements Runnable {
@Override
public void run() {
}
}
protected class AccountCleanupTask implements Runnable {
@Override
public void run() {
try {
GlobalLock lock = GlobalLock.getInternLock("AccountCleanup");
if (lock == null) {
s_logger.debug("Couldn't get the global lock");
return;
}
if (!lock.lock(30)) {
s_logger.debug("Couldn't lock the db");
return;
}
Transaction txn = null;
try {
txn = Transaction.open(Transaction.CLOUD_DB);
// Cleanup removed accounts
List<AccountVO> removedAccounts = _accountDao.findCleanupsForRemovedAccounts(null);
s_logger.info("Found " + removedAccounts.size() + " removed accounts to cleanup");
for (AccountVO account : removedAccounts) {
s_logger.debug("Cleaning up " + account.getId());
cleanupAccount(account, getSystemUser().getId(), getSystemAccount());
}
// cleanup disabled accounts
List<AccountVO> disabledAccounts = _accountDao.findCleanupsForDisabledAccounts();
s_logger.info("Found " + disabledAccounts.size() + " disabled accounts to cleanup");
for (AccountVO account : disabledAccounts) {
s_logger.debug("Disabling account " + account.getId());
try {
disableAccount(account.getId());
} catch (Exception e) {
s_logger.error("Skipping due to error on account " + account.getId(), e);
}
}
// cleanup inactive domains
List<? extends Domain> inactiveDomains = _domainMgr.findInactiveDomains();
s_logger.info("Found " + inactiveDomains.size() + " inactive domains to cleanup");
for (Domain inactiveDomain : inactiveDomains) {
long domainId = inactiveDomain.getId();
try {
List<AccountVO> accountsForCleanupInDomain = _accountDao.findCleanupsForRemovedAccounts(domainId);
if (accountsForCleanupInDomain.isEmpty()) {
s_logger.debug("Removing inactive domain id=" + domainId);
_domainMgr.removeDomain(domainId);
} else {
s_logger.debug("Can't remove inactive domain id=" + domainId + " as it has accounts that need cleanup");
}
} catch (Exception e) {
s_logger.error("Skipping due to error on domain " + domainId, e);
}
}
// cleanup inactive projects
List<ProjectVO> inactiveProjects = _projectDao.listByState(Project.State.Disabled);
s_logger.info("Found " + inactiveProjects.size() + " disabled projects to cleanup");
for (ProjectVO project : inactiveProjects) {
try {
Account projectAccount = getAccount(project.getProjectAccountId());
if (projectAccount == null) {
s_logger.debug("Removing inactive project id=" + project.getId());
_projectMgr.deleteProject(UserContext.current().getCaller(), UserContext.current().getCallerUserId(), project);
} else {
s_logger.debug("Can't remove disabled project " + project + " as it has non removed account id=" + project.getId());
}
} catch (Exception e) {
s_logger.error("Skipping due to error on project " + project, e);
}
}
} catch (Exception e) {
s_logger.error("Exception ", e);
} finally {
if (txn != null) {
txn.close();
}
lock.unlock();
}
} catch (Exception e) {
s_logger.error("Exception ", e);
}
}
}
@Override
public Account finalizeOwner(Account caller, String accountName, Long domainId, Long projectId) {
// don't default the owner to the system account
if (caller.getId() == Account.ACCOUNT_ID_SYSTEM && ((accountName == null || domainId == null) && projectId == null)) {
throw new InvalidParameterValueException("Account and domainId are needed for resource creation");
}
// projectId and account/domainId can't be specified together
if ((accountName != null && domainId != null) && projectId != null) {
throw new InvalidParameterValueException("ProjectId and account/domainId can't be specified together");
}
if (projectId != null) {
Project project = _projectMgr.getProject(projectId);
if (project == null) {
throw new InvalidParameterValueException("Unable to find project by id=" + projectId);
}
if (!_projectMgr.canAccessProjectAccount(caller, project.getProjectAccountId())) {
throw new PermissionDeniedException("Account " + caller + " is unauthorised to use project id=" + projectId);
}
return getAccount(project.getProjectAccountId());
}
if (isAdmin(caller.getType()) && accountName != null && domainId != null) {
Domain domain = _domainMgr.getDomain(domainId);
if (domain == null) {
throw new InvalidParameterValueException("Unable to find the domain by id=" + domainId);
}
Account owner = _accountDao.findActiveAccount(accountName, domainId);
if (owner == null) {
throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId);
}
checkAccess(caller, domain);
return owner;
} else if (!isAdmin(caller.getType()) && accountName != null && domainId != null) {
if (!accountName.equals(caller.getAccountName()) || domainId.longValue() != caller.getDomainId()) {
throw new PermissionDeniedException("Can't create/list resources for account " + accountName + " in domain " + domainId + ", permission denied");
} else {
return caller;
}
} else {
if ((accountName == null && domainId != null) || (accountName != null && domainId == null)) {
throw new InvalidParameterValueException("AccountName and domainId must be specified together");
}
// regular user can't create/list resources for other people
return caller;
}
}
@Override
public Account getActiveAccountByName(String accountName, Long domainId) {
if (accountName == null || domainId == null) {
throw new InvalidParameterValueException("Both accountName and domainId are required for finding active account in the system");
} else {
return _accountDao.findActiveAccount(accountName, domainId);
}
}
@Override
public Account getActiveAccountById(Long accountId) {
if (accountId == null) {
throw new InvalidParameterValueException("AccountId is required by account search");
} else {
return _accountDao.findById(accountId);
}
}
@Override
public Account getAccount(Long accountId) {
if (accountId == null) {
throw new InvalidParameterValueException("AccountId is required by account search");
} else {
return _accountDao.findByIdIncludingRemoved(accountId);
}
}
@Override
public User getActiveUser(long userId) {
return _userDao.findById(userId);
}
@Override
public User getUserIncludingRemoved(long userId) {
return _userDao.findByIdIncludingRemoved(userId);
}
@Override
public Pair<List<Long>, Long> finalizeAccountDomainForList(Account caller, String accountName, Long domainId, Long projectId) {
List<Long> permittedAccounts = new ArrayList<Long>();
if (isAdmin(caller.getType())) {
if (domainId == null && accountName != null) {
throw new InvalidParameterValueException("accountName and domainId might be specified together");
} else if (domainId != null) {
Domain domain = _domainMgr.getDomain(domainId);
if (domain == null) {
throw new InvalidParameterValueException("Unable to find the domain by id=" + domainId);
}
checkAccess(caller, domain);
if (accountName != null) {
Account owner = getActiveAccountByName(accountName, domainId);
if (owner == null) {
throw new InvalidParameterValueException("Unable to find account with name " + accountName + " in domain id=" + domainId);
}
permittedAccounts.add(owner.getId());
}
}
} else if (accountName != null && domainId != null) {
if (!accountName.equals(caller.getAccountName()) || domainId.longValue() != caller.getDomainId()) {
throw new PermissionDeniedException("Can't list port forwarding rules for account " + accountName + " in domain " + domainId + ", permission denied");
}
permittedAccounts.add(getActiveAccountByName(accountName, domainId).getId());
} else {
permittedAccounts.add(caller.getAccountId());
}
if (domainId == null && caller.getType() == Account.ACCOUNT_TYPE_DOMAIN_ADMIN) {
domainId = caller.getDomainId();
}
// set project information
if (projectId != null) {
if (projectId == -1) {
permittedAccounts.addAll(_projectMgr.listPermittedProjectAccounts(caller.getId()));
} else {
permittedAccounts.clear();
Project project = _projectMgr.getProject(projectId);
if (project == null) {
throw new InvalidParameterValueException("Unable to find project by id " + projectId);
}
if (!_projectMgr.canAccessProjectAccount(caller, project.getProjectAccountId())) {
throw new InvalidParameterValueException("Account " + caller + " can't access project id=" + projectId);
}
permittedAccounts.add(project.getProjectAccountId());
}
}
return new Pair<List<Long>, Long>(permittedAccounts, domainId);
}
@Override
public User getActiveUserByRegistrationToken(String registrationToken) {
return _userDao.findUserByRegistrationToken(registrationToken);
}
@Override
public void markUserRegistered(long userId) {
UserVO userForUpdate = _userDao.createForUpdate();
userForUpdate.setRegistered(true);
_userDao.update(Long.valueOf(userId), userForUpdate);
}
@Override
@DB
public Account createAccount(String accountName, short accountType, Long domainId, String networkDomain, Map details) {
// Validate domain
Domain domain = _domainMgr.getDomain(domainId);
if (domain == null) {
throw new InvalidParameterValueException("The domain " + domainId + " does not exist; unable to create account");
}
if (domain.getState().equals(Domain.State.Inactive)) {
throw new CloudRuntimeException("The account cannot be created as domain " + domain.getName() + " is being deleted");
}
if ((domainId != DomainVO.ROOT_DOMAIN) && (accountType == Account.ACCOUNT_TYPE_ADMIN)) {
throw new InvalidParameterValueException("Invalid account type " + accountType + " given for an account in domain " + domainId + "; unable to create user.");
}
// Validate account/user/domain settings
if (_accountDao.findActiveAccount(accountName, domainId) != null) {
throw new InvalidParameterValueException("The specified account: " + accountName + " already exists");
}
if (networkDomain != null) {
if (!NetUtils.verifyDomainName(networkDomain)) {
throw new InvalidParameterValueException(
"Invalid network domain. Total length shouldn't exceed 190 chars. Each domain label must be between 1 and 63 characters long, can contain ASCII letters 'a' through 'z', the digits '0' through '9', "
+ "and the hyphen ('-'); can't start or end with \"-\"");
}
}
// Verify account type
if ((accountType < Account.ACCOUNT_TYPE_NORMAL) || (accountType > Account.ACCOUNT_TYPE_PROJECT)) {
throw new InvalidParameterValueException("Invalid account type " + accountType + " given; unable to create user");
}
if (accountType == Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN) {
List<DataCenterVO> dc = _dcDao.findZonesByDomainId(domainId);
if (dc.isEmpty()) {
throw new InvalidParameterValueException("The account cannot be created as domain " + domain.getName() + " is not associated with any private Zone");
}
}
// Create the account
Transaction txn = Transaction.currentTxn();
txn.start();
Account account = _accountDao.persist(new AccountVO(accountName, domainId, networkDomain, accountType));
if (account == null) {
throw new CloudRuntimeException("Failed to create account name " + accountName + " in domain id=" + domainId);
}
Long accountId = account.getId();
if (details != null) {
_accountDetailsDao.persist(accountId, details);
}
// Create resource count records for the account
_resourceCountDao.createResourceCounts(accountId, ResourceLimit.ResourceOwnerType.Account);
// Create default security group
_networkGroupMgr.createDefaultSecurityGroup(accountId);
txn.commit();
return account;
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_USER_CREATE, eventDescription = "creating User")
public UserVO createUser(long accountId, String userName, String password, String firstName, String lastName, String email, String timezone) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Creating user: " + userName + ", accountId: " + accountId + " timezone:" + timezone);
}
UserVO user = _userDao.persist(new UserVO(accountId, userName, password, firstName, lastName, email, timezone));
return user;
}
@Override
public void logoutUser(Long userId) {
UserAccount userAcct = _userAccountDao.findById(userId);
if (userAcct != null) {
EventUtils.saveEvent(userId, userAcct.getAccountId(), userAcct.getDomainId(), EventTypes.EVENT_USER_LOGOUT, "user has logged out");
} // else log some kind of error event? This likely means the user doesn't exist, or has been deleted...
}
@Override
public UserAccount getUserAccount(String username, Long domainId) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Retrieiving user: " + username + " in domain " + domainId);
}
UserAccount userAccount = _userAccountDao.getUserAccount(username, domainId);
if (userAccount == null) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Unable to find user with name " + username + " in domain " + domainId);
}
return null;
}
return userAccount;
}
@Override
public UserAccount authenticateUser(String username, String password, Long domainId, String loginIpAddress, Map<String, Object[]> requestParameters) {
UserAccount user = null;
if (password != null) {
user = getUserAccount(username, password, domainId, requestParameters);
} else {
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;
}
long tolerance = Long.parseLong(singleSignOnTolerance);
String signature = null;
long timestamp = 0L;
String unsignedRequest = null;
// - 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<String>();
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 (s_logger.isDebugEnabled()) {
s_logger.debug("Expired timestamp passed in to login, current time = " + currentTime + ", timestamp = " + timestamp);
}
return null;
}
} catch (NumberFormatException nfe) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Invalid timestamp passed in to login: " + timestampStr);
}
return null;
}
}
if (unsignedRequest == null) {
unsignedRequest = paramName + "=" + URLEncoder.encode(paramValue, "UTF-8").replaceAll("\\+", "%20");
} else {
unsignedRequest = unsignedRequest + "&" + paramName + "=" + URLEncoder.encode(paramValue, "UTF-8").replaceAll("\\+", "%20");
}
}
}
if ((signature == null) || (timestamp == 0L)) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Missing parameters in login request, signature = " + signature + ", timestamp = " + timestamp);
}
return null;
}
unsignedRequest = unsignedRequest.toLowerCase();
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 = signature.equals(computedSignature);
if (!equalSig) {
s_logger.info("User signature: " + signature + " is not equaled to computed signature: " + computedSignature);
} else {
user = _userAccountDao.getUserAccount(username, domainId);
}
} catch (Exception ex) {
s_logger.error("Exception authenticating user", ex);
return null;
}
}
if (user != null) {
//don't allow to authenticate system user
if (user.getId() == User.UID_SYSTEM) {
s_logger.error("Failed to authenticate user: " + username + " in domain " + domainId);
return null;
}
if (s_logger.isDebugEnabled()) {
s_logger.debug("User: " + username + " in domain " + domainId + " has successfully logged in");
}
if (NetUtils.isValidIp(loginIpAddress)) {
EventUtils.saveEvent(user.getId(), user.getAccountId(), user.getDomainId(), EventTypes.EVENT_USER_LOGIN,
"user has logged in from IP Address " + loginIpAddress);
} else {
EventUtils.saveEvent(user.getId(), user.getAccountId(), user.getDomainId(), EventTypes.EVENT_USER_LOGIN,
"user has logged in. The IP Address cannot be determined");
}
return user;
} else {
if (s_logger.isDebugEnabled()) {
s_logger.debug("User: " + username + " in domain " + domainId + " has failed to log in");
}
return null;
}
}
private UserAccount getUserAccount(String username, String password, Long domainId, Map<String, Object[]> requestParameters) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Attempting to log in user: " + username + " in domain " + domainId);
}
boolean authenticated = false;
for (Enumeration<UserAuthenticator> en = _userAuthenticators.enumeration(); en.hasMoreElements();) {
UserAuthenticator authenticator = en.nextElement();
if (authenticator.authenticate(username, password, domainId, requestParameters)) {
authenticated = true;
break;
}
}
if (authenticated) {
UserAccount userAccount = _userAccountDao.getUserAccount(username, domainId);
if (userAccount == null) {
s_logger.warn("Unable to find an authenticated user with username " + username + " in domain " + domainId);
return null;
}
Domain domain = _domainMgr.getDomain(domainId);
String domainName = null;
if (domain != null) {
domainName = domain.getName();
}
if (!userAccount.getState().equalsIgnoreCase(Account.State.enabled.toString()) || !userAccount.getAccountState().equalsIgnoreCase(Account.State.enabled.toString())) {
if (s_logger.isInfoEnabled()) {
s_logger.info("User " + username + " in domain " + domainName + " is disabled/locked (or account is disabled/locked)");
}
throw new CloudAuthenticationException("User " + username + " in domain " + domainName + " is disabled/locked (or account is disabled/locked)");
// return null;
}
// Whenever the user is able to log in successfully, reset the login attempts to zero
if(!isInternalAccount(userAccount.getType()))
updateLoginAttempts(userAccount.getId(), 0, false);
return userAccount;
} else {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Unable to authenticate user with username " + username + " in domain " + domainId);
}
UserAccount userAccount = _userAccountDao.getUserAccount(username, domainId);
if (userAccount != null) {
if (userAccount.getState().equalsIgnoreCase(Account.State.enabled.toString())) {
if (!isInternalAccount(userAccount.getType())) {
//Internal accounts are not disabled
int attemptsMade = userAccount.getLoginAttempts() + 1;
if (attemptsMade < _allowedLoginAttempts) {
updateLoginAttempts(userAccount.getId(), attemptsMade, false);
s_logger.warn("Login attempt failed. You have " + ( _allowedLoginAttempts - attemptsMade ) + " attempt(s) remaining");
} else {
updateLoginAttempts(userAccount.getId(), _allowedLoginAttempts, true);
s_logger.warn("User " + userAccount.getUsername() + " has been disabled due to multiple failed login attempts." +
" Please contact admin.");
}
}
} else {
s_logger.info("User " + userAccount.getUsername() + " is disabled/locked");
}
} else {
s_logger.warn("Authentication failure: No user with name " + username + " for domainId " + domainId);
}
return null;
}
}
@Override
public Pair<User, Account> findUserByApiKey(String apiKey) {
return _accountDao.findUserAccountByApiKey(apiKey);
}
@Override @DB
public String[] createApiKeyAndSecretKey(RegisterCmd cmd) {
Long userId = cmd.getId();
User user = getUserIncludingRemoved(userId);
if (user == null) {
throw new InvalidParameterValueException("unable to find user by id");
}
//don't allow updating system user
if (user.getId() == User.UID_SYSTEM) {
throw new PermissionDeniedException("user id : " + user.getId() + " is system account, update is not allowed");
}
// generate both an api key and a secret key, update the user table with the keys, return the keys to the user
String[] keys = new String[2];
Transaction txn = Transaction.currentTxn();
txn.start();
keys[0] = createUserApiKey(userId);
keys[1] = createUserSecretKey(userId);
txn.commit();
return keys;
}
private String createUserApiKey(long userId) {
try {
UserVO updatedUser = _userDao.createForUpdate();
String encodedKey = null;
Pair<User, Account> userAcct = null;
int retryLimit = 10;
do {
// FIXME: what algorithm should we use for API keys?
KeyGenerator generator = KeyGenerator.getInstance("HmacSHA1");
SecretKey key = generator.generateKey();
encodedKey = Base64.encodeBase64URLSafeString(key.getEncoded());
userAcct = _accountDao.findUserAccountByApiKey(encodedKey);
retryLimit--;
} while ((userAcct != null) && (retryLimit >= 0));
if (userAcct != null) {
return null;
}
updatedUser.setApiKey(encodedKey);
_userDao.update(userId, updatedUser);
return encodedKey;
} catch (NoSuchAlgorithmException ex) {
s_logger.error("error generating secret key for user id=" + userId, ex);
}
return null;
}
private String createUserSecretKey(long userId) {
try {
UserVO updatedUser = _userDao.createForUpdate();
String encodedKey = null;
int retryLimit = 10;
UserVO userBySecretKey = null;
do {
KeyGenerator generator = KeyGenerator.getInstance("HmacSHA1");
SecretKey key = generator.generateKey();
encodedKey = Base64.encodeBase64URLSafeString(key.getEncoded());
userBySecretKey = _userDao.findUserBySecretKey(encodedKey);
retryLimit--;
} while ((userBySecretKey != null) && (retryLimit >= 0));
if (userBySecretKey != null) {
return null;
}
updatedUser.setSecretKey(encodedKey);
_userDao.update(userId, updatedUser);
return encodedKey;
} catch (NoSuchAlgorithmException ex) {
s_logger.error("error generating secret key for user id=" + userId, ex);
}
return null;
}
@Override
public List<AccountVO> searchForAccounts(ListAccountsCmd cmd) {
Account caller = UserContext.current().getCaller();
Long domainId = cmd.getDomainId();
Long accountId = cmd.getId();
String accountName = cmd.getSearchName();
boolean isRecursive = cmd.isRecursive();
boolean listAll = cmd.listAll();
Boolean listForDomain = false;
if (accountId != null) {
Account account = _accountDao.findById(accountId);
if (account == null || account.getId() == Account.ACCOUNT_ID_SYSTEM) {
throw new InvalidParameterValueException("Unable to find account by id " + accountId);
}
checkAccess(caller, null, true, account);
}
if (domainId != null) {
Domain domain = _domainMgr.getDomain(domainId);
if (domain == null) {
throw new InvalidParameterValueException("Domain id=" + domainId + " doesn't exist");
}
checkAccess(caller, domain);
if (accountName != null) {
Account account = _accountDao.findActiveAccount(accountName, domainId);
if (account == null || account.getId() == Account.ACCOUNT_ID_SYSTEM) {
throw new InvalidParameterValueException("Unable to find account by name " + accountName + " in domain " + domainId);
}
checkAccess(caller, null, true, account);
}
}
if (accountId == null) {
if (isAdmin(caller.getType()) && listAll && domainId == null) {
listForDomain = true;
isRecursive = true;
if (domainId == null) {
domainId = caller.getDomainId();
}
} else if (domainId != null) {
listForDomain = true;
} else {
accountId = caller.getAccountId();
}
}
Filter searchFilter = new Filter(AccountVO.class, "id", true, cmd.getStartIndex(), cmd.getPageSizeVal());
Object type = cmd.getAccountType();
Object state = cmd.getState();
Object isCleanupRequired = cmd.isCleanupRequired();
Object keyword = cmd.getKeyword();
SearchBuilder<AccountVO> sb = _accountDao.createSearchBuilder();
sb.and("accountName", sb.entity().getAccountName(), SearchCriteria.Op.EQ);
sb.and("domainId", sb.entity().getDomainId(), SearchCriteria.Op.EQ);
sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ);
sb.and("type", sb.entity().getType(), SearchCriteria.Op.EQ);
sb.and("state", sb.entity().getState(), SearchCriteria.Op.EQ);
sb.and("needsCleanup", sb.entity().getNeedsCleanup(), SearchCriteria.Op.EQ);
sb.and("typeNEQ", sb.entity().getType(), SearchCriteria.Op.NEQ);
sb.and("idNEQ", sb.entity().getId(), SearchCriteria.Op.NEQ);
if (listForDomain && isRecursive) {
SearchBuilder<DomainVO> domainSearch = _domainDao.createSearchBuilder();
domainSearch.and("path", domainSearch.entity().getPath(), SearchCriteria.Op.LIKE);
sb.join("domainSearch", domainSearch, sb.entity().getDomainId(), domainSearch.entity().getId(), JoinBuilder.JoinType.INNER);
}
SearchCriteria<AccountVO> sc = sb.create();
sc.setParameters("idNEQ", Account.ACCOUNT_ID_SYSTEM);
if (keyword != null) {
SearchCriteria<AccountVO> ssc = _accountDao.createSearchCriteria();
ssc.addOr("accountName", SearchCriteria.Op.LIKE, "%" + keyword + "%");
ssc.addOr("state", SearchCriteria.Op.LIKE, "%" + keyword + "%");
sc.addAnd("accountName", SearchCriteria.Op.SC, ssc);
}
if (type != null) {
sc.setParameters("type", type);
}
if (state != null) {
sc.setParameters("state", state);
}
if (isCleanupRequired != null) {
sc.setParameters("needsCleanup", isCleanupRequired);
}
if (accountName != null) {
sc.setParameters("accountName", accountName);
}
// don't return account of type project to the end user
sc.setParameters("typeNEQ", 5);
if (accountId != null) {
sc.setParameters("id", accountId);
}
if (listForDomain) {
DomainVO domain = _domainDao.findById(domainId);
if (isRecursive) {
sc.setJoinParameters("domainSearch", "path", domain.getPath() + "%");
} else {
sc.setParameters("domainId", domainId);
}
}
return _accountDao.search(sc, searchFilter);
}
@Override
public List<UserAccountVO> searchForUsers(ListUsersCmd cmd) throws PermissionDeniedException {
Account caller = UserContext.current().getCaller();
Long domainId = cmd.getDomainId();
if (domainId != null) {
Domain domain = _domainDao.findById(domainId);
if (domain == null) {
throw new InvalidParameterValueException("Unable to find domain by id=" + domainId);
}
checkAccess(caller, domain);
} else {
// default domainId to the caller's domain
domainId = caller.getDomainId();
}
Filter searchFilter = new Filter(UserAccountVO.class, "id", true, cmd.getStartIndex(), cmd.getPageSizeVal());
Long id = cmd.getId();
Object username = cmd.getUsername();
Object type = cmd.getAccountType();
Object accountName = cmd.getAccountName();
Object state = cmd.getState();
Object keyword = cmd.getKeyword();
SearchBuilder<UserAccountVO> sb = _userAccountDao.createSearchBuilder();
sb.and("username", sb.entity().getUsername(), SearchCriteria.Op.LIKE);
if (id != null && id == 1) {
// system user should NOT be searchable
List<UserAccountVO> emptyList = new ArrayList<UserAccountVO>();
return emptyList;
} else if (id != null) {
sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ);
} else {
// this condition is used to exclude system user from the search results
sb.and("id", sb.entity().getId(), SearchCriteria.Op.NEQ);
}
sb.and("type", sb.entity().getType(), SearchCriteria.Op.EQ);
sb.and("domainId", sb.entity().getDomainId(), SearchCriteria.Op.EQ);
sb.and("accountName", sb.entity().getAccountName(), SearchCriteria.Op.EQ);
sb.and("state", sb.entity().getState(), SearchCriteria.Op.EQ);
if ((accountName == null) && (domainId != null)) {
SearchBuilder<DomainVO> domainSearch = _domainDao.createSearchBuilder();
domainSearch.and("path", domainSearch.entity().getPath(), SearchCriteria.Op.LIKE);
sb.join("domainSearch", domainSearch, sb.entity().getDomainId(), domainSearch.entity().getId(), JoinBuilder.JoinType.INNER);
}
SearchCriteria<UserAccountVO> sc = sb.create();
if (keyword != null) {
SearchCriteria<UserAccountVO> ssc = _userAccountDao.createSearchCriteria();
ssc.addOr("username", SearchCriteria.Op.LIKE, "%" + keyword + "%");
ssc.addOr("firstname", SearchCriteria.Op.LIKE, "%" + keyword + "%");
ssc.addOr("lastname", SearchCriteria.Op.LIKE, "%" + keyword + "%");
ssc.addOr("email", SearchCriteria.Op.LIKE, "%" + keyword + "%");
ssc.addOr("state", SearchCriteria.Op.LIKE, "%" + keyword + "%");
ssc.addOr("accountName", SearchCriteria.Op.LIKE, "%" + keyword + "%");
ssc.addOr("type", SearchCriteria.Op.LIKE, "%" + keyword + "%");
ssc.addOr("accountState", SearchCriteria.Op.LIKE, "%" + keyword + "%");
sc.addAnd("username", SearchCriteria.Op.SC, ssc);
}
if (username != null) {
sc.setParameters("username", username);
}
if (id != null) {
sc.setParameters("id", id);
} else {
// Don't return system user, search builder with NEQ
sc.setParameters("id", 1);
}
if (type != null) {
sc.setParameters("type", type);
}
if (accountName != null) {
sc.setParameters("accountName", accountName);
if (domainId != null) {
sc.setParameters("domainId", domainId);
}
} else if (domainId != null) {
DomainVO domainVO = _domainDao.findById(domainId);
sc.setJoinParameters("domainSearch", "path", domainVO.getPath() + "%");
}
if (state != null) {
sc.setParameters("state", state);
}
return _userAccountDao.search(sc, searchFilter);
}
@Override
public void buildACLSearchBuilder(SearchBuilder<? extends ControlledEntity> sb,
Long domainId, boolean isRecursive, List<Long> permittedAccounts, ListProjectResourcesCriteria listProjectResourcesCriteria) {
if (sb.entity() instanceof IPAddressVO) {
sb.and("accountIdIN", ((IPAddressVO) sb.entity()).getAllocatedToAccountId(), SearchCriteria.Op.IN);
sb.and("domainId", ((IPAddressVO) sb.entity()).getAllocatedInDomainId(), SearchCriteria.Op.EQ);
} else if (sb.entity() instanceof ProjectInvitationVO) {
sb.and("accountIdIN", ((ProjectInvitationVO) sb.entity()).getForAccountId(), SearchCriteria.Op.IN);
sb.and("domainId", ((ProjectInvitationVO) sb.entity()).getInDomainId(), SearchCriteria.Op.EQ);
} else {
sb.and("accountIdIN", sb.entity().getAccountId(), SearchCriteria.Op.IN);
sb.and("domainId", sb.entity().getDomainId(), SearchCriteria.Op.EQ);
}
if (((permittedAccounts.isEmpty()) && (domainId != null) && isRecursive)) {
// if accountId isn't specified, we can do a domain match for the admin case if isRecursive is true
SearchBuilder<DomainVO> domainSearch = _domainDao.createSearchBuilder();
domainSearch.and("path", domainSearch.entity().getPath(), SearchCriteria.Op.LIKE);
if (sb.entity() instanceof IPAddressVO) {
sb.join("domainSearch", domainSearch, ((IPAddressVO) sb.entity()).getAllocatedInDomainId(), domainSearch.entity().getId(), JoinBuilder.JoinType.INNER);
} else if (sb.entity() instanceof ProjectInvitationVO) {
sb.join("domainSearch", domainSearch, ((ProjectInvitationVO) sb.entity()).getInDomainId(), domainSearch.entity().getId(), JoinBuilder.JoinType.INNER);
} else {
sb.join("domainSearch", domainSearch, sb.entity().getDomainId(), domainSearch.entity().getId(), JoinBuilder.JoinType.INNER);
}
}
if (listProjectResourcesCriteria != null) {
SearchBuilder<AccountVO> accountSearch = _accountDao.createSearchBuilder();
if (listProjectResourcesCriteria == Project.ListProjectResourcesCriteria.ListProjectResourcesOnly) {
accountSearch.and("type", accountSearch.entity().getType(), SearchCriteria.Op.EQ);
} else if (listProjectResourcesCriteria == Project.ListProjectResourcesCriteria.SkipProjectResources) {
accountSearch.and("type", accountSearch.entity().getType(), SearchCriteria.Op.NEQ);
}
if (sb.entity() instanceof IPAddressVO) {
sb.join("accountSearch", accountSearch, ((IPAddressVO) sb.entity()).getAllocatedToAccountId(), accountSearch.entity().getId(), JoinBuilder.JoinType.INNER);
} else if (sb.entity() instanceof ProjectInvitationVO) {
sb.join("accountSearch", accountSearch, ((ProjectInvitationVO) sb.entity()).getForAccountId(), accountSearch.entity().getId(), JoinBuilder.JoinType.INNER);
} else {
sb.join("accountSearch", accountSearch, sb.entity().getAccountId(), accountSearch.entity().getId(), JoinBuilder.JoinType.INNER);
}
}
}
@Override
public void buildACLSearchCriteria(SearchCriteria<? extends ControlledEntity> sc,
Long domainId, boolean isRecursive, List<Long> permittedAccounts, ListProjectResourcesCriteria listProjectResourcesCriteria) {
if (listProjectResourcesCriteria != null) {
sc.setJoinParameters("accountSearch", "type", Account.ACCOUNT_TYPE_PROJECT);
}
if (!permittedAccounts.isEmpty()) {
sc.setParameters("accountIdIN", permittedAccounts.toArray());
} else if (domainId != null) {
DomainVO domain = _domainDao.findById(domainId);
if (isRecursive) {
sc.setJoinParameters("domainSearch", "path", domain.getPath() + "%");
} else {
sc.setParameters("domainId", domainId);
}
}
}
@Override
public void buildACLSearchParameters(Account caller, Long id, String accountName, Long projectId, List<Long>
permittedAccounts, Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject,
boolean listAll, boolean forProjectInvitation) {
Long domainId = domainIdRecursiveListProject.first();
if (domainId != null) {
Domain domain = _domainDao.findById(domainId);
if (domain == null) {
throw new InvalidParameterValueException("Unable to find domain by id " + domainId);
}
// check permissions
checkAccess(caller, domain);
}
if (accountName != null) {
if (projectId != null) {
throw new InvalidParameterValueException("Account and projectId can't be specified together");
}
Account userAccount = null;
if (domainId != null) {
userAccount = _accountDao.findActiveAccount(accountName, domainId);
} else {
userAccount = _accountDao.findActiveAccount(accountName, caller.getDomainId());
}
if (userAccount != null) {
checkAccess(caller, null, false, userAccount);
//check permissions
permittedAccounts.add(userAccount.getId());
} else {
throw new InvalidParameterValueException("could not find account " + accountName + " in domain " + domainId);
}
}
// set project information
if (projectId != null) {
if (!forProjectInvitation) {
if (projectId == -1) {
if (caller.getType() == Account.ACCOUNT_TYPE_NORMAL) {
permittedAccounts.addAll(_projectMgr.listPermittedProjectAccounts(caller.getId()));
} else {
domainIdRecursiveListProject.third(Project.ListProjectResourcesCriteria.ListProjectResourcesOnly);
}
} else {
Project project = _projectMgr.getProject(projectId);
if (project == null) {
throw new InvalidParameterValueException("Unable to find project by id " + projectId);
}
if (!_projectMgr.canAccessProjectAccount(caller, project.getProjectAccountId())) {
throw new PermissionDeniedException("Account " + caller + " can't access project id=" + projectId);
}
permittedAccounts.add(project.getProjectAccountId());
}
}
} else {
if (id == null) {
domainIdRecursiveListProject.third(Project.ListProjectResourcesCriteria.SkipProjectResources);
}
if (permittedAccounts.isEmpty() && domainId == null) {
if (caller.getType() == Account.ACCOUNT_TYPE_NORMAL) {
permittedAccounts.add(caller.getId());
} else if (!listAll) {
if (id == null) {
permittedAccounts.add(caller.getId());
} else if (caller.getType() != Account.ACCOUNT_TYPE_ADMIN) {
domainIdRecursiveListProject.first(caller.getDomainId());
domainIdRecursiveListProject.second(true);
}
} else if (domainId == null) {
if (caller.getType() == Account.ACCOUNT_TYPE_DOMAIN_ADMIN) {
domainIdRecursiveListProject.first(caller.getDomainId());
domainIdRecursiveListProject.second(true);
}
}
}
}
}
@Override
public UserAccount getUserByApiKey(String apiKey) {
return _userAccountDao.getUserByApiKey(apiKey);
}
}