| // 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.network.vpn; |
| |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| import javax.inject.Inject; |
| import javax.naming.ConfigurationException; |
| |
| import org.apache.log4j.Logger; |
| |
| import org.apache.cloudstack.acl.SecurityChecker.AccessType; |
| import org.apache.cloudstack.api.command.user.vpn.ListRemoteAccessVpnsCmd; |
| import org.apache.cloudstack.api.command.user.vpn.ListVpnUsersCmd; |
| import org.apache.cloudstack.context.CallContext; |
| import org.apache.cloudstack.framework.config.ConfigKey; |
| import org.apache.cloudstack.framework.config.Configurable; |
| import org.apache.cloudstack.framework.config.dao.ConfigurationDao; |
| |
| import com.cloud.configuration.Config; |
| 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.UsageEventUtils; |
| import com.cloud.event.dao.UsageEventDao; |
| import com.cloud.exception.AccountLimitException; |
| import com.cloud.exception.InvalidParameterValueException; |
| import com.cloud.exception.NetworkRuleConflictException; |
| import com.cloud.exception.ResourceUnavailableException; |
| import com.cloud.network.Network; |
| import com.cloud.network.Network.Service; |
| import com.cloud.network.NetworkModel; |
| import com.cloud.network.PublicIpAddress; |
| import com.cloud.network.RemoteAccessVpn; |
| import com.cloud.network.VpnUser; |
| import com.cloud.network.VpnUser.State; |
| import com.cloud.network.VpnUserVO; |
| import com.cloud.network.dao.FirewallRulesDao; |
| import com.cloud.network.dao.IPAddressDao; |
| import com.cloud.network.dao.IPAddressVO; |
| import com.cloud.network.dao.RemoteAccessVpnDao; |
| import com.cloud.network.dao.RemoteAccessVpnVO; |
| import com.cloud.network.dao.VpnUserDao; |
| import com.cloud.network.element.RemoteAccessVPNServiceProvider; |
| import com.cloud.network.rules.FirewallManager; |
| import com.cloud.network.rules.FirewallRule; |
| import com.cloud.network.rules.FirewallRule.Purpose; |
| import com.cloud.network.rules.FirewallRuleVO; |
| import com.cloud.network.rules.RulesManager; |
| import com.cloud.network.vpc.Vpc; |
| import com.cloud.network.vpc.dao.VpcDao; |
| import com.cloud.projects.Project.ListProjectResourcesCriteria; |
| import com.cloud.server.ConfigurationServer; |
| import com.cloud.user.Account; |
| import com.cloud.user.AccountManager; |
| import com.cloud.user.DomainManager; |
| import com.cloud.user.dao.AccountDao; |
| import com.cloud.utils.NumbersUtil; |
| import com.cloud.utils.Pair; |
| import com.cloud.utils.PasswordGenerator; |
| import com.cloud.utils.Ternary; |
| import com.cloud.utils.component.ManagerBase; |
| import com.cloud.utils.db.DB; |
| import com.cloud.utils.db.Filter; |
| import com.cloud.utils.db.JoinBuilder; |
| import com.cloud.utils.db.SearchBuilder; |
| import com.cloud.utils.db.SearchCriteria; |
| import com.cloud.utils.db.SearchCriteria.Op; |
| import com.cloud.utils.db.Transaction; |
| import com.cloud.utils.db.TransactionCallback; |
| import com.cloud.utils.db.TransactionCallbackNoReturn; |
| import com.cloud.utils.db.TransactionCallbackWithException; |
| import com.cloud.utils.db.TransactionStatus; |
| import com.cloud.utils.net.NetUtils; |
| import org.apache.commons.collections.CollectionUtils; |
| |
| public class RemoteAccessVpnManagerImpl extends ManagerBase implements RemoteAccessVpnService, Configurable { |
| private final static Logger s_logger = Logger.getLogger(RemoteAccessVpnManagerImpl.class); |
| |
| static final ConfigKey<String> RemoteAccessVpnClientIpRange = new ConfigKey<String>("Network", String.class, RemoteAccessVpnClientIpRangeCK, "10.1.2.1-10.1.2.8", |
| "The range of ips to be allocated to remote access vpn clients. The first ip in the range is used by the VPN server", false, ConfigKey.Scope.Account); |
| |
| @Inject |
| AccountDao _accountDao; |
| @Inject |
| VpnUserDao _vpnUsersDao; |
| @Inject |
| RemoteAccessVpnDao _remoteAccessVpnDao; |
| @Inject |
| IPAddressDao _ipAddressDao; |
| @Inject |
| AccountManager _accountMgr; |
| @Inject |
| DomainManager _domainMgr; |
| @Inject |
| NetworkModel _networkMgr; |
| @Inject |
| RulesManager _rulesMgr; |
| @Inject |
| DomainDao _domainDao; |
| @Inject |
| FirewallRulesDao _rulesDao; |
| @Inject |
| FirewallManager _firewallMgr; |
| @Inject |
| UsageEventDao _usageEventDao; |
| @Inject |
| ConfigurationDao _configDao; |
| List<RemoteAccessVPNServiceProvider> _vpnServiceProviders; |
| |
| @Inject |
| ConfigurationServer _configServer; |
| @Inject |
| VpcDao _vpcDao; |
| |
| int _userLimit; |
| int _pskLength; |
| SearchBuilder<RemoteAccessVpnVO> VpnSearch; |
| |
| @Override |
| @DB |
| public RemoteAccessVpn createRemoteAccessVpn(final long publicIpId, String ipRange, boolean openFirewall, final Boolean forDisplay) throws NetworkRuleConflictException { |
| CallContext ctx = CallContext.current(); |
| final Account caller = ctx.getCallingAccount(); |
| |
| Long networkId = null; |
| |
| // make sure ip address exists |
| final PublicIpAddress ipAddr = _networkMgr.getPublicIpAddress(publicIpId); |
| if (ipAddr == null) { |
| throw new InvalidParameterValueException("Unable to create remote access vpn, invalid public IP address id" + publicIpId); |
| } |
| |
| _accountMgr.checkAccess(caller, null, true, ipAddr); |
| |
| if (!ipAddr.readyToUse()) { |
| throw new InvalidParameterValueException("The Ip address is not ready to be used yet: " + ipAddr.getAddress()); |
| } |
| |
| IPAddressVO ipAddress = _ipAddressDao.findById(publicIpId); |
| |
| networkId = ipAddress.getAssociatedWithNetworkId(); |
| if (networkId != null) { |
| _networkMgr.checkIpForService(ipAddress, Service.Vpn, null); |
| } |
| |
| final Long vpcId = ipAddress.getVpcId(); |
| /* IP Address used for VPC must be the source NAT IP of whole VPC */ |
| if (vpcId != null && ipAddress.isSourceNat()) { |
| assert networkId == null; |
| // No firewall setting for VPC, it would be open internally |
| openFirewall = false; |
| } |
| |
| final boolean openFirewallFinal = openFirewall; |
| |
| if (networkId == null && vpcId == null) { |
| throw new InvalidParameterValueException("Unable to create remote access vpn for the ipAddress: " + ipAddr.getAddress().addr() + |
| " as ip is not associated with any network or VPC"); |
| } |
| |
| RemoteAccessVpnVO vpnVO = _remoteAccessVpnDao.findByPublicIpAddress(publicIpId); |
| |
| if (vpnVO != null) { |
| //if vpn is in Added state, return it to the api |
| if (vpnVO.getState() == RemoteAccessVpn.State.Added) { |
| return vpnVO; |
| } |
| throw new InvalidParameterValueException("A Remote Access VPN already exists for this public Ip address"); |
| } |
| |
| if (ipRange == null) { |
| ipRange = RemoteAccessVpnClientIpRange.valueIn(ipAddr.getAccountId()); |
| } |
| final String[] range = ipRange.split("-"); |
| if (range.length != 2) { |
| throw new InvalidParameterValueException("Invalid ip range"); |
| } |
| if (!NetUtils.isValidIp4(range[0]) || !NetUtils.isValidIp4(range[1])) { |
| throw new InvalidParameterValueException("Invalid ip in range specification " + ipRange); |
| } |
| if (!NetUtils.validIpRange(range[0], range[1])) { |
| throw new InvalidParameterValueException("Invalid ip range " + ipRange); |
| } |
| |
| Pair<String, Integer> cidr = null; |
| |
| // TODO: assumes one virtual network / domr per account per zone |
| if (networkId != null) { |
| vpnVO = _remoteAccessVpnDao.findByAccountAndNetwork(ipAddr.getAccountId(), networkId); |
| if (vpnVO != null) { |
| //if vpn is in Added state, return it to the api |
| if (vpnVO.getState() == RemoteAccessVpn.State.Added) { |
| return vpnVO; |
| } |
| throw new InvalidParameterValueException("A Remote Access VPN already exists for this account"); |
| } |
| //Verify that vpn service is enabled for the network |
| Network network = _networkMgr.getNetwork(networkId); |
| if (!_networkMgr.areServicesSupportedInNetwork(network.getId(), Service.Vpn)) { |
| throw new InvalidParameterValueException("Vpn service is not supported in network id=" + ipAddr.getAssociatedWithNetworkId()); |
| } |
| cidr = NetUtils.getCidr(network.getCidr()); |
| } else { // Don't need to check VPC because there is only one IP(source NAT IP) available for VPN |
| Vpc vpc = _vpcDao.findById(vpcId); |
| cidr = NetUtils.getCidr(vpc.getCidr()); |
| } |
| |
| // FIXME: This check won't work for the case where the guest ip range |
| // changes depending on the vlan allocated. |
| String[] guestIpRange = NetUtils.getIpRangeFromCidr(cidr.first(), cidr.second()); |
| if (NetUtils.ipRangesOverlap(range[0], range[1], guestIpRange[0], guestIpRange[1])) { |
| throw new InvalidParameterValueException("Invalid ip range: " + ipRange + " overlaps with guest ip range " + guestIpRange[0] + "-" + guestIpRange[1]); |
| } |
| // TODO: check sufficient range |
| // TODO: check overlap with private and public ip ranges in datacenter |
| |
| long startIp = NetUtils.ip2Long(range[0]); |
| final String newIpRange = NetUtils.long2Ip(++startIp) + "-" + range[1]; |
| final String sharedSecret = PasswordGenerator.generatePresharedKey(_pskLength); |
| |
| return Transaction.execute(new TransactionCallbackWithException<RemoteAccessVpn, NetworkRuleConflictException>() { |
| @Override |
| public RemoteAccessVpn doInTransaction(TransactionStatus status) throws NetworkRuleConflictException { |
| if (vpcId == null) { |
| _rulesMgr.reservePorts(ipAddr, NetUtils.UDP_PROTO, Purpose.Vpn, openFirewallFinal, caller, NetUtils.VPN_PORT, NetUtils.VPN_L2TP_PORT, |
| NetUtils.VPN_NATT_PORT); |
| } |
| RemoteAccessVpnVO vpnVO = |
| new RemoteAccessVpnVO(ipAddr.getAccountId(), ipAddr.getDomainId(), ipAddr.getAssociatedWithNetworkId(), publicIpId, vpcId, range[0], newIpRange, |
| sharedSecret); |
| |
| if (forDisplay != null) { |
| vpnVO.setDisplay(forDisplay); |
| } |
| return _remoteAccessVpnDao.persist(vpnVO); |
| } |
| }); |
| } |
| |
| private void validateRemoteAccessVpnConfiguration() throws ConfigurationException { |
| String ipRange = RemoteAccessVpnClientIpRange.value(); |
| if (ipRange == null) { |
| s_logger.warn("Remote Access VPN global configuration missing client ip range -- ignoring"); |
| return; |
| } |
| Integer pskLength = _pskLength; |
| if (pskLength != null && (pskLength < 8 || pskLength > 256)) { |
| throw new ConfigurationException("Remote Access VPN: IPSec preshared key length should be between 8 and 256"); |
| } |
| |
| String[] range = ipRange.split("-"); |
| if (range.length != 2) { |
| throw new ConfigurationException("Remote Access VPN: Invalid ip range " + ipRange); |
| } |
| if (!NetUtils.isValidIp4(range[0]) || !NetUtils.isValidIp4(range[1])) { |
| throw new ConfigurationException("Remote Access VPN: Invalid ip in range specification " + ipRange); |
| } |
| if (!NetUtils.validIpRange(range[0], range[1])) { |
| throw new ConfigurationException("Remote Access VPN: Invalid ip range " + ipRange); |
| } |
| } |
| |
| @Override |
| @DB |
| @ActionEvent(eventType = EventTypes.EVENT_REMOTE_ACCESS_VPN_DESTROY, eventDescription = "removing remote access vpn", async = true) |
| public boolean destroyRemoteAccessVpnForIp(long ipId, Account caller, final boolean forceCleanup) throws ResourceUnavailableException { |
| final RemoteAccessVpnVO vpn = _remoteAccessVpnDao.findByPublicIpAddress(ipId); |
| if (vpn == null) { |
| s_logger.debug("there are no Remote access vpns for public ip address id=" + ipId); |
| return true; |
| } |
| |
| _accountMgr.checkAccess(caller, AccessType.OperateEntry, true, vpn); |
| |
| RemoteAccessVpn.State prevState = vpn.getState(); |
| vpn.setState(RemoteAccessVpn.State.Removed); |
| _remoteAccessVpnDao.update(vpn.getId(), vpn); |
| |
| boolean success = false; |
| try { |
| for (RemoteAccessVPNServiceProvider element : _vpnServiceProviders) { |
| if (element.stopVpn(vpn)) { |
| success = true; |
| break; |
| } |
| } |
| }catch (ResourceUnavailableException ex) { |
| vpn.setState(prevState); |
| _remoteAccessVpnDao.update(vpn.getId(), vpn); |
| s_logger.debug("Failed to stop the vpn " + vpn.getId() + " , so reverted state to "+ |
| RemoteAccessVpn.State.Running); |
| success = false; |
| } finally { |
| if (success|| forceCleanup) { |
| //Cleanup corresponding ports |
| final List<? extends FirewallRule> vpnFwRules = _rulesDao.listByIpAndPurpose(ipId, Purpose.Vpn); |
| |
| boolean applyFirewall = false; |
| final List<FirewallRuleVO> fwRules = new ArrayList<FirewallRuleVO>(); |
| //if related firewall rule is created for the first vpn port, it would be created for the 2 other ports as well, so need to cleanup the backend |
| if (vpnFwRules.size() != 0 && _rulesDao.findByRelatedId(vpnFwRules.get(0).getId()) != null) { |
| applyFirewall = true; |
| } |
| |
| if (applyFirewall) { |
| Transaction.execute(new TransactionCallbackNoReturn() { |
| @Override |
| public void doInTransactionWithoutResult(TransactionStatus status) { |
| for (FirewallRule vpnFwRule : vpnFwRules) { |
| //don't apply on the backend yet; send all 3 rules in a banch |
| _firewallMgr.revokeRelatedFirewallRule(vpnFwRule.getId(), false); |
| fwRules.add(_rulesDao.findByRelatedId(vpnFwRule.getId())); |
| } |
| |
| s_logger.debug("Marked " + fwRules.size() + " firewall rules as Revoked as a part of disable remote access vpn"); |
| } |
| }); |
| |
| //now apply vpn rules on the backend |
| s_logger.debug("Reapplying firewall rules for ip id=" + ipId + " as a part of disable remote access vpn"); |
| success = _firewallMgr.applyIngressFirewallRules(ipId, caller); |
| } |
| |
| if (success|| forceCleanup) { |
| try { |
| Transaction.execute(new TransactionCallbackNoReturn() { |
| @Override |
| public void doInTransactionWithoutResult(TransactionStatus status) { |
| _remoteAccessVpnDao.remove(vpn.getId()); |
| // Stop billing of VPN users when VPN is removed. VPN_User_ADD events will be generated when VPN is created again |
| List<VpnUserVO> vpnUsers = _vpnUsersDao.listByAccount(vpn.getAccountId()); |
| for (VpnUserVO user : vpnUsers) { |
| // VPN_USER_REMOVE event is already generated for users in Revoke state |
| if (user.getState() != VpnUser.State.Revoke) { |
| UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VPN_USER_REMOVE, user.getAccountId(), 0, user.getId(), user.getUsername(), |
| user.getClass().getName(), user.getUuid()); |
| } |
| } |
| if (vpnFwRules != null) { |
| for (FirewallRule vpnFwRule : vpnFwRules) { |
| _rulesDao.remove(vpnFwRule.getId()); |
| s_logger.debug("Successfully removed firewall rule with ip id=" + vpnFwRule.getSourceIpAddressId() + " and port " + |
| vpnFwRule.getSourcePortStart().intValue() + " as a part of vpn cleanup"); |
| } |
| } |
| } |
| }); |
| } catch (Exception ex) { |
| s_logger.warn("Unable to release the three vpn ports from the firewall rules", ex); |
| } |
| } |
| } |
| } |
| return success; |
| } |
| |
| @Override |
| @DB |
| public VpnUser addVpnUser(final long vpnOwnerId, final String username, final String password) { |
| final Account caller = CallContext.current().getCallingAccount(); |
| |
| if (!username.matches("^[a-zA-Z0-9][a-zA-Z0-9@._-]{2,63}$")) { |
| throw new InvalidParameterValueException("Username has to be begin with an alphabet have 3-64 characters including alphabets, numbers and the set '@.-_'"); |
| } |
| if (!password.matches("^[a-zA-Z0-9][a-zA-Z0-9@+=._-]{2,31}$")) { |
| throw new InvalidParameterValueException("Password has to be 3-32 characters including alphabets, numbers and the set '@+=.-_'"); |
| } |
| |
| return Transaction.execute(new TransactionCallback<VpnUser>() { |
| @Override |
| public VpnUser doInTransaction(TransactionStatus status) { |
| Account owner = _accountDao.lockRow(vpnOwnerId, true); |
| if (owner == null) { |
| throw new InvalidParameterValueException("Unable to add vpn user: Another operation active"); |
| } |
| _accountMgr.checkAccess(caller, null, true, owner); |
| |
| //don't allow duplicated user names for the same account |
| VpnUserVO vpnUser = _vpnUsersDao.findByAccountAndUsername(owner.getId(), username); |
| if (vpnUser != null) { |
| throw new InvalidParameterValueException("VPN User with name " + username + " is already added for account " + owner); |
| } |
| |
| long userCount = _vpnUsersDao.getVpnUserCount(owner.getId()); |
| if (userCount >= _userLimit) { |
| throw new AccountLimitException("Cannot add more than " + _userLimit + " remote access vpn users"); |
| } |
| |
| VpnUser user = _vpnUsersDao.persist(new VpnUserVO(vpnOwnerId, owner.getDomainId(), username, password)); |
| UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VPN_USER_ADD, user.getAccountId(), 0, user.getId(), user.getUsername(), user.getClass().getName(), |
| user.getUuid()); |
| |
| return user; |
| } |
| }); |
| } |
| |
| @DB |
| @Override |
| public boolean removeVpnUser(long vpnOwnerId, String username, Account caller) { |
| final VpnUserVO user = _vpnUsersDao.findByAccountAndUsername(vpnOwnerId, username); |
| if (user == null) { |
| throw new InvalidParameterValueException("Could not find vpn user " + username); |
| } |
| _accountMgr.checkAccess(caller, null, true, user); |
| |
| Transaction.execute(new TransactionCallbackNoReturn() { |
| @Override |
| public void doInTransactionWithoutResult(TransactionStatus status) { |
| user.setState(State.Revoke); |
| _vpnUsersDao.update(user.getId(), user); |
| UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VPN_USER_REMOVE, user.getAccountId(), 0, user.getId(), user.getUsername(), user.getClass().getName(), |
| user.getUuid()); |
| } |
| }); |
| |
| return true; |
| } |
| |
| @Override |
| public List<? extends VpnUser> listVpnUsers(long vpnOwnerId, String userName) { |
| Account caller = CallContext.current().getCallingAccount(); |
| Account owner = _accountDao.findById(vpnOwnerId); |
| _accountMgr.checkAccess(caller, null, true, owner); |
| return _vpnUsersDao.listByAccount(vpnOwnerId); |
| } |
| |
| @Override |
| @DB |
| @ActionEvent(eventType = EventTypes.EVENT_REMOTE_ACCESS_VPN_CREATE, eventDescription = "creating remote access vpn", async = true) |
| public RemoteAccessVpnVO startRemoteAccessVpn(long ipAddressId, boolean openFirewall) throws ResourceUnavailableException { |
| Account caller = CallContext.current().getCallingAccount(); |
| |
| final RemoteAccessVpnVO vpn = _remoteAccessVpnDao.findByPublicIpAddress(ipAddressId); |
| if (vpn == null) { |
| throw new InvalidParameterValueException("Unable to find your vpn: " + ipAddressId); |
| } |
| |
| if (vpn.getVpcId() != null) { |
| openFirewall = false; |
| } |
| |
| _accountMgr.checkAccess(caller, null, true, vpn); |
| |
| boolean started = false; |
| try { |
| boolean firewallOpened = true; |
| if (openFirewall) { |
| firewallOpened = _firewallMgr.applyIngressFirewallRules(vpn.getServerAddressId(), caller); |
| } |
| |
| if (firewallOpened) { |
| for (RemoteAccessVPNServiceProvider element : _vpnServiceProviders) { |
| if (element.startVpn(vpn)) { |
| started = true; |
| break; |
| } |
| } |
| } |
| |
| return vpn; |
| } finally { |
| if (started) { |
| Transaction.execute(new TransactionCallbackNoReturn() { |
| @Override |
| public void doInTransactionWithoutResult(TransactionStatus status) { |
| vpn.setState(RemoteAccessVpn.State.Running); |
| _remoteAccessVpnDao.update(vpn.getId(), vpn); |
| |
| // Start billing of existing VPN users in ADD and Active state |
| List<VpnUserVO> vpnUsers = _vpnUsersDao.listByAccount(vpn.getAccountId()); |
| for (VpnUserVO user : vpnUsers) { |
| if (user.getState() != VpnUser.State.Revoke) { |
| UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VPN_USER_ADD, user.getAccountId(), 0, user.getId(), user.getUsername(), |
| user.getClass().getName(), user.getUuid()); |
| } |
| } |
| } |
| }); |
| } |
| } |
| } |
| |
| @DB |
| @Override |
| public boolean applyVpnUsers(long vpnOwnerId, String userName) throws ResourceUnavailableException { |
| Account caller = CallContext.current().getCallingAccount(); |
| Account owner = _accountDao.findById(vpnOwnerId); |
| _accountMgr.checkAccess(caller, null, true, owner); |
| |
| s_logger.debug("Applying vpn users for " + owner); |
| List<RemoteAccessVpnVO> vpns = _remoteAccessVpnDao.findByAccount(vpnOwnerId); |
| |
| if (CollectionUtils.isEmpty(vpns)) { |
| s_logger.debug("There are no remote access vpns configured on this account " + owner +" to apply vpn user, failing add vpn user "); |
| return false; |
| } |
| |
| RemoteAccessVpnVO vpnTemp = null; |
| |
| List<VpnUserVO> users = _vpnUsersDao.listByAccount(vpnOwnerId); |
| |
| //If user is in Active state, we still have to resend them therefore their status has to be Add |
| for (VpnUserVO user : users) { |
| if (user.getState() == State.Active) { |
| user.setState(State.Add); |
| _vpnUsersDao.update(user.getId(), user); |
| } |
| } |
| |
| boolean success = true; |
| |
| Boolean[] finals = new Boolean[users.size()]; |
| for (RemoteAccessVPNServiceProvider element : _vpnServiceProviders) { |
| s_logger.debug("Applying vpn access to " + element.getName()); |
| for (RemoteAccessVpnVO vpn : vpns) { |
| try { |
| String[] results = element.applyVpnUsers(vpn, users); |
| if (results != null) { |
| int indexUser = -1; |
| for (int i = 0; i < results.length; i++) { |
| indexUser ++; |
| if (indexUser == users.size()) { |
| indexUser = 0; // results on multiple VPC routers are combined in commit 13eb789, reset user index if one VR is done. |
| } |
| s_logger.debug("VPN User " + users.get(indexUser) + (results[i] == null ? " is set on " : (" couldn't be set due to " + results[i]) + " on ") + vpn.getUuid()); |
| if (results[i] == null) { |
| if (finals[indexUser] == null) { |
| finals[indexUser] = true; |
| } |
| } else { |
| finals[indexUser] = false; |
| success = false; |
| vpnTemp = vpn; |
| } |
| } |
| } |
| } catch (Exception e) { |
| s_logger.warn("Unable to apply vpn users ", e); |
| success = false; |
| vpnTemp = vpn; |
| |
| for (int i = 0; i < finals.length; i++) { |
| finals[i] = false; |
| } |
| } |
| } |
| } |
| |
| for (int i = 0; i < finals.length; i++) { |
| final VpnUserVO user = users.get(i); |
| if (finals[i]) { |
| if (user.getState() == State.Add) { |
| user.setState(State.Active); |
| _vpnUsersDao.update(user.getId(), user); |
| } else if (user.getState() == State.Revoke) { |
| _vpnUsersDao.remove(user.getId()); |
| } |
| } else { |
| if (user.getState() == State.Add && (user.getUsername()).equals(userName)) { |
| Transaction.execute(new TransactionCallbackNoReturn() { |
| @Override |
| public void doInTransactionWithoutResult(TransactionStatus status) { |
| _vpnUsersDao.remove(user.getId()); |
| UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VPN_USER_REMOVE, user.getAccountId(), 0, user.getId(), user.getUsername(), user.getClass() |
| .getName(), user.getUuid()); |
| } |
| }); |
| } |
| s_logger.warn("Failed to apply vpn for user " + user.getUsername() + ", accountId=" + user.getAccountId()); |
| } |
| } |
| |
| if (!success) { |
| throw new ResourceUnavailableException("Failed add vpn user due to Resource unavailable ", |
| RemoteAccessVPNServiceProvider.class, vpnTemp.getId()); |
| } |
| |
| return success; |
| } |
| |
| @Override |
| public Pair<List<? extends VpnUser>, Integer> searchForVpnUsers(ListVpnUsersCmd cmd) { |
| String username = cmd.getUsername(); |
| Long id = cmd.getId(); |
| String keyword = cmd.getKeyword(); |
| Account caller = CallContext.current().getCallingAccount(); |
| List<Long> permittedAccounts = new ArrayList<Long>(); |
| |
| Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<Long, Boolean, ListProjectResourcesCriteria>(cmd.getDomainId(), cmd.isRecursive(), null); |
| _accountMgr.buildACLSearchParameters(caller, id, cmd.getAccountName(), cmd.getProjectId(), permittedAccounts, domainIdRecursiveListProject, cmd.listAll(), false); |
| Long domainId = domainIdRecursiveListProject.first(); |
| Boolean isRecursive = domainIdRecursiveListProject.second(); |
| ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third(); |
| Filter searchFilter = new Filter(VpnUserVO.class, "username", true, cmd.getStartIndex(), cmd.getPageSizeVal()); |
| SearchBuilder<VpnUserVO> sb = _vpnUsersDao.createSearchBuilder(); |
| _accountMgr.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); |
| |
| |
| sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); |
| sb.and("username", sb.entity().getUsername(), SearchCriteria.Op.EQ); |
| sb.and("keyword", sb.entity().getUsername(), SearchCriteria.Op.LIKE); |
| sb.and("state", sb.entity().getState(), Op.IN); |
| |
| SearchCriteria<VpnUserVO> sc = sb.create(); |
| _accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); |
| |
| //list only active users |
| sc.setParameters("state", State.Active, State.Add); |
| |
| if(keyword != null){ |
| sc.setParameters("keyword", "%" + keyword + "%"); |
| } |
| |
| if (id != null) { |
| sc.setParameters("id", id); |
| } |
| |
| if (username != null) { |
| sc.setParameters("username", username); |
| } |
| |
| Pair<List<VpnUserVO>, Integer> result = _vpnUsersDao.searchAndCount(sc, searchFilter); |
| return new Pair<List<? extends VpnUser>, Integer>(result.first(), result.second()); |
| } |
| |
| @Override |
| public Pair<List<? extends RemoteAccessVpn>, Integer> searchForRemoteAccessVpns(ListRemoteAccessVpnsCmd cmd) { |
| // do some parameter validation |
| Account caller = CallContext.current().getCallingAccount(); |
| Long ipAddressId = cmd.getPublicIpId(); |
| List<Long> permittedAccounts = new ArrayList<Long>(); |
| |
| Long vpnId = cmd.getId(); |
| Long networkId = cmd.getNetworkId(); |
| |
| if (ipAddressId != null) { |
| PublicIpAddress publicIp = _networkMgr.getPublicIpAddress(ipAddressId); |
| if (publicIp == null) { |
| throw new InvalidParameterValueException("Unable to list remote access vpns, IP address " + ipAddressId + " not found."); |
| } else { |
| Long ipAddrAcctId = publicIp.getAccountId(); |
| if (ipAddrAcctId == null) { |
| throw new InvalidParameterValueException("Unable to list remote access vpns, IP address " + ipAddressId + " is not associated with an account."); |
| } |
| } |
| _accountMgr.checkAccess(caller, null, true, publicIp); |
| } |
| |
| Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<Long, Boolean, ListProjectResourcesCriteria>(cmd.getDomainId(), cmd.isRecursive(), null); |
| _accountMgr.buildACLSearchParameters(caller, null, cmd.getAccountName(), cmd.getProjectId(), permittedAccounts, domainIdRecursiveListProject, cmd.listAll(), false); |
| Long domainId = domainIdRecursiveListProject.first(); |
| Boolean isRecursive = domainIdRecursiveListProject.second(); |
| ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third(); |
| |
| Filter filter = new Filter(RemoteAccessVpnVO.class, "serverAddressId", false, cmd.getStartIndex(), cmd.getPageSizeVal()); |
| SearchBuilder<RemoteAccessVpnVO> sb = _remoteAccessVpnDao.createSearchBuilder(); |
| _accountMgr.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); |
| |
| sb.and("serverAddressId", sb.entity().getServerAddressId(), Op.EQ); |
| sb.and("id", sb.entity().getId(), Op.EQ); |
| sb.and("networkId", sb.entity().getNetworkId(), Op.EQ); |
| sb.and("state", sb.entity().getState(), Op.EQ); |
| sb.and("display", sb.entity().isDisplay(), Op.EQ); |
| |
| SearchCriteria<RemoteAccessVpnVO> sc = sb.create(); |
| _accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); |
| |
| |
| sc.setParameters("state", RemoteAccessVpn.State.Running); |
| |
| if (ipAddressId != null) { |
| sc.setParameters("serverAddressId", ipAddressId); |
| } |
| |
| if (vpnId != null) { |
| sc.setParameters("id", vpnId); |
| } |
| |
| if (networkId != null) { |
| sc.setParameters("networkId", networkId); |
| } |
| |
| Pair<List<RemoteAccessVpnVO>, Integer> result = _remoteAccessVpnDao.searchAndCount(sc, filter); |
| return new Pair<List<? extends RemoteAccessVpn>, Integer>(result.first(), result.second()); |
| } |
| |
| @Override |
| public boolean configure(String name, Map<String, Object> params) throws ConfigurationException { |
| Map<String, String> configs = _configDao.getConfiguration(params); |
| |
| _userLimit = NumbersUtil.parseInt(configs.get(Config.RemoteAccessVpnUserLimit.key()), 8); |
| |
| _pskLength = NumbersUtil.parseInt(configs.get(Config.RemoteAccessVpnPskLength.key()), 24); |
| |
| validateRemoteAccessVpnConfiguration(); |
| |
| VpnSearch = _remoteAccessVpnDao.createSearchBuilder(); |
| VpnSearch.and("accountId", VpnSearch.entity().getAccountId(), SearchCriteria.Op.EQ); |
| SearchBuilder<DomainVO> domainSearch = _domainDao.createSearchBuilder(); |
| domainSearch.and("path", domainSearch.entity().getPath(), SearchCriteria.Op.LIKE); |
| VpnSearch.join("domainSearch", domainSearch, VpnSearch.entity().getDomainId(), domainSearch.entity().getId(), JoinBuilder.JoinType.INNER); |
| VpnSearch.done(); |
| |
| return true; |
| } |
| |
| @Override |
| public List<? extends RemoteAccessVpn> listRemoteAccessVpns(long networkId) { |
| return _remoteAccessVpnDao.listByNetworkId(networkId); |
| } |
| |
| @Override |
| public RemoteAccessVpn getRemoteAccessVpn(long vpnAddrId) { |
| return _remoteAccessVpnDao.findByPublicIpAddress(vpnAddrId); |
| } |
| |
| @Override |
| public RemoteAccessVpn getRemoteAccessVpnById(long vpnId) { |
| return _remoteAccessVpnDao.findById(vpnId); |
| } |
| |
| public List<RemoteAccessVPNServiceProvider> getRemoteAccessVPNServiceProviders() { |
| List<RemoteAccessVPNServiceProvider> result = new ArrayList<RemoteAccessVPNServiceProvider>(); |
| for (Iterator<RemoteAccessVPNServiceProvider> e = _vpnServiceProviders.iterator(); e.hasNext();) { |
| result.add(e.next()); |
| } |
| |
| return result; |
| } |
| |
| @Override |
| public String getConfigComponentName() { |
| return RemoteAccessVpnService.class.getSimpleName(); |
| } |
| |
| @Override |
| public ConfigKey<?>[] getConfigKeys() { |
| return new ConfigKey<?>[] {RemoteAccessVpnClientIpRange}; |
| } |
| |
| public List<RemoteAccessVPNServiceProvider> getVpnServiceProviders() { |
| return _vpnServiceProviders; |
| } |
| |
| public void setVpnServiceProviders(List<RemoteAccessVPNServiceProvider> vpnServiceProviders) { |
| _vpnServiceProviders = vpnServiceProviders; |
| } |
| |
| @Override |
| @ActionEvent(eventType = EventTypes.EVENT_REMOTE_ACCESS_VPN_UPDATE, eventDescription = "updating remote access vpn", async = true) |
| public RemoteAccessVpn updateRemoteAccessVpn(long id, String customId, Boolean forDisplay) { |
| final RemoteAccessVpnVO vpn = _remoteAccessVpnDao.findById(id); |
| if (vpn == null) { |
| throw new InvalidParameterValueException("Can't find remote access vpn by id " + id); |
| } |
| |
| _accountMgr.checkAccess(CallContext.current().getCallingAccount(), null, true, vpn); |
| if (customId != null) { |
| vpn.setUuid(customId); |
| } |
| if (forDisplay != null) { |
| vpn.setDisplay(forDisplay); |
| } |
| |
| _remoteAccessVpnDao.update(vpn.getId(), vpn); |
| return _remoteAccessVpnDao.findById(id); |
| } |
| |
| } |