mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
2336 lines
100 KiB
Java
Executable File
2336 lines
100 KiB
Java
Executable File
// 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.Site2SiteCustomerGatewayVO;
|
|
import com.cloud.network.Site2SiteVpnConnectionVO;
|
|
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.Site2SiteCustomerGatewayDao;
|
|
import com.cloud.network.dao.Site2SiteVpnConnectionDao;
|
|
import com.cloud.network.dao.Site2SiteVpnGatewayDao;
|
|
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 (!_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");
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
// 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
|
|
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);
|
|
}
|
|
|
|
checkAccess(caller, null, true, account);
|
|
|
|
// don't allow modify system account
|
|
if (account.getId() == Account.ACCOUNT_ID_SYSTEM) {
|
|
throw new InvalidParameterValueException("can not lock system 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);
|
|
}
|
|
|
|
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) {
|
|
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);
|
|
UserAccountVO user = _userAccountDao.findById(userAccount.getId());
|
|
if (user != null) {
|
|
if ((user.getState().toString()).equals("enabled")) {
|
|
if (!isInternalAccount(user.getType())) {
|
|
//Internal accounts are not disabled
|
|
int attemptsMade = user.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 " + user.getUsername() + " has been disabled due to multiple failed login attempts." +
|
|
" Please contact admin.");
|
|
}
|
|
}
|
|
} else {
|
|
s_logger.info("User " + user.getUsername() + " is disabled/locked");
|
|
}
|
|
}
|
|
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();
|
|
|
|
if (getUserIncludingRemoved(userId) == null) {
|
|
throw new InvalidParameterValueException("unable to find user for id : " + userId);
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
}
|