diff --git a/api/src/org/apache/cloudstack/api/command/admin/config/ListCfgsByCmd.java b/api/src/org/apache/cloudstack/api/command/admin/config/ListCfgsByCmd.java index 8f71f48470e..80ebaf43f64 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/config/ListCfgsByCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/config/ListCfgsByCmd.java @@ -19,6 +19,7 @@ package org.apache.cloudstack.api.command.admin.config; import java.util.ArrayList; import java.util.List; +import org.apache.cloudstack.api.response.DomainResponse; import org.apache.log4j.Logger; import org.apache.cloudstack.api.APICommand; @@ -77,6 +78,12 @@ public class ListCfgsByCmd extends BaseListCmd { description = "the ID of the Account to update the parameter value for corresponding account") private Long accountId; + @Parameter(name = ApiConstants.DOMAIN_ID, + type = CommandType.UUID, + entityType = DomainResponse.class, + description = "the ID of the Domain to update the parameter value for corresponding domain") + private Long domainId; + @Parameter(name = ApiConstants.IMAGE_STORE_UUID, type = CommandType.UUID, entityType = ImageStoreResponse.class, @@ -111,6 +118,10 @@ public class ListCfgsByCmd extends BaseListCmd { return accountId; } + public Long getDomainId() { + return domainId; + } + public Long getImageStoreId() { return imageStoreId; } @@ -158,6 +169,9 @@ public class ListCfgsByCmd extends BaseListCmd { if (getAccountId() != null) { cfgResponse.setScope("account"); } + if (getDomainId() != null) { + cfgResponse.setScope("domain"); + } if (getImageStoreId() != null){ cfgResponse.setScope("imagestore"); } diff --git a/api/src/org/apache/cloudstack/api/command/admin/config/UpdateCfgCmd.java b/api/src/org/apache/cloudstack/api/command/admin/config/UpdateCfgCmd.java index fa5e26e418f..936f0cd69f1 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/config/UpdateCfgCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/config/UpdateCfgCmd.java @@ -18,6 +18,7 @@ package org.apache.cloudstack.api.command.admin.config; import com.google.common.base.Strings; import org.apache.cloudstack.acl.RoleService; +import org.apache.cloudstack.api.response.DomainResponse; import org.apache.log4j.Logger; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiArgValidator; @@ -76,6 +77,12 @@ public class UpdateCfgCmd extends BaseCmd { description = "the ID of the Account to update the parameter value for corresponding account") private Long accountId; + @Parameter(name = ApiConstants.DOMAIN_ID, + type = CommandType.UUID, + entityType = DomainResponse.class, + description = "the ID of the Domain to update the parameter value for corresponding domain") + private Long domainId; + @Parameter(name = ApiConstants.IMAGE_STORE_UUID, type = CommandType.UUID, entityType = ImageStoreResponse.class, @@ -115,6 +122,10 @@ public class UpdateCfgCmd extends BaseCmd { return accountId; } + public Long getDomainId() { + return domainId; + } + public Long getImageStoreId() { return imageStoreId; } @@ -157,6 +168,9 @@ public class UpdateCfgCmd extends BaseCmd { if (getAccountId() != null) { response.setScope("account"); } + if (getDomainId() != null) { + response.setScope("domain"); + } response.setValue(value); this.setResponseObject(response); } else { diff --git a/engine/schema/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml b/engine/schema/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml index 8a0d7cdde5c..84c27583925 100644 --- a/engine/schema/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml +++ b/engine/schema/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml @@ -147,6 +147,7 @@ + diff --git a/engine/schema/resources/META-INF/db/schema-41000to41100.sql b/engine/schema/resources/META-INF/db/schema-41000to41100.sql index 585c7fd4992..283844e96d6 100644 --- a/engine/schema/resources/META-INF/db/schema-41000to41100.sql +++ b/engine/schema/resources/META-INF/db/schema-41000to41100.sql @@ -451,7 +451,7 @@ CREATE VIEW `cloud`.`volume_view` AS `cloud`.`domain` resource_tag_domain ON resource_tag_domain.id = resource_tags.domain_id; -- Extra Dhcp Options -CREATE TABLE `cloud`.`nic_extra_dhcp_options` ( +CREATE TABLE IF NOT EXISTS `cloud`.`nic_extra_dhcp_options` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', `uuid` varchar(255) UNIQUE, `nic_id` bigint unsigned NOT NULL COMMENT ' nic id where dhcp options are applied', @@ -523,3 +523,20 @@ ADD COLUMN `forsystemvms` TINYINT(1) NOT NULL DEFAULT '0' COMMENT 'Indicates if ALTER TABLE `cloud`.`op_dc_ip_address_alloc` ADD COLUMN `vlan` INT(10) UNSIGNED NULL COMMENT 'Vlan the management network range is on'; + +-- ldap binding on domain level +CREATE TABLE IF NOT EXISTS `cloud`.`domain_details` ( + `id` bigint unsigned NOT NULL auto_increment, + `domain_id` bigint unsigned NOT NULL COMMENT 'account id', + `name` varchar(255) NOT NULL, + `value` varchar(255) NOT NULL, + PRIMARY KEY (`id`), + CONSTRAINT `fk_domain_details__domain_id` FOREIGN KEY (`domain_id`) REFERENCES `domain`(`id`) ON DELETE CASCADE +)ENGINE=InnoDB DEFAULT CHARSET=utf8; + +ALTER TABLE cloud.ldap_configuration ADD COLUMN domain_id BIGINT(20) DEFAULT NULL; +ALTER TABLE cloud.ldap_trust_map ADD COLUMN account_id BIGINT(20) DEFAULT 0; +ALTER TABLE cloud.ldap_trust_map DROP FOREIGN KEY fk_ldap_trust_map__domain_id; +DROP INDEX uk_ldap_trust_map__domain_id ON cloud.ldap_trust_map; +CREATE UNIQUE INDEX uk_ldap_trust_map__bind_location ON ldap_trust_map (domain_id, account_id); + diff --git a/engine/schema/src/com/cloud/domain/DomainDetailVO.java b/engine/schema/src/com/cloud/domain/DomainDetailVO.java new file mode 100644 index 00000000000..61eb6cfd28e --- /dev/null +++ b/engine/schema/src/com/cloud/domain/DomainDetailVO.java @@ -0,0 +1,76 @@ +// 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.domain; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import com.cloud.utils.db.Encrypt; +import org.apache.cloudstack.api.InternalIdentity; + +@Entity +@Table(name = "domain_details") +public class DomainDetailVO implements InternalIdentity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "domain_id") + private long domainId; + + @Column(name = "name") + private String name; + + @Encrypt + @Column(name = "value") + private String value; + + protected DomainDetailVO() { + } + + public DomainDetailVO(long domainId, String name, String value) { + this.domainId = domainId; + this.name = name; + this.value = value; + } + + public long getDomainId() { + return domainId; + } + + public String getName() { + return name; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public long getId() { + return id; + } +} diff --git a/engine/schema/src/com/cloud/domain/dao/DomainDetailsDao.java b/engine/schema/src/com/cloud/domain/dao/DomainDetailsDao.java new file mode 100644 index 00000000000..51362cf885e --- /dev/null +++ b/engine/schema/src/com/cloud/domain/dao/DomainDetailsDao.java @@ -0,0 +1,34 @@ +// 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.domain.dao; + +import java.util.Map; + +import com.cloud.domain.DomainDetailVO; +import com.cloud.utils.db.GenericDao; + +public interface DomainDetailsDao extends GenericDao { + Map findDetails(long domainId); + + void persist(long domainId, Map details); + + DomainDetailVO findDetail(long domainId, String name); + + void deleteDetails(long domainId); + + void update(long domainId, Map details); +} diff --git a/engine/schema/src/com/cloud/domain/dao/DomainDetailsDaoImpl.java b/engine/schema/src/com/cloud/domain/dao/DomainDetailsDaoImpl.java new file mode 100644 index 00000000000..ad7f7040207 --- /dev/null +++ b/engine/schema/src/com/cloud/domain/dao/DomainDetailsDaoImpl.java @@ -0,0 +1,104 @@ +// 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.domain.dao; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.cloud.domain.DomainDetailVO; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.QueryBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.SearchCriteria.Op; +import com.cloud.utils.db.TransactionLegacy; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.ConfigKey.Scope; +import org.apache.cloudstack.framework.config.ScopedConfigStorage; + +public class DomainDetailsDaoImpl extends GenericDaoBase implements DomainDetailsDao, ScopedConfigStorage { + protected final SearchBuilder domainSearch; + + protected DomainDetailsDaoImpl() { + domainSearch = createSearchBuilder(); + domainSearch.and("domainId", domainSearch.entity().getDomainId(), Op.EQ); + domainSearch.done(); + } + + @Override + public Map findDetails(long domainId) { + QueryBuilder sc = QueryBuilder.create(DomainDetailVO.class); + sc.and(sc.entity().getDomainId(), Op.EQ, domainId); + List results = sc.list(); + Map details = new HashMap(results.size()); + for (DomainDetailVO r : results) { + details.put(r.getName(), r.getValue()); + } + return details; + } + + @Override + public void persist(long domainId, Map details) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + txn.start(); + SearchCriteria sc = domainSearch.create(); + sc.setParameters("domainId", domainId); + expunge(sc); + for (Map.Entry detail : details.entrySet()) { + DomainDetailVO vo = new DomainDetailVO(domainId, detail.getKey(), detail.getValue()); + persist(vo); + } + txn.commit(); + } + + @Override + public DomainDetailVO findDetail(long domainId, String name) { + QueryBuilder sc = QueryBuilder.create(DomainDetailVO.class); + sc.and(sc.entity().getDomainId(), Op.EQ, domainId); + sc.and(sc.entity().getName(), Op.EQ, name); + return sc.find(); + } + + @Override + public void deleteDetails(long domainId) { + SearchCriteria sc = domainSearch.create(); + sc.setParameters("domainId", domainId); + List results = search(sc, null); + for (DomainDetailVO result : results) { + remove(result.getId()); + } + } + + @Override + public void update(long domainId, Map details) { + Map oldDetails = findDetails(domainId); + oldDetails.putAll(details); + persist(domainId, oldDetails); + } + + @Override + public Scope getScope() { + return Scope.Domain; + } + + @Override + public String getConfigValue(long id, ConfigKey key) { + DomainDetailVO vo = findDetail(id, key.key()); + return vo == null ? null : vo.getValue(); + } +} diff --git a/framework/config/src/org/apache/cloudstack/framework/config/ConfigKey.java b/framework/config/src/org/apache/cloudstack/framework/config/ConfigKey.java index fb2a57b71f6..1734b98757b 100644 --- a/framework/config/src/org/apache/cloudstack/framework/config/ConfigKey.java +++ b/framework/config/src/org/apache/cloudstack/framework/config/ConfigKey.java @@ -31,7 +31,7 @@ import com.cloud.utils.exception.CloudRuntimeException; public class ConfigKey { public static enum Scope { - Global, Zone, Cluster, StoragePool, Account, ManagementServer, ImageStore + Global, Zone, Cluster, StoragePool, Account, ManagementServer, ImageStore, Domain } private final String _category; diff --git a/framework/config/src/org/apache/cloudstack/framework/config/impl/ConfigDepotImpl.java b/framework/config/src/org/apache/cloudstack/framework/config/impl/ConfigDepotImpl.java index e68fd3cdae3..6a85b90b70d 100644 --- a/framework/config/src/org/apache/cloudstack/framework/config/impl/ConfigDepotImpl.java +++ b/framework/config/src/org/apache/cloudstack/framework/config/impl/ConfigDepotImpl.java @@ -85,6 +85,7 @@ public class ConfigDepotImpl implements ConfigDepot, ConfigDepotAdmin { _scopeLevelConfigsMap.put(ConfigKey.Scope.StoragePool, new HashSet>()); _scopeLevelConfigsMap.put(ConfigKey.Scope.Account, new HashSet>()); _scopeLevelConfigsMap.put(ConfigKey.Scope.ImageStore, new HashSet>()); + _scopeLevelConfigsMap.put(ConfigKey.Scope.Domain, new HashSet>()); } @Override diff --git a/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/IntegrationTestConfiguration.java b/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/IntegrationTestConfiguration.java index 7f1d5b805a8..4105a617e6c 100644 --- a/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/IntegrationTestConfiguration.java +++ b/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/IntegrationTestConfiguration.java @@ -140,6 +140,7 @@ import com.cloud.deploy.DeploymentPlanner; import com.cloud.deploy.DeploymentPlanningManager; import com.cloud.deploy.dao.PlannerHostReservationDaoImpl; import com.cloud.domain.dao.DomainDaoImpl; +import com.cloud.domain.dao.DomainDetailsDaoImpl; import com.cloud.event.dao.EventDaoImpl; import com.cloud.event.dao.EventJoinDaoImpl; import com.cloud.event.dao.UsageEventDaoImpl; @@ -148,8 +149,8 @@ import com.cloud.host.dao.HostDaoImpl; import com.cloud.host.dao.HostDetailsDaoImpl; import com.cloud.host.dao.HostTagsDaoImpl; import com.cloud.hypervisor.HypervisorGuruManagerImpl; -import com.cloud.hypervisor.dao.HypervisorCapabilitiesDaoImpl; import com.cloud.hypervisor.XenServerGuru; +import com.cloud.hypervisor.dao.HypervisorCapabilitiesDaoImpl; import com.cloud.network.ExternalDeviceUsageManager; import com.cloud.network.IpAddress; import com.cloud.network.IpAddressManagerImpl; @@ -169,8 +170,8 @@ import com.cloud.network.as.dao.CounterDaoImpl; import com.cloud.network.dao.AccountGuestVlanMapDaoImpl; import com.cloud.network.dao.FirewallRulesCidrsDaoImpl; import com.cloud.network.dao.FirewallRulesDaoImpl; -import com.cloud.network.dao.IPAddressDaoImpl; import com.cloud.network.dao.IPAddressDao; +import com.cloud.network.dao.IPAddressDaoImpl; import com.cloud.network.dao.LBHealthCheckPolicyDaoImpl; import com.cloud.network.dao.LBStickinessPolicyDaoImpl; import com.cloud.network.dao.LoadBalancerDaoImpl; @@ -308,7 +309,7 @@ import com.cloud.vm.snapshot.dao.VMSnapshotDaoImpl; ConditionDaoImpl.class, ConfigurationDaoImpl.class, ConfigurationManagerImpl.class, ConfigurationServerImpl.class, ConsoleProxyDaoImpl.class, ContrailElementImpl.class, ContrailGuru.class, ContrailManagerImpl.class, CounterDaoImpl.class, DataCenterDaoImpl.class, DataCenterDetailsDaoImpl.class, DataCenterIpAddressDaoImpl.class, DataCenterJoinDaoImpl.class, DataCenterLinkLocalIpAddressDaoImpl.class, DataCenterVnetDaoImpl.class, DcDetailsDaoImpl.class, DedicatedResourceDaoImpl.class, - DiskOfferingDaoImpl.class, DiskOfferingJoinDaoImpl.class, DomainDaoImpl.class, DomainManagerImpl.class, DomainRouterDaoImpl.class, DomainRouterJoinDaoImpl.class, + DiskOfferingDaoImpl.class, DiskOfferingJoinDaoImpl.class, DomainDaoImpl.class, DomainDetailsDaoImpl.class, DomainManagerImpl.class, DomainRouterDaoImpl.class, DomainRouterJoinDaoImpl.class, EventDaoImpl.class, EventJoinDaoImpl.class, EventUtils.class, ExtensionRegistry.class, FirewallManagerImpl.class, FirewallRulesCidrsDaoImpl.class, FirewallRulesDaoImpl.class, GuestOSCategoryDaoImpl.class, GuestOSDaoImpl.class, HostDaoImpl.class, HostDetailsDaoImpl.class, HostJoinDaoImpl.class, HostPodDaoImpl.class, HostTagsDaoImpl.class, HostTransferMapDaoImpl.class, HypervisorCapabilitiesDaoImpl.class, HypervisorGuruManagerImpl.class, diff --git a/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/MockAccountManager.java b/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/MockAccountManager.java index fa933244a66..37ca2bccff4 100644 --- a/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/MockAccountManager.java +++ b/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/MockAccountManager.java @@ -317,7 +317,13 @@ public class MockAccountManager extends ManagerBase implements AccountManager { return false; } - @Override public boolean moveUser(MoveUserCmd moveUserCmd) { + @Override + public boolean moveUser(MoveUserCmd moveUserCmd) { + return false; + } + + @Override + public boolean moveUser(long id, Long domainId, long accountId) { return false; } diff --git a/plugins/user-authenticators/ldap/pom.xml b/plugins/user-authenticators/ldap/pom.xml index 9f97f08f3fd..e2b0ead17e0 100644 --- a/plugins/user-authenticators/ldap/pom.xml +++ b/plugins/user-authenticators/ldap/pom.xml @@ -37,9 +37,9 @@ - test/groovy + test - **/*.groovy + groovy/**/*.groovy @@ -70,7 +70,8 @@ maven-surefire-plugin - **/*Spec* + **/*Spec.groovy + **/*Test.java @@ -90,6 +91,7 @@ + test diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LDAPConfigCmd.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LDAPConfigCmd.java index a138e7ddd4a..cfef21e2aff 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LDAPConfigCmd.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LDAPConfigCmd.java @@ -49,7 +49,7 @@ import com.cloud.utils.Pair; * @deprecated as of 4.3 use the new api {@link LdapAddConfigurationCmd} */ @Deprecated -@APICommand(name = "ldapConfig", description = "Configure the LDAP context for this site.", responseObject = LDAPConfigResponse.class, since = "3.0.0", +@APICommand(name = "ldapConfig", description = "(Deprecated, use addLdapConfiguration) Configure the LDAP context for this site.", responseObject = LDAPConfigResponse.class, since = "3.0.0", requestHasSensitiveInfo = true, responseHasSensitiveInfo = false) public class LDAPConfigCmd extends BaseCmd { @@ -190,8 +190,8 @@ public class LDAPConfigCmd extends BaseCmd { if (result.second() > 0) { boolean useSSlConfig = _ldapConfiguration.getSSLStatus(); - String searchBaseConfig = _ldapConfiguration.getBaseDn(); - String bindDnConfig = _ldapConfiguration.getBindPrincipal(); + String searchBaseConfig = _ldapConfiguration.getBaseDn(null); + String bindDnConfig = _ldapConfiguration.getBindPrincipal(null); for (LdapConfigurationVO ldapConfigurationVO : result.first()) { responses.add(createLDAPConfigResponse(ldapConfigurationVO.getHostname(), ldapConfigurationVO.getPort(), useSSlConfig, null, searchBaseConfig, bindDnConfig)); @@ -226,7 +226,7 @@ public class LDAPConfigCmd extends BaseCmd { } private boolean updateLDAP() { - _ldapManager.addConfiguration(hostname, port); + _ldapManager.addConfiguration(hostname, port, null); /** * There is no query filter now. It is derived from ldap.user.object and ldap.search.group.principle diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LDAPRemoveCmd.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LDAPRemoveCmd.java index eb3729d9d9e..0a4dc20ee0b 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LDAPRemoveCmd.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LDAPRemoveCmd.java @@ -35,7 +35,7 @@ import com.cloud.utils.Pair; * @deprecated as of 4.3 use the new api {@link LdapDeleteConfigurationCmd} */ @Deprecated -@APICommand(name = "ldapRemove", description = "Remove the LDAP context for this site.", responseObject = LDAPConfigResponse.class, since = "3.0.1", +@APICommand(name = "ldapRemove", description = "(Deprecated , use deleteLdapConfiguration) Remove the LDAP context for this site.", responseObject = LDAPConfigResponse.class, since = "3.0.1", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class LDAPRemoveCmd extends BaseCmd { public static final Logger s_logger = Logger.getLogger(LDAPRemoveCmd.class.getName()); @@ -60,7 +60,7 @@ public class LDAPRemoveCmd extends BaseCmd { LdapListConfigurationCmd listConfigurationCmd = new LdapListConfigurationCmd(_ldapManager); Pair, Integer> result = _ldapManager.listConfigurations(listConfigurationCmd); for (LdapConfigurationVO config : result.first()) { - _ldapManager.deleteConfiguration(config.getHostname()); + _ldapManager.deleteConfiguration(config.getHostname(), 0, null); } return true; } diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapAddConfigurationCmd.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapAddConfigurationCmd.java index 555d1a987fd..7c592888364 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapAddConfigurationCmd.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapAddConfigurationCmd.java @@ -18,6 +18,8 @@ package org.apache.cloudstack.api.command; import javax.inject.Inject; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.response.DomainResponse; import org.apache.log4j.Logger; import org.apache.cloudstack.api.APICommand; @@ -40,12 +42,15 @@ public class LdapAddConfigurationCmd extends BaseCmd { @Inject private LdapManager _ldapManager; - @Parameter(name = "hostname", type = CommandType.STRING, required = true, description = "Hostname") + @Parameter(name = ApiConstants.HOST_NAME, type = CommandType.STRING, required = true, description = "Hostname") private String hostname; - @Parameter(name = "port", type = CommandType.INTEGER, required = true, description = "Port") + @Parameter(name = ApiConstants.PORT, type = CommandType.INTEGER, required = true, description = "Port") private int port; + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, required = false, entityType = DomainResponse.class, description = "linked domain") + private Long domainId; + public LdapAddConfigurationCmd() { super(); } @@ -58,7 +63,7 @@ public class LdapAddConfigurationCmd extends BaseCmd { @Override public void execute() throws ServerApiException { try { - final LdapConfigurationResponse response = _ldapManager.addConfiguration(hostname, port); + final LdapConfigurationResponse response = _ldapManager.addConfiguration(hostname, port, domainId); response.setObjectName("LdapAddConfiguration"); response.setResponseName(getCommandName()); setResponseObject(response); @@ -86,6 +91,10 @@ public class LdapAddConfigurationCmd extends BaseCmd { return port; } + public Long getDomainId() { + return domainId; + } + public void setHostname(final String hostname) { this.hostname = hostname; } diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapCreateAccountCmd.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapCreateAccountCmd.java index d845857925d..6e69259ebdd 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapCreateAccountCmd.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapCreateAccountCmd.java @@ -139,7 +139,7 @@ public class LdapCreateAccountCmd extends BaseCmd { Long finalDomainId = getDomainId(); callContext.setEventDetails("Account Name: " + finalAccountName + ", Domain Id:" + finalDomainId); try { - final LdapUser user = _ldapManager.getUser(username); + final LdapUser user = _ldapManager.getUser(username, domainId); validateUser(user); final UserAccount userAccount = createCloudstackUserAccount(user, finalAccountName, finalDomainId); if (userAccount != null) { diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapDeleteConfigurationCmd.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapDeleteConfigurationCmd.java index 30b37d8b88d..3ffebecfb95 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapDeleteConfigurationCmd.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapDeleteConfigurationCmd.java @@ -18,6 +18,8 @@ package org.apache.cloudstack.api.command; import javax.inject.Inject; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.response.DomainResponse; import org.apache.log4j.Logger; import org.apache.cloudstack.api.APICommand; @@ -40,9 +42,16 @@ public class LdapDeleteConfigurationCmd extends BaseCmd { @Inject private LdapManager _ldapManager; - @Parameter(name = "hostname", type = CommandType.STRING, required = true, description = "Hostname") + + @Parameter(name = ApiConstants.HOST_NAME, type = CommandType.STRING, required = true, description = "Hostname") private String hostname; + @Parameter(name = ApiConstants.PORT, type = CommandType.INTEGER, required = false, description = "port") + private int port; + + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, required = false, entityType = DomainResponse.class, description = "linked domain") + private Long domainId; + public LdapDeleteConfigurationCmd() { super(); } @@ -52,10 +61,22 @@ public class LdapDeleteConfigurationCmd extends BaseCmd { _ldapManager = ldapManager; } + public String getHostname() { + return hostname; + } + + public int getPort() { + return port; + } + + public Long getDomainId() { + return domainId; + } + @Override public void execute() throws ServerApiException { try { - final LdapConfigurationResponse response = _ldapManager.deleteConfiguration(hostname); + final LdapConfigurationResponse response = _ldapManager.deleteConfiguration(this); response.setObjectName("LdapDeleteConfiguration"); response.setResponseName(getCommandName()); setResponseObject(response); diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapImportUsersCmd.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapImportUsersCmd.java index 9fdd700638c..564c1d0a1ef 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapImportUsersCmd.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapImportUsersCmd.java @@ -142,9 +142,9 @@ public class LdapImportUsersCmd extends BaseListCmd { try { if (StringUtils.isNotBlank(groupName)) { - users = _ldapManager.getUsersInGroup(groupName); + users = _ldapManager.getUsersInGroup(groupName, domainId); } else { - users = _ldapManager.getUsers(); + users = _ldapManager.getUsers(domainId); } } catch (NoLdapUserMatchingQueryException ex) { users = new ArrayList(); diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapListConfigurationCmd.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapListConfigurationCmd.java index 050fb36cb19..db6318e6b2c 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapListConfigurationCmd.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapListConfigurationCmd.java @@ -21,6 +21,8 @@ import java.util.List; import javax.inject.Inject; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.response.DomainResponse; import org.apache.log4j.Logger; import org.apache.cloudstack.api.APICommand; @@ -44,12 +46,15 @@ public class LdapListConfigurationCmd extends BaseListCmd { @Inject private LdapManager _ldapManager; - @Parameter(name = "hostname", type = CommandType.STRING, required = false, description = "Hostname") + @Parameter(name = ApiConstants. HOST_NAME, type = CommandType.STRING, required = false, description = "Hostname") private String hostname; - @Parameter(name = "port", type = CommandType.INTEGER, required = false, description = "Port") + @Parameter(name = ApiConstants.PORT, type = CommandType.INTEGER, required = false, description = "Port") private int port; + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, required = false, entityType = DomainResponse.class, description = "linked domain") + private Long domainId; + public LdapListConfigurationCmd() { super(); } @@ -97,6 +102,10 @@ public class LdapListConfigurationCmd extends BaseListCmd { return port; } + public Long getDomainId() { + return domainId; + } + public void setHostname(final String hostname) { this.hostname = hostname; } @@ -104,4 +113,8 @@ public class LdapListConfigurationCmd extends BaseListCmd { public void setPort(final int port) { this.port = port; } + + public void setDomainId(final Long domainId) { + this.domainId = domainId; + } } diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapListUsersCmd.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapListUsersCmd.java index e655f5f4ac0..b2266dc8fd3 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapListUsersCmd.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LdapListUsersCmd.java @@ -83,7 +83,7 @@ public class LdapListUsersCmd extends BaseListCmd { List ldapResponses = null; final ListResponse response = new ListResponse(); try { - final List users = _ldapManager.getUsers(); + final List users = _ldapManager.getUsers(null); ldapResponses = createLdapUserResponse(users); } catch (final NoLdapUserMatchingQueryException ex) { ldapResponses = new ArrayList(); diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LinkAccountToLdapCmd.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LinkAccountToLdapCmd.java new file mode 100644 index 00000000000..52adc664ff8 --- /dev/null +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LinkAccountToLdapCmd.java @@ -0,0 +1,142 @@ +/* + * 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 org.apache.cloudstack.api.command; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; +import com.cloud.user.User; +import com.cloud.user.UserAccount; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.LinkAccountToLdapResponse; +import org.apache.cloudstack.api.response.LinkDomainToLdapResponse; +import org.apache.cloudstack.ldap.LdapManager; +import org.apache.cloudstack.ldap.LdapUser; +import org.apache.cloudstack.ldap.NoLdapUserMatchingQueryException; +import org.apache.log4j.Logger; + +import javax.inject.Inject; +import java.util.UUID; + +@APICommand(name = LinkAccountToLdapCmd.APINAME, description = "link a cloudstack account to a group or OU in ldap", responseObject = LinkDomainToLdapResponse.class, since = "4.11.0", + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin,RoleType.DomainAdmin}) +public class LinkAccountToLdapCmd extends BaseCmd { + public static final Logger LOGGER = Logger.getLogger(LinkAccountToLdapCmd.class.getName()); + public static final String APINAME = "linkAccountToLdap"; + + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, required = true, entityType = DomainResponse.class, description = "The id of the domain that is to contain the linked account.") + private Long domainId; + + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, required = false, description = "type of the ldap name. GROUP or OU, defaults to GROUP") + private String type; + + @Parameter(name = ApiConstants.LDAP_DOMAIN, type = CommandType.STRING, required = true, description = "name of the group or OU in LDAP") + private String ldapDomain; + + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, required = true, description = "name of the account, it will be created if it does not exist") + private String accountName; + + @Parameter(name = ApiConstants.ADMIN, type = CommandType.STRING, required = false, description = "domain admin username in LDAP ") + private String admin; + + @Parameter(name = ApiConstants.ACCOUNT_TYPE, type = CommandType.SHORT, required = true, description = "Type of the account to auto import. Specify 0 for user and 2 for " + + "domain admin") + private short accountType; + + @Inject + private LdapManager _ldapManager; + + @Override + public void execute() throws ServerApiException { + try { + LinkAccountToLdapResponse response = _ldapManager.linkAccountToLdap(this); + if (admin != null) { + LdapUser ldapUser = null; + try { + ldapUser = _ldapManager.getUser(admin, type, ldapDomain, domainId); + } catch (NoLdapUserMatchingQueryException e) { + LOGGER.debug("no ldap user matching username " + admin + " in the given group/ou", e); + } + if (ldapUser != null && !ldapUser.isDisabled()) { + Account account = _accountService.getActiveAccountByName(admin, domainId); + if (account == null) { + try { + UserAccount userAccount = _accountService + .createUserAccount(admin, "", ldapUser.getFirstname(), ldapUser.getLastname(), ldapUser.getEmail(), null, admin, Account.ACCOUNT_TYPE_DOMAIN_ADMIN, RoleType.DomainAdmin.getId(), domainId, null, null, UUID.randomUUID().toString(), + UUID.randomUUID().toString(), User.Source.LDAP); + response.setAdminId(String.valueOf(userAccount.getAccountId())); + LOGGER.info("created an account with name " + admin + " in the given domain " + domainId); + } catch (Exception e) { + LOGGER.info("an exception occurred while creating account with name " + admin + " in domain " + domainId, e); + } + } else { + LOGGER.debug("an account with name " + admin + " already exists in the domain " + domainId); + } + } else { + LOGGER.debug("ldap user with username " + admin + " is disabled in the given group/ou"); + } + } + response.setObjectName(APINAME); + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (final InvalidParameterValueException e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.toString()); + } + } + + @Override + public String getCommandName() { + return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + public Long getDomainId() { + return domainId; + } + + public String getType() { + return type; + } + + public String getLdapDomain() { + return ldapDomain; + } + + public String getAccountName() { + return accountName; + } + + public String getAdmin() { + return admin; + } + + public short getAccountType() { + return accountType; + } +} diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LinkDomainToLdapCmd.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LinkDomainToLdapCmd.java index 477e80f2556..00140952051 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LinkDomainToLdapCmd.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LinkDomainToLdapCmd.java @@ -54,6 +54,10 @@ public class LinkDomainToLdapCmd extends BaseCmd { @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, required = true, description = "type of the ldap name. GROUP or OU") private String type; + @Parameter(name = ApiConstants.LDAP_DOMAIN, type = CommandType.STRING, required = true, description = "name of the group or OU in LDAP") + private String ldapDomain; + + @Deprecated @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "name of the group or OU in LDAP") private String name; @@ -67,14 +71,35 @@ public class LinkDomainToLdapCmd extends BaseCmd { @Inject private LdapManager _ldapManager; + public Long getDomainId() { + return domainId; + } + + public String getType() { + return type; + } + + public String getLdapDomain() { + return ldapDomain == null ? name : ldapDomain; + } + + public String getAdmin() { + return admin; + } + + public short getAccountType() { + return accountType; + } + + @Override public void execute() throws ServerApiException { try { - LinkDomainToLdapResponse response = _ldapManager.linkDomainToLdap(domainId, type, name, accountType); + LinkDomainToLdapResponse response = _ldapManager.linkDomainToLdap(this); if(admin!=null) { LdapUser ldapUser = null; try { - ldapUser = _ldapManager.getUser(admin, type, name); + ldapUser = _ldapManager.getUser(admin, type, getLdapDomain(), domainId); } catch (NoLdapUserMatchingQueryException e) { s_logger.debug("no ldap user matching username " + admin + " in the given group/ou", e); } diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LdapConfigurationResponse.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LdapConfigurationResponse.java index a4e47828844..c6d2bf38cb2 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LdapConfigurationResponse.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LdapConfigurationResponse.java @@ -18,31 +18,44 @@ package org.apache.cloudstack.api.response; import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; import com.cloud.serializer.Param; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.ldap.LdapConfiguration; +@EntityReference(value = LdapConfiguration.class) public class LdapConfigurationResponse extends BaseResponse { - @SerializedName("hostname") - @Param(description = "hostname") + @SerializedName(ApiConstants.HOST_NAME) + @Param(description = "name of the host running the ldap server") private String hostname; - @SerializedName("port") - @Param(description = "port") + @SerializedName(ApiConstants.PORT) + @Param(description = "port teh ldap server is running on") private int port; + @SerializedName(ApiConstants.DOMAIN_ID) + @Param(description = "linked domain") + private String domainId; + public LdapConfigurationResponse() { super(); } public LdapConfigurationResponse(final String hostname) { super(); - this.hostname = hostname; + setHostname(hostname); } public LdapConfigurationResponse(final String hostname, final int port) { - this.hostname = hostname; - this.port = port; + this(hostname); + setPort(port); + } + + public LdapConfigurationResponse(final String hostname, final int port, final String domainId) { + this(hostname, port); + setDomainId(domainId); } public String getHostname() { @@ -60,4 +73,12 @@ public class LdapConfigurationResponse extends BaseResponse { public void setPort(final int port) { this.port = port; } + + public String getDomainId() { + return domainId; + } + + public void setDomainId(String domainId) { + this.domainId = domainId; + } } \ No newline at end of file diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LinkAccountToLdapResponse.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LinkAccountToLdapResponse.java new file mode 100644 index 00000000000..23456e71641 --- /dev/null +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LinkAccountToLdapResponse.java @@ -0,0 +1,85 @@ +/* + * 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 org.apache.cloudstack.api.response; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; + +public class LinkAccountToLdapResponse extends BaseResponse { + + @SerializedName(ApiConstants.DOMAIN_ID) + @Param(description = "id of the Domain which is linked to LDAP") + private String domainId; + + @SerializedName(ApiConstants.LDAP_DOMAIN) + @Param(description = "name of the group or OU in LDAP which is linked to the domain") + private String ldapDomain; + + @SerializedName(ApiConstants.TYPE) + @Param(description = "type of the name in LDAP which is linke to the domain") + private String type; + + @SerializedName(ApiConstants.ACCOUNT_TYPE) + @Param(description = "Type of the account to auto import") + private short accountType; + + @SerializedName(ApiConstants.ACCOUNT_ID) + @Param(description = "Domain Admin accountId that is created") + private String adminId; + + @SerializedName(ApiConstants.ACCOUNT) + @Param(description = "name of the account") + private String accountName; + + + public LinkAccountToLdapResponse(String domainId, String type, String ldapDomain, short accountType, String adminId, String accountName) { + this.domainId = domainId; + this.type = type; + this.ldapDomain = ldapDomain; + this.accountType = accountType; + this.adminId = adminId; + this.accountName = accountName; + } + + public String getDomainId() { + return domainId; + } + + public String getLdapDomain() { + return ldapDomain; + } + + public String getType() { + return type; + } + + public short getAccountType() { + return accountType; + } + + public String getAdminId() { + return adminId; + } + + public void setAdminId(String adminId) { + this.adminId = adminId; + } +} diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LinkDomainToLdapResponse.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LinkDomainToLdapResponse.java index 050eb6c3eb5..d6d4b55e257 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LinkDomainToLdapResponse.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LinkDomainToLdapResponse.java @@ -66,11 +66,6 @@ public class LinkDomainToLdapResponse extends BaseResponse { return ldapDomain == null ? name : ldapDomain; } - @Deprecated - public String getName() { - return ldapDomain == null ? name : ldapDomain; - } - public String getType() { return type; } diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/ADLdapUserManagerImpl.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/ADLdapUserManagerImpl.java index 0df638ad228..e844df57c1c 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/ADLdapUserManagerImpl.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/ADLdapUserManagerImpl.java @@ -36,38 +36,38 @@ public class ADLdapUserManagerImpl extends OpenLdapUserManagerImpl implements Ld private static final String MICROSOFT_AD_MEMBERS_FILTER = "memberOf"; @Override - public List getUsersInGroup(String groupName, LdapContext context) throws NamingException { + public List getUsersInGroup(String groupName, LdapContext context, Long domainId) throws NamingException { if (StringUtils.isBlank(groupName)) { throw new IllegalArgumentException("ldap group name cannot be blank"); } - String basedn = _ldapConfiguration.getBaseDn(); + String basedn = _ldapConfiguration.getBaseDn(domainId); if (StringUtils.isBlank(basedn)) { throw new IllegalArgumentException("ldap basedn is not configured"); } final SearchControls searchControls = new SearchControls(); searchControls.setSearchScope(_ldapConfiguration.getScope()); - searchControls.setReturningAttributes(_ldapConfiguration.getReturnAttributes()); + searchControls.setReturningAttributes(_ldapConfiguration.getReturnAttributes(domainId)); - NamingEnumeration results = context.search(basedn, generateADGroupSearchFilter(groupName), searchControls); + NamingEnumeration results = context.search(basedn, generateADGroupSearchFilter(groupName, domainId), searchControls); final List users = new ArrayList(); while (results.hasMoreElements()) { final SearchResult result = results.nextElement(); - users.add(createUser(result)); + users.add(createUser(result, domainId)); } return users; } - private String generateADGroupSearchFilter(String groupName) { + private String generateADGroupSearchFilter(String groupName, Long domainId) { final StringBuilder userObjectFilter = new StringBuilder(); userObjectFilter.append("(objectClass="); - userObjectFilter.append(_ldapConfiguration.getUserObject()); + userObjectFilter.append(_ldapConfiguration.getUserObject(domainId)); userObjectFilter.append(")"); final StringBuilder memberOfFilter = new StringBuilder(); - String groupCnName = _ldapConfiguration.getCommonNameAttribute() + "=" +groupName + "," + _ldapConfiguration.getBaseDn(); - memberOfFilter.append("(").append(getMemberOfAttribute()).append("="); + String groupCnName = _ldapConfiguration.getCommonNameAttribute() + "=" +groupName + "," + _ldapConfiguration.getBaseDn(domainId); + memberOfFilter.append("(").append(getMemberOfAttribute(domainId)).append("="); memberOfFilter.append(groupCnName); memberOfFilter.append(")"); @@ -94,8 +94,8 @@ public class ADLdapUserManagerImpl extends OpenLdapUserManagerImpl implements Ld return isDisabledUser; } - protected String getMemberOfAttribute() { - if(_ldapConfiguration.isNestedGroupsEnabled()) { + protected String getMemberOfAttribute(final Long domainId) { + if(_ldapConfiguration.isNestedGroupsEnabled(domainId)) { return MICROSOFT_AD_NESTED_MEMBERS_FILTER; } else { return MICROSOFT_AD_MEMBERS_FILTER; diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapAuthenticator.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapAuthenticator.java index add39c5b13d..cd4ed3d5cea 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapAuthenticator.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapAuthenticator.java @@ -16,6 +16,8 @@ // under the License. package org.apache.cloudstack.ldap; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.UUID; @@ -56,60 +58,181 @@ public class LdapAuthenticator extends AdapterBase implements UserAuthenticator @Override public Pair authenticate(final String username, final String password, final Long domainId, final Map requestParameters) { + Pair rc = new Pair(false, null); + // TODO not allowing an empty password is a policy we shouldn't decide on. A private cloud may well want to allow this. if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) { s_logger.debug("Username or Password cannot be empty"); - return new Pair(false, null); + return rc; } - boolean result = false; - ActionOnFailedAuthentication action = null; - if (_ldapManager.isLdapEnabled()) { final UserAccount user = _userAccountDao.getUserAccount(username, domainId); - LdapTrustMapVO ldapTrustMapVO = _ldapManager.getDomainLinkedToLdap(domainId); - if(ldapTrustMapVO != null) { - try { - LdapUser ldapUser = _ldapManager.getUser(username, ldapTrustMapVO.getType().toString(), ldapTrustMapVO.getName()); - if(!ldapUser.isDisabled()) { - result = _ldapManager.canAuthenticate(ldapUser.getPrincipal(), password); - if(result) { - if(user == null) { - // import user to cloudstack - createCloudStackUserAccount(ldapUser, domainId, ldapTrustMapVO.getAccountType()); - } else { - enableUserInCloudStack(user); - } - } - } else { - //disable user in cloudstack - disableUserInCloudStack(user); - } - } catch (NoLdapUserMatchingQueryException e) { - s_logger.debug(e.getMessage()); + List ldapTrustMapVOs = _ldapManager.getDomainLinkage(domainId); + if(ldapTrustMapVOs != null && ldapTrustMapVOs.size() > 0) { + if(ldapTrustMapVOs.size() == 1 && ldapTrustMapVOs.get(0).getAccountId() == 0) { + // We have a single mapping of a domain to an ldap group or ou + return authenticate(username, password, domainId, user, ldapTrustMapVOs.get(0)); + } else { + // we are dealing with mapping of accounts in a domain to ldap groups + return authenticate(username, password, domainId, user, ldapTrustMapVOs); } - } else { //domain is not linked to ldap follow normal authentication - if(user != null ) { - try { - LdapUser ldapUser = _ldapManager.getUser(username); - if(!ldapUser.isDisabled()) { - result = _ldapManager.canAuthenticate(ldapUser.getPrincipal(), password); - } else { - s_logger.debug("user with principal "+ ldapUser.getPrincipal() + " is disabled in ldap"); - } - } catch (NoLdapUserMatchingQueryException e) { - s_logger.debug(e.getMessage()); - } - } - } - if (!result && user != null) { - action = ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT; + return authenticate(username, password, domainId, user); } } - return new Pair(result, action); + return rc; + } + + /** + * checks if the user exists in ldap and create in cloudstack if needed. + * + * @param username login id + * @param password pass phrase + * @param domainId domain the user is trying to log on to + * @param userAccount cloudstack user object + * @param ldapTrustMapVOs the trust mappings of accounts in the domain to ldap groups + * @return false if the ldap user object does not exist, is not mapped to an account, is mapped to multiple accounts or if authenitication fails + */ + private Pair authenticate(String username, String password, Long domainId, UserAccount userAccount, List ldapTrustMapVOs) { + Pair rc = new Pair(false, null); + try { + LdapUser ldapUser = _ldapManager.getUser(username, domainId); + List memberships = ldapUser.getMemberships(); + List mappedGroups = getMappedGroups(ldapTrustMapVOs); + mappedGroups.retainAll(memberships); + // check membership, there must be only one match in this domain + if(ldapUser.isDisabled()) { + logAndDisable(userAccount, "attempt to log on using disabled ldap user " + userAccount.getUsername(), false); + } else if(mappedGroups.size() > 1) { + logAndDisable(userAccount, "user '" + username + "' is mapped to more then one account in domain and will be disabled.", false); + } else if(mappedGroups.size() < 1) { + logAndDisable(userAccount, "user '" + username + "' is not mapped to an account in domain and will be removed.", true); + } else { + // a valid ldap configured user exists + LdapTrustMapVO mapping = _ldapManager.getLinkedLdapGroup(domainId,mappedGroups.get(0)); + // we could now assert that ldapTrustMapVOs.contains(mapping); + // createUser in Account can only be done by account name not by account id + String accountName = _accountManager.getAccount(mapping.getAccountId()).getAccountName(); + rc.first(_ldapManager.canAuthenticate(ldapUser.getPrincipal(), password, domainId)); + // for security reasons we keep processing on faulty login attempt to not give a way information on userid existence + if (userAccount == null) { + // new user that is in ldap; authenticate and create + User user = _accountManager.createUser(username, "", ldapUser.getFirstname(), ldapUser.getLastname(), ldapUser.getEmail(), null, accountName, + domainId, UUID.randomUUID().toString(), User.Source.LDAP); + /* expected error conditions: + * + * caught in APIServlet: CloudRuntimeException("The domain " + domainId + " does not exist; unable to create user"); + * caught in APIServlet: CloudRuntimeException("The user cannot be created as domain " + domain.getName() + " is being deleted"); + * would have been thrown above: InvalidParameterValueException("Unable to find account " + accountName + " in domain id=" + domainId + " to create user"); + * we are system user: PermissionDeniedException("Account id : " + account.getId() + " is a system account, can't add a user to it"); + * serious and must be thrown: CloudRuntimeException("The user " + userName + " already exists in domain " + domainId); + * fatal system error and must be thrown: CloudRuntimeException("Failed to encode password"); + */ + userAccount = _accountManager.getUserAccountById(user.getId()); + } else { + // not a new user, check if mapped group has changed + if(userAccount.getAccountId() != mapping.getAccountId()) { + _accountManager.moveUser(userAccount.getId(),userAccount.getDomainId(),mapping.getAccountId()); + } + // else { the user hasn't changed in ldap, the ldap group stayed the same, hurray, pass, fun thou self a lot of fun } + } + } + } catch (NoLdapUserMatchingQueryException e) { + s_logger.debug(e.getMessage()); + disableUserInCloudStack(userAccount); + } + + return rc; + } + + private void logAndDisable(UserAccount userAccount, String msg, boolean remove) { + if (s_logger.isInfoEnabled()) { + s_logger.info(msg); + } + if(remove) { + removeUserInCloudStack(userAccount); + } else { + disableUserInCloudStack(userAccount); + } + } + + private List getMappedGroups(List ldapTrustMapVOs) { + List groups = new ArrayList<>(); + for (LdapTrustMapVO vo : ldapTrustMapVOs) { + groups.add(vo.getName()); + } + return groups; + } + + /** + * checks if the user exists in ldap and create in cloudstack if needed + * @param username login id + * @param password pass phrase + * @param domainId domain the user is trying to log on to + * @param user cloudstack user object + * @param ldapTrustMapVO the trust mapping for the domain to the ldap group + * @return false if the ldap user object does not exist or authenitication fails + */ + private Pair authenticate(String username, String password, Long domainId, UserAccount user, LdapTrustMapVO ldapTrustMapVO) { + Pair rc = new Pair(false, null); + try { + LdapUser ldapUser = _ldapManager.getUser(username, ldapTrustMapVO.getType().toString(), ldapTrustMapVO.getName(), domainId); + final short accountType = ldapTrustMapVO.getAccountType(); + processLdapUser(password, domainId, user, rc, ldapUser, accountType); + } catch (NoLdapUserMatchingQueryException e) { + s_logger.debug(e.getMessage()); + } + return rc; + } + + private void processLdapUser(String password, Long domainId, UserAccount user, Pair rc, LdapUser ldapUser, short accountType) { + if(!ldapUser.isDisabled()) { + rc.first(_ldapManager.canAuthenticate(ldapUser.getPrincipal(), password, domainId)); + if(rc.first()) { + if(user == null) { + // import user to cloudstack + createCloudStackUserAccount(ldapUser, domainId, accountType); + } else { + enableUserInCloudStack(user); + } + } else if(user != null) { + rc.second(ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT); + } + } else { + //disable user in cloudstack + disableUserInCloudStack(user); + } + } + + /** + * checks if the user is configured both in ldap and in cloudstack. + * @param username login id + * @param password pass phrase + * @param domainId domain the user is trying to log on to + * @param user cloudstack user object + * @return false if either user object does not exist or authenitication fails + */ + private Pair authenticate(String username, String password, Long domainId, UserAccount user) { + boolean result = false; + + if(user != null ) { + try { + LdapUser ldapUser = _ldapManager.getUser(username, domainId); + if(!ldapUser.isDisabled()) { + result = _ldapManager.canAuthenticate(ldapUser.getPrincipal(), password, domainId); + } else { + s_logger.debug("user with principal "+ ldapUser.getPrincipal() + " is disabled in ldap"); + } + } catch (NoLdapUserMatchingQueryException e) { + s_logger.debug(e.getMessage()); + } + } + return (!result && user != null) ? + new Pair(false, ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT): + new Pair(false, null); } private void enableUserInCloudStack(UserAccount user) { @@ -131,6 +254,12 @@ public class LdapAuthenticator extends AdapterBase implements UserAuthenticator } } + private void removeUserInCloudStack(UserAccount user) { + if (user != null) { + _accountManager.disableUser(user.getId()); + } + } + @Override public String encode(final String password) { return password; diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapConfiguration.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapConfiguration.java index 56b39a8b3d1..22f8abc9aa5 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapConfiguration.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapConfiguration.java @@ -31,75 +31,215 @@ import org.apache.cloudstack.ldap.dao.LdapConfigurationDao; public class LdapConfiguration implements Configurable{ private final static String factory = "com.sun.jndi.ldap.LdapCtxFactory"; - private static final ConfigKey ldapReadTimeout = new ConfigKey(Long.class, "ldap.read.timeout", "Advanced", "1000", - "LDAP connection Timeout in milli sec", true, ConfigKey.Scope.Global, 1l); + private static final ConfigKey ldapReadTimeout = new ConfigKey( + Long.class, + "ldap.read.timeout", + "Advanced", + "1000", + "LDAP connection Timeout in milli sec", + true, + ConfigKey.Scope.Domain, + 1l); - private static final ConfigKey ldapPageSize = new ConfigKey(Integer.class, "ldap.request.page.size", "Advanced", "1000", - "page size sent to ldap server on each request to get user", true, ConfigKey.Scope.Global, 1); - private static final ConfigKey ldapProvider = new ConfigKey(String.class, "ldap.provider", "Advanced", "openldap", "ldap provider ex:openldap, microsoftad", - true, ConfigKey.Scope.Global, null); + private static final ConfigKey ldapPageSize = new ConfigKey( + Integer.class, + "ldap.request.page.size", + "Advanced", + "1000", + "page size sent to ldap server on each request to get user", + true, + ConfigKey.Scope.Domain, + 1); - private static final ConfigKey ldapEnableNestedGroups = new ConfigKey(Boolean.class, "ldap.nested.groups.enable", "Advanced", "true", - "if true, nested groups will also be queried", true, ConfigKey.Scope.Global, null); + private static final ConfigKey ldapEnableNestedGroups = new ConfigKey( + "Advanced", + Boolean.class, + "ldap.nested.groups.enable", + "true", + "if true, nested groups will also be queried", + true, + ConfigKey.Scope.Domain); + + private static final ConfigKey ldapMemberOfAttribute = new ConfigKey( + "Advanced", + String.class, + "ldap.user.memberof.attribute", + "memberof", + "the reverse membership attibute for group members", + true, + ConfigKey.Scope.Domain); + + private static final ConfigKey ldapProvider = new ConfigKey( + "Advanced", + String.class, + "ldap.provider", + "openldap", + "ldap provider ex:openldap, microsoftad", + true, + ConfigKey.Scope.Domain); + + private static final ConfigKey ldapBaseDn = new ConfigKey( + "Advanced", + String.class, + "ldap.basedn", + null, + "Sets the basedn for LDAP", + true, + ConfigKey.Scope.Domain); + + private static final ConfigKey ldapBindPassword = new ConfigKey( + "Advanced", + String.class, + "ldap.bind.password", + null, + "Sets the bind password for LDAP", + true, + ConfigKey.Scope.Domain); + private static final ConfigKey ldapBindPrincipal = new ConfigKey( + "Advanced", + String.class, + "ldap.bind.principal", + null, + "Sets the bind principal for LDAP", + true, + ConfigKey.Scope.Domain); + private static final ConfigKey ldapEmailAttribute = new ConfigKey( + "Advanced", + String.class, + "ldap.email.attribute", + "mail", + "Sets the email attribute used within LDAP", + true, + ConfigKey.Scope.Domain); + private static final ConfigKey ldapFirstnameAttribute = new ConfigKey( + "Advanced", + String.class, + "ldap.firstname.attribute", + "givenname", + "Sets the firstname attribute used within LDAP", + true, + ConfigKey.Scope.Domain); + private static final ConfigKey ldapLastnameAttribute = new ConfigKey( + "Advanced", + String.class, "ldap.lastname.attribute", + "sn", + "Sets the lastname attribute used within LDAP", + true, + ConfigKey.Scope.Domain); + private static final ConfigKey ldapUsernameAttribute = new ConfigKey( + "Advanced", + String.class, + "ldap.username.attribute", + "uid", + "Sets the username attribute used within LDAP", + true, + ConfigKey.Scope.Domain); + private static final ConfigKey ldapUserObject = new ConfigKey( + "Advanced", + String.class, + "ldap.user.object", + "inetOrgPerson", + "Sets the object type of users within LDAP", + true, + ConfigKey.Scope.Domain); + private static final ConfigKey ldapSearchGroupPrinciple = new ConfigKey( + "Advanced", + String.class, + "ldap.search.group.principle", + null, + "Sets the principle of the group that users must be a member of", + true, + ConfigKey.Scope.Domain); + private static final ConfigKey ldapGroupObject = new ConfigKey( + "Advanced", + String.class, + "ldap.group.object", + "groupOfUniqueNames", + "Sets the object type of groups within LDAP", + true, + ConfigKey.Scope.Domain); + private static final ConfigKey ldapGroupUniqueMemberAttribute = new ConfigKey( + "Advanced", + String.class, + "ldap.group.user.uniquemember", + "uniquemember", + "Sets the attribute for uniquemembers within a group", + true, + ConfigKey.Scope.Domain); + + private static final ConfigKey ldapTrustStore = new ConfigKey( + "Advanced", + String.class, + "ldap.truststore", + null, + "Sets the path to the truststore to use for SSL", + true, + ConfigKey.Scope.Domain); + private static final ConfigKey ldapTrustStorePassword = new ConfigKey( + "Advanced", + String.class, + "ldap.truststore.password", + null, + "Sets the password for the truststore", + true, + ConfigKey.Scope.Domain); private final static int scope = SearchControls.SUBTREE_SCOPE; - @Inject - private ConfigurationDao _configDao; - @Inject private LdapConfigurationDao _ldapConfigurationDao; public LdapConfiguration() { } - public LdapConfiguration(final ConfigurationDao configDao, final LdapConfigurationDao ldapConfigurationDao) { - _configDao = configDao; + public LdapConfiguration(final LdapConfigurationDao ldapConfigurationDao) { _ldapConfigurationDao = ldapConfigurationDao; } - public String getAuthentication() { - if ((getBindPrincipal() == null) && (getBindPassword() == null)) { + @Deprecated + public LdapConfiguration(final ConfigurationDao configDao, final LdapConfigurationDao ldapConfigurationDao) { + _ldapConfigurationDao = ldapConfigurationDao; + } + + public String getAuthentication(final Long domainId) { + if ((getBindPrincipal(domainId) == null) && (getBindPassword(domainId) == null)) { return "none"; } else { return "simple"; } } - public String getBaseDn() { - return _configDao.getValue("ldap.basedn"); + public String getBaseDn(final Long domainId) { + return ldapBaseDn.valueIn(domainId); } - public String getBindPassword() { - return _configDao.getValue("ldap.bind.password"); + public String getBindPassword(final Long domainId) { + return ldapBindPassword.valueIn(domainId); } - public String getBindPrincipal() { - return _configDao.getValue("ldap.bind.principal"); + public String getBindPrincipal(final Long domainId) { + return ldapBindPrincipal.valueIn(domainId); } - public String getEmailAttribute() { - final String emailAttribute = _configDao.getValue("ldap.email.attribute"); - return emailAttribute == null ? "mail" : emailAttribute; + public String getEmailAttribute(final Long domainId) { + return ldapEmailAttribute.valueIn(domainId); } public String getFactory() { return factory; } - public String getFirstnameAttribute() { - final String firstnameAttribute = _configDao.getValue("ldap.firstname.attribute"); - return firstnameAttribute == null ? "givenname" : firstnameAttribute; + public String getFirstnameAttribute(final Long domainId) { + return ldapFirstnameAttribute.valueIn(domainId); } - public String getLastnameAttribute() { - final String lastnameAttribute = _configDao.getValue("ldap.lastname.attribute"); - return lastnameAttribute == null ? "sn" : lastnameAttribute; + public String getLastnameAttribute(final Long domainId) { + return ldapLastnameAttribute.valueIn(domainId); } - public String getProviderUrl() { + public String getProviderUrl(final Long domainId) { final String protocol = getSSLStatus() == true ? "ldaps://" : "ldap://"; - final Pair, Integer> result = _ldapConfigurationDao.searchConfigurations(null, 0); + final Pair, Integer> result = _ldapConfigurationDao.searchConfigurations(null, 0, domainId); final StringBuilder providerUrls = new StringBuilder(); String delim = ""; for (final LdapConfigurationVO resource : result.first()) { @@ -110,17 +250,24 @@ public class LdapConfiguration implements Configurable{ return providerUrls.toString(); } - public String[] getReturnAttributes() { - return new String[] {getUsernameAttribute(), getEmailAttribute(), getFirstnameAttribute(), getLastnameAttribute(), getCommonNameAttribute(), - getUserAccountControlAttribute()}; + public String[] getReturnAttributes(final Long domainId) { + return new String[] { + getUsernameAttribute(domainId), + getEmailAttribute(domainId), + getFirstnameAttribute(domainId), + getLastnameAttribute(domainId), + getCommonNameAttribute(), + getUserAccountControlAttribute(), + getUserMemberOfAttribute(domainId) + }; } public int getScope() { return scope; } - public String getSearchGroupPrinciple() { - return _configDao.getValue("ldap.search.group.principle"); + public String getSearchGroupPrinciple(final Long domainId) { + return ldapSearchGroupPrinciple.valueIn(domainId); } public boolean getSSLStatus() { @@ -132,53 +279,51 @@ public class LdapConfiguration implements Configurable{ } public String getTrustStore() { - return _configDao.getValue("ldap.truststore"); + return ldapTrustStore.value(); } public String getTrustStorePassword() { - return _configDao.getValue("ldap.truststore.password"); + return ldapTrustStorePassword.value(); } - public String getUsernameAttribute() { - final String usernameAttribute = _configDao.getValue("ldap.username.attribute"); - return usernameAttribute == null ? "uid" : usernameAttribute; + public String getUsernameAttribute(final Long domainId) { + return ldapUsernameAttribute.valueIn(domainId); } - public String getUserObject() { - final String userObject = _configDao.getValue("ldap.user.object"); - return userObject == null ? "inetOrgPerson" : userObject; + public String getUserObject(final Long domainId) { + return ldapUserObject.valueIn(domainId); } - public String getGroupObject() { - final String groupObject = _configDao.getValue("ldap.group.object"); - return groupObject == null ? "groupOfUniqueNames" : groupObject; + public String getGroupObject(final Long domainId) { + return ldapGroupObject.valueIn(domainId); } - public String getGroupUniqueMemeberAttribute() { - final String uniqueMemberAttribute = _configDao.getValue("ldap.group.user.uniquemember"); - return uniqueMemberAttribute == null ? "uniquemember" : uniqueMemberAttribute; + public String getGroupUniqueMemberAttribute(final Long domainId) { + return ldapGroupUniqueMemberAttribute.valueIn(domainId); } + // TODO remove hard-coding public String getCommonNameAttribute() { return "cn"; } + // TODO remove hard-coding public String getUserAccountControlAttribute() { return "userAccountControl"; } - public Long getReadTimeout() { - return ldapReadTimeout.value(); + public Long getReadTimeout(final Long domainId) { + return ldapReadTimeout.valueIn(domainId); } - public Integer getLdapPageSize() { - return ldapPageSize.value(); + public Integer getLdapPageSize(final Long domainId) { + return ldapPageSize.valueIn(domainId); } - public LdapUserManager.Provider getLdapProvider() { + public LdapUserManager.Provider getLdapProvider(final Long domainId) { LdapUserManager.Provider provider; try { - provider = LdapUserManager.Provider.valueOf(ldapProvider.value().toUpperCase()); + provider = LdapUserManager.Provider.valueOf(ldapProvider.valueIn(domainId).toUpperCase()); } catch (IllegalArgumentException ex) { //openldap is the default provider = LdapUserManager.Provider.OPENLDAP; @@ -186,8 +331,12 @@ public class LdapConfiguration implements Configurable{ return provider; } - public boolean isNestedGroupsEnabled() { - return ldapEnableNestedGroups.value(); + public boolean isNestedGroupsEnabled(final Long domainId) { + return ldapEnableNestedGroups.valueIn(domainId); + } + + public static String getUserMemberOfAttribute(final Long domainId) { + return ldapMemberOfAttribute.valueIn(domainId); } @Override @@ -197,6 +346,25 @@ public class LdapConfiguration implements Configurable{ @Override public ConfigKey[] getConfigKeys() { - return new ConfigKey[] {ldapReadTimeout, ldapPageSize, ldapProvider, ldapEnableNestedGroups}; + return new ConfigKey[]{ + ldapReadTimeout, + ldapPageSize, + ldapProvider, + ldapEnableNestedGroups, + ldapBaseDn, + ldapBindPassword, + ldapBindPrincipal, + ldapEmailAttribute, + ldapFirstnameAttribute, + ldapLastnameAttribute, + ldapUsernameAttribute, + ldapUserObject, + ldapSearchGroupPrinciple, + ldapGroupObject, + ldapGroupUniqueMemberAttribute, + ldapTrustStore, + ldapTrustStorePassword, + ldapMemberOfAttribute + }; } -} \ No newline at end of file +} diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapConfigurationVO.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapConfigurationVO.java index 488e7f44485..e7db88675ab 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapConfigurationVO.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapConfigurationVO.java @@ -39,12 +39,16 @@ public class LdapConfigurationVO implements InternalIdentity { @Column(name = "port") private int port; + @Column(name = "domain_id") + private Long domainId; + public LdapConfigurationVO() { } - public LdapConfigurationVO(final String hostname, final int port) { + public LdapConfigurationVO(final String hostname, final int port, final Long domainId) { this.hostname = hostname; this.port = port; + this.domainId = domainId; } public String getHostname() { @@ -60,6 +64,10 @@ public class LdapConfigurationVO implements InternalIdentity { return port; } + public Long getDomainId() { + return domainId; + } + public void setId(final long id) { this.id = id; } diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapContextFactory.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapContextFactory.java index 9e27fff078e..b141f053008 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapContextFactory.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapContextFactory.java @@ -40,29 +40,31 @@ public class LdapContextFactory { _ldapConfiguration = ldapConfiguration; } - public LdapContext createBindContext() throws NamingException, IOException { - return createBindContext(null); + // TODO add optional domain (optional only for backwards compatibility) + public LdapContext createBindContext(Long domainId) throws NamingException, IOException { + return createBindContext(null, domainId); } - public LdapContext createBindContext(final String providerUrl) throws NamingException, IOException { - final String bindPrincipal = _ldapConfiguration.getBindPrincipal(); - final String bindPassword = _ldapConfiguration.getBindPassword(); - return createInitialDirContext(bindPrincipal, bindPassword, providerUrl, true); + // TODO add optional domain (optional only for backwards compatibility) + public LdapContext createBindContext(final String providerUrl, Long domainId) throws NamingException, IOException { + final String bindPrincipal = _ldapConfiguration.getBindPrincipal(domainId); + final String bindPassword = _ldapConfiguration.getBindPassword(domainId); + return createInitialDirContext(bindPrincipal, bindPassword, providerUrl, true, domainId); } - private LdapContext createInitialDirContext(final String principal, final String password, final boolean isSystemContext) throws NamingException, IOException { - return createInitialDirContext(principal, password, null, isSystemContext); + private LdapContext createInitialDirContext(final String principal, final String password, final boolean isSystemContext, Long domainId) throws NamingException, IOException { + return createInitialDirContext(principal, password, null, isSystemContext, domainId); } - private LdapContext createInitialDirContext(final String principal, final String password, final String providerUrl, final boolean isSystemContext) + private LdapContext createInitialDirContext(final String principal, final String password, final String providerUrl, final boolean isSystemContext, Long domainId) throws NamingException, IOException { - Hashtable environment = getEnvironment(principal, password, providerUrl, isSystemContext); + Hashtable environment = getEnvironment(principal, password, providerUrl, isSystemContext, domainId); s_logger.debug("initializing ldap with provider url: " + environment.get(Context.PROVIDER_URL)); return new InitialLdapContext(environment, null); } - public LdapContext createUserContext(final String principal, final String password) throws NamingException, IOException { - return createInitialDirContext(principal, password, false); + public LdapContext createUserContext(final String principal, final String password, Long domainId) throws NamingException, IOException { + return createInitialDirContext(principal, password, false, domainId); } private void enableSSL(final Hashtable environment) { @@ -76,19 +78,19 @@ public class LdapContextFactory { } } - private Hashtable getEnvironment(final String principal, final String password, final String providerUrl, final boolean isSystemContext) { + private Hashtable getEnvironment(final String principal, final String password, final String providerUrl, final boolean isSystemContext, Long domainId) { final String factory = _ldapConfiguration.getFactory(); - final String url = providerUrl == null ? _ldapConfiguration.getProviderUrl() : providerUrl; + final String url = providerUrl == null ? _ldapConfiguration.getProviderUrl(domainId) : providerUrl; final Hashtable environment = new Hashtable(); environment.put(Context.INITIAL_CONTEXT_FACTORY, factory); environment.put(Context.PROVIDER_URL, url); - environment.put("com.sun.jndi.ldap.read.timeout", _ldapConfiguration.getReadTimeout().toString()); + environment.put("com.sun.jndi.ldap.read.timeout", _ldapConfiguration.getReadTimeout(domainId).toString()); environment.put("com.sun.jndi.ldap.connect.pool", "true"); enableSSL(environment); - setAuthentication(environment, isSystemContext); + setAuthentication(environment, isSystemContext, domainId); if (principal != null) { environment.put(Context.SECURITY_PRINCIPAL, principal); @@ -101,8 +103,8 @@ public class LdapContextFactory { return environment; } - private void setAuthentication(final Hashtable environment, final boolean isSystemContext) { - final String authentication = _ldapConfiguration.getAuthentication(); + private void setAuthentication(final Hashtable environment, final boolean isSystemContext, final Long domainId) { + final String authentication = _ldapConfiguration.getAuthentication(domainId); if ("none".equals(authentication) && !isSystemContext) { environment.put(Context.SECURITY_AUTHENTICATION, "simple"); diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapManager.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapManager.java index 6af2c4ebd95..002242c8f02 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapManager.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapManager.java @@ -18,36 +18,48 @@ package org.apache.cloudstack.ldap; import java.util.List; +import org.apache.cloudstack.api.command.LdapAddConfigurationCmd; +import org.apache.cloudstack.api.command.LdapDeleteConfigurationCmd; import org.apache.cloudstack.api.command.LdapListConfigurationCmd; +import org.apache.cloudstack.api.command.LinkAccountToLdapCmd; +import org.apache.cloudstack.api.command.LinkDomainToLdapCmd; import org.apache.cloudstack.api.response.LdapConfigurationResponse; import org.apache.cloudstack.api.response.LdapUserResponse; import com.cloud.exception.InvalidParameterValueException; import com.cloud.utils.Pair; import com.cloud.utils.component.PluggableService; +import org.apache.cloudstack.api.response.LinkAccountToLdapResponse; import org.apache.cloudstack.api.response.LinkDomainToLdapResponse; public interface LdapManager extends PluggableService { enum LinkType { GROUP, OU;} - LdapConfigurationResponse addConfiguration(String hostname, int port) throws InvalidParameterValueException; + LdapConfigurationResponse addConfiguration(final LdapAddConfigurationCmd cmd) throws InvalidParameterValueException; - boolean canAuthenticate(String principal, String password); + @Deprecated + LdapConfigurationResponse addConfiguration(String hostname, int port, Long domainId) throws InvalidParameterValueException; + + boolean canAuthenticate(String principal, String password, final Long domainId); LdapConfigurationResponse createLdapConfigurationResponse(LdapConfigurationVO configuration); LdapUserResponse createLdapUserResponse(LdapUser user); - LdapConfigurationResponse deleteConfiguration(String hostname) throws InvalidParameterValueException; + LdapConfigurationResponse deleteConfiguration(LdapDeleteConfigurationCmd cmd) throws InvalidParameterValueException; - LdapUser getUser(final String username) throws NoLdapUserMatchingQueryException; + @Deprecated + LdapConfigurationResponse deleteConfiguration(String hostname, int port, Long domainId) throws InvalidParameterValueException; - LdapUser getUser(String username, String type, String name) throws NoLdapUserMatchingQueryException; + // TODO username is only unique withing domain scope (add domain id to call) + LdapUser getUser(final String username, Long domainId) throws NoLdapUserMatchingQueryException; - List getUsers() throws NoLdapUserMatchingQueryException; + LdapUser getUser(String username, String type, String name, Long domainId) throws NoLdapUserMatchingQueryException; - List getUsersInGroup(String groupName) throws NoLdapUserMatchingQueryException; + List getUsers(Long domainId) throws NoLdapUserMatchingQueryException; + + List getUsersInGroup(String groupName, Long domainId) throws NoLdapUserMatchingQueryException; boolean isLdapEnabled(); @@ -55,7 +67,15 @@ public interface LdapManager extends PluggableService { List searchUsers(String query) throws NoLdapUserMatchingQueryException; - LinkDomainToLdapResponse linkDomainToLdap(Long domainId, String type, String name, short accountType); + LinkDomainToLdapResponse linkDomainToLdap(LinkDomainToLdapCmd cmd); - public LdapTrustMapVO getDomainLinkedToLdap(long domainId); + LdapTrustMapVO getDomainLinkedToLdap(long domainId); + + List getDomainLinkage(long domainId); + + LdapTrustMapVO getAccountLinkedToLdap(long domainId, long accountId); + + LdapTrustMapVO getLinkedLdapGroup(long domainId, String group); + + LinkAccountToLdapResponse linkAccountToLdap(LinkAccountToLdapCmd linkAccountToLdapCmd); } \ No newline at end of file diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapManagerImpl.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapManagerImpl.java index beb7a61cd70..b82231c99d7 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapManagerImpl.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapManagerImpl.java @@ -23,6 +23,7 @@ import java.util.List; import javax.inject.Inject; import javax.naming.NamingException; import javax.naming.ldap.LdapContext; +import java.util.UUID; import org.apache.cloudstack.api.LdapValidator; import org.apache.cloudstack.api.command.LDAPConfigCmd; @@ -34,9 +35,11 @@ import org.apache.cloudstack.api.command.LdapImportUsersCmd; import org.apache.cloudstack.api.command.LdapListConfigurationCmd; import org.apache.cloudstack.api.command.LdapListUsersCmd; import org.apache.cloudstack.api.command.LdapUserSearchCmd; +import org.apache.cloudstack.api.command.LinkAccountToLdapCmd; import org.apache.cloudstack.api.command.LinkDomainToLdapCmd; import org.apache.cloudstack.api.response.LdapConfigurationResponse; import org.apache.cloudstack.api.response.LdapUserResponse; +import org.apache.cloudstack.api.response.LinkAccountToLdapResponse; import org.apache.cloudstack.api.response.LinkDomainToLdapResponse; import org.apache.cloudstack.ldap.dao.LdapConfigurationDao; import org.apache.cloudstack.ldap.dao.LdapTrustMapDao; @@ -47,6 +50,9 @@ import org.springframework.stereotype.Component; import com.cloud.domain.DomainVO; import com.cloud.domain.dao.DomainDao; import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; +import com.cloud.user.AccountVO; +import com.cloud.user.dao.AccountDao; import com.cloud.utils.Pair; @Component @@ -59,6 +65,9 @@ public class LdapManagerImpl implements LdapManager, LdapValidator { @Inject private DomainDao domainDao; + @Inject + private AccountDao accountDao; + @Inject private LdapContextFactory _ldapContextFactory; @@ -85,17 +94,32 @@ public class LdapManagerImpl implements LdapManager, LdapValidator { } @Override - public LdapConfigurationResponse addConfiguration(final String hostname, final int port) throws InvalidParameterValueException { - LdapConfigurationVO configuration = _ldapConfigurationDao.findByHostname(hostname); + public LdapConfigurationResponse addConfiguration(final LdapAddConfigurationCmd cmd) throws InvalidParameterValueException { + return addConfigurationInternal(cmd.getHostname(),cmd.getPort(),cmd.getDomainId()); + } + + @Override // TODO make private + public LdapConfigurationResponse addConfiguration(final String hostname, int port, final Long domainId) throws InvalidParameterValueException { + return addConfigurationInternal(hostname,port,domainId); + } + + private LdapConfigurationResponse addConfigurationInternal(final String hostname, int port, final Long domainId) throws InvalidParameterValueException { + // TODO evaluate what the right default should be + if(port <= 0) { + port = 389; + } + + // hostname:port is unique for domain binding + LdapConfigurationVO configuration = _ldapConfigurationDao.find(hostname, port, domainId); if (configuration == null) { LdapContext context = null; try { final String providerUrl = "ldap://" + hostname + ":" + port; - context = _ldapContextFactory.createBindContext(providerUrl); - configuration = new LdapConfigurationVO(hostname, port); + context = _ldapContextFactory.createBindContext(providerUrl,domainId); + configuration = new LdapConfigurationVO(hostname, port, domainId); _ldapConfigurationDao.persist(configuration); - s_logger.info("Added new ldap server with hostname: " + hostname); - return new LdapConfigurationResponse(hostname, port); + s_logger.info("Added new ldap server with url: " + providerUrl + (domainId == null ? "": " for domain " + domainId)); + return createLdapConfigurationResponse(configuration); } catch (NamingException | IOException e) { s_logger.debug("NamingException while doing an LDAP bind", e); throw new InvalidParameterValueException("Unable to bind to the given LDAP server"); @@ -107,10 +131,18 @@ public class LdapManagerImpl implements LdapManager, LdapValidator { } } + /** + * TODO decide if the principal is good enough to get the domain id or we need to add it as parameter + * @param principal + * @param password + * @param domainId + * @return + */ @Override - public boolean canAuthenticate(final String principal, final String password) { + public boolean canAuthenticate(final String principal, final String password, final Long domainId) { try { - final LdapContext context = _ldapContextFactory.createUserContext(principal, password); + // TODO return the right account for this user + final LdapContext context = _ldapContextFactory.createUserContext(principal, password,domainId); closeContext(context); return true; } catch (NamingException | IOException e) { @@ -132,10 +164,11 @@ public class LdapManagerImpl implements LdapManager, LdapValidator { @Override public LdapConfigurationResponse createLdapConfigurationResponse(final LdapConfigurationVO configuration) { - final LdapConfigurationResponse response = new LdapConfigurationResponse(); - response.setHostname(configuration.getHostname()); - response.setPort(configuration.getPort()); - return response; + String domainUuid = null; + if(configuration.getDomainId() != null) { + domainUuid = domainDao.findById(configuration.getDomainId()).getUuid(); + } + return new LdapConfigurationResponse(configuration.getHostname(), configuration.getPort(), domainUuid); } @Override @@ -151,14 +184,23 @@ public class LdapManagerImpl implements LdapManager, LdapValidator { } @Override - public LdapConfigurationResponse deleteConfiguration(final String hostname) throws InvalidParameterValueException { - final LdapConfigurationVO configuration = _ldapConfigurationDao.findByHostname(hostname); + public LdapConfigurationResponse deleteConfiguration(final LdapDeleteConfigurationCmd cmd) throws InvalidParameterValueException { + return deleteConfigurationInternal(cmd.getHostname(), cmd.getPort(), cmd.getDomainId()); + } + + @Override + public LdapConfigurationResponse deleteConfiguration(final String hostname, int port, Long domainId) throws InvalidParameterValueException { + return deleteConfigurationInternal(hostname, port, domainId); + } + + private LdapConfigurationResponse deleteConfigurationInternal(final String hostname, int port, Long domainId) throws InvalidParameterValueException { + final LdapConfigurationVO configuration = _ldapConfigurationDao.find(hostname,port,domainId); if (configuration == null) { throw new InvalidParameterValueException("Cannot find configuration with hostname " + hostname); } else { _ldapConfigurationDao.remove(configuration.getId()); - s_logger.info("Removed ldap server with hostname: " + hostname); - return new LdapConfigurationResponse(configuration.getHostname(), configuration.getPort()); + s_logger.info("Removed ldap server with url: " + hostname + ':' + port + (domainId == null ? "" : " for domain id " + domainId)); + return createLdapConfigurationResponse(configuration); } } @@ -175,17 +217,18 @@ public class LdapManagerImpl implements LdapManager, LdapValidator { cmdList.add(LDAPConfigCmd.class); cmdList.add(LDAPRemoveCmd.class); cmdList.add(LinkDomainToLdapCmd.class); + cmdList.add(LinkAccountToLdapCmd.class); return cmdList; } @Override - public LdapUser getUser(final String username) throws NoLdapUserMatchingQueryException { + public LdapUser getUser(final String username, Long domainId) throws NoLdapUserMatchingQueryException { LdapContext context = null; try { - context = _ldapContextFactory.createBindContext(); + context = _ldapContextFactory.createBindContext(domainId); final String escapedUsername = LdapUtils.escapeLDAPSearchFilter(username); - return _ldapUserManagerFactory.getInstance(_ldapConfiguration.getLdapProvider()).getUser(escapedUsername, context); + return _ldapUserManagerFactory.getInstance(_ldapConfiguration.getLdapProvider(null)).getUser(escapedUsername, context, domainId); } catch (NamingException | IOException e) { s_logger.debug("ldap Exception: ",e); @@ -196,26 +239,26 @@ public class LdapManagerImpl implements LdapManager, LdapValidator { } @Override - public LdapUser getUser(final String username, final String type, final String name) throws NoLdapUserMatchingQueryException { + public LdapUser getUser(final String username, final String type, final String name, Long domainId) throws NoLdapUserMatchingQueryException { LdapContext context = null; try { - context = _ldapContextFactory.createBindContext(); + context = _ldapContextFactory.createBindContext(domainId); final String escapedUsername = LdapUtils.escapeLDAPSearchFilter(username); - return _ldapUserManagerFactory.getInstance(_ldapConfiguration.getLdapProvider()).getUser(escapedUsername, type, name, context); + return _ldapUserManagerFactory.getInstance(_ldapConfiguration.getLdapProvider(null)).getUser(escapedUsername, type, name, context, domainId); } catch (NamingException | IOException e) { s_logger.debug("ldap Exception: ",e); - throw new NoLdapUserMatchingQueryException("No Ldap User found for username: "+username + "name: " + name + "of type: " + type); + throw new NoLdapUserMatchingQueryException("No Ldap User found for username: "+username + " in group: " + name + " of type: " + type); } finally { closeContext(context); } } @Override - public List getUsers() throws NoLdapUserMatchingQueryException { + public List getUsers(Long domainId) throws NoLdapUserMatchingQueryException { LdapContext context = null; try { - context = _ldapContextFactory.createBindContext(); - return _ldapUserManagerFactory.getInstance(_ldapConfiguration.getLdapProvider()).getUsers(context); + context = _ldapContextFactory.createBindContext(domainId); + return _ldapUserManagerFactory.getInstance(_ldapConfiguration.getLdapProvider(domainId)).getUsers(context, domainId); } catch (NamingException | IOException e) { s_logger.debug("ldap Exception: ",e); throw new NoLdapUserMatchingQueryException("*"); @@ -225,11 +268,11 @@ public class LdapManagerImpl implements LdapManager, LdapValidator { } @Override - public List getUsersInGroup(String groupName) throws NoLdapUserMatchingQueryException { + public List getUsersInGroup(String groupName, Long domainId) throws NoLdapUserMatchingQueryException { LdapContext context = null; try { - context = _ldapContextFactory.createBindContext(); - return _ldapUserManagerFactory.getInstance(_ldapConfiguration.getLdapProvider()).getUsersInGroup(groupName, context); + context = _ldapContextFactory.createBindContext(domainId); + return _ldapUserManagerFactory.getInstance(_ldapConfiguration.getLdapProvider(domainId)).getUsersInGroup(groupName, context, domainId); } catch (NamingException | IOException e) { s_logger.debug("ldap NamingException: ",e); throw new NoLdapUserMatchingQueryException("groupName=" + groupName); @@ -247,7 +290,8 @@ public class LdapManagerImpl implements LdapManager, LdapValidator { public Pair, Integer> listConfigurations(final LdapListConfigurationCmd cmd) { final String hostname = cmd.getHostname(); final int port = cmd.getPort(); - final Pair, Integer> result = _ldapConfigurationDao.searchConfigurations(hostname, port); + final Long domainId = cmd.getDomainId(); + final Pair, Integer> result = _ldapConfigurationDao.searchConfigurations(hostname, port, domainId); return new Pair, Integer>(result.first(), result.second()); } @@ -255,9 +299,10 @@ public class LdapManagerImpl implements LdapManager, LdapValidator { public List searchUsers(final String username) throws NoLdapUserMatchingQueryException { LdapContext context = null; try { - context = _ldapContextFactory.createBindContext(); + // TODO search users per domain (only?) + context = _ldapContextFactory.createBindContext(null); final String escapedUsername = LdapUtils.escapeLDAPSearchFilter(username); - return _ldapUserManagerFactory.getInstance(_ldapConfiguration.getLdapProvider()).getUsers("*" + escapedUsername + "*", context); + return _ldapUserManagerFactory.getInstance(_ldapConfiguration.getLdapProvider(null)).getUsers("*" + escapedUsername + "*", context, null); } catch (NamingException | IOException e) { s_logger.debug("ldap Exception: ",e); throw new NoLdapUserMatchingQueryException(username); @@ -267,14 +312,20 @@ public class LdapManagerImpl implements LdapManager, LdapValidator { } @Override - public LinkDomainToLdapResponse linkDomainToLdap(Long domainId, String type, String name, short accountType) { + public LinkDomainToLdapResponse linkDomainToLdap(LinkDomainToLdapCmd cmd) { + Validate.isTrue(_ldapConfiguration.getBaseDn(cmd.getDomainId()) == null, "can not configure an ldap server and an ldap group/ou to a domain"); + Validate.notEmpty(cmd.getLdapDomain(), "ldapDomain cannot be empty, please supply a GROUP or OU name"); + return linkDomainToLdap(cmd.getDomainId(),cmd.getType(),cmd.getLdapDomain(),cmd.getAccountType()); + } + + private LinkDomainToLdapResponse linkDomainToLdap(Long domainId, String type, String name, short accountType) { Validate.notNull(type, "type cannot be null. It should either be GROUP or OU"); Validate.notNull(domainId, "domainId cannot be null."); Validate.notEmpty(name, "GROUP or OU name cannot be empty"); //Account type should be 0 or 2. check the constants in com.cloud.user.Account Validate.isTrue(accountType==0 || accountType==2, "accountype should be either 0(normal user) or 2(domain admin)"); LinkType linkType = LdapManager.LinkType.valueOf(type.toUpperCase()); - LdapTrustMapVO vo = _ldapTrustMapDao.persist(new LdapTrustMapVO(domainId, linkType, name, accountType)); + LdapTrustMapVO vo = _ldapTrustMapDao.persist(new LdapTrustMapVO(domainId, linkType, name, accountType, 0)); DomainVO domain = domainDao.findById(vo.getDomainId()); String domainUuid = ""; if (domain == null) { @@ -290,4 +341,46 @@ public class LdapManagerImpl implements LdapManager, LdapValidator { public LdapTrustMapVO getDomainLinkedToLdap(long domainId){ return _ldapTrustMapDao.findByDomainId(domainId); } + + @Override + public List getDomainLinkage(long domainId){ + return _ldapTrustMapDao.searchByDomainId(domainId); + } + + public LdapTrustMapVO getAccountLinkedToLdap(long domainId, long accountId){ + return _ldapTrustMapDao.findByAccount(domainId, accountId); + } + + @Override + public LdapTrustMapVO getLinkedLdapGroup(long domainId, String group) { + return _ldapTrustMapDao.findGroupInDomain(domainId, group); + } + + @Override public LinkAccountToLdapResponse linkAccountToLdap(LinkAccountToLdapCmd cmd) { + Validate.notNull(_ldapConfiguration.getBaseDn(cmd.getDomainId()), "can not configure an ldap server and an ldap group/ou to a domain"); + Validate.notNull(cmd.getDomainId(), "domainId cannot be null."); + Validate.notEmpty(cmd.getAccountName(), "accountName cannot be empty."); + Validate.notEmpty(cmd.getLdapDomain(), "ldapDomain cannot be empty, please supply a GROUP or OU name"); + Validate.notNull(cmd.getType(), "type cannot be null. It should either be GROUP or OU"); + Validate.notEmpty(cmd.getLdapDomain(), "GROUP or OU name cannot be empty"); + + LinkType linkType = LdapManager.LinkType.valueOf(cmd.getType().toUpperCase()); + Account account = accountDao.findActiveAccount(cmd.getAccountName(),cmd.getDomainId()); + if (account == null) { + account = new AccountVO(cmd.getAccountName(), cmd.getDomainId(), null, cmd.getAccountType(), UUID.randomUUID().toString()); + accountDao.persist((AccountVO)account); + } + Long accountId = account.getAccountId(); + LdapTrustMapVO vo = _ldapTrustMapDao.persist(new LdapTrustMapVO(cmd.getDomainId(), linkType, cmd.getLdapDomain(), cmd.getAccountType(), accountId)); + DomainVO domain = domainDao.findById(vo.getDomainId()); + String domainUuid = ""; + if (domain == null) { + s_logger.error("no domain in database for id " + vo.getDomainId()); + } else { + domainUuid = domain.getUuid(); + } + + LinkAccountToLdapResponse response = new LinkAccountToLdapResponse(domainUuid, vo.getType().toString(), vo.getName(), vo.getAccountType(), account.getUuid(), cmd.getAccountName()); + return response; + } } diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapTrustMapVO.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapTrustMapVO.java index 8b1363816fa..c402747b813 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapTrustMapVO.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapTrustMapVO.java @@ -45,6 +45,9 @@ public class LdapTrustMapVO implements InternalIdentity { @Column(name = "domain_id") private long domainId; + @Column(name = "account_id") + private long accountId; + @Column(name = "account_type") private short accountType; @@ -52,11 +55,12 @@ public class LdapTrustMapVO implements InternalIdentity { public LdapTrustMapVO() { } - public LdapTrustMapVO(long domainId, LdapManager.LinkType type, String name, short accountType) { + public LdapTrustMapVO(long domainId, LdapManager.LinkType type, String name, short accountType, long accountId) { this.domainId = domainId; this.type = type; this.name = name; this.accountType = accountType; + this.accountId = accountId; } @Override @@ -80,6 +84,10 @@ public class LdapTrustMapVO implements InternalIdentity { return accountType; } + public long getAccountId() { + return accountId; + } + @Override public boolean equals(Object o) { if (this == o) { @@ -94,6 +102,9 @@ public class LdapTrustMapVO implements InternalIdentity { if (domainId != that.domainId) { return false; } + if (accountId != that.accountId) { + return false; + } if (accountType != that.accountType) { return false; } @@ -109,6 +120,7 @@ public class LdapTrustMapVO implements InternalIdentity { int result = type.hashCode(); result = 31 * result + name.hashCode(); result = 31 * result + (int) (domainId ^ (domainId >>> 32)); + result = 31 * result + (int) (accountId ^ (accountId >>> 32)); result = 31 * result + (int) accountType; return result; } diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUser.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUser.java index c4c334b5b6e..064ee412ab4 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUser.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUser.java @@ -16,6 +16,9 @@ // under the License. package org.apache.cloudstack.ldap; +import java.util.ArrayList; +import java.util.List; + public class LdapUser implements Comparable { private final String email; private final String principal; @@ -24,8 +27,10 @@ public class LdapUser implements Comparable { private final String username; private final String domain; private final boolean disabled; + private List memberships; - public LdapUser(final String username, final String email, final String firstname, final String lastname, final String principal, String domain, boolean disabled) { + public LdapUser(final String username, final String email, final String firstname, final String lastname, final String principal, String domain, boolean disabled, + List memberships) { this.username = username; this.email = email; this.firstname = firstname; @@ -33,6 +38,7 @@ public class LdapUser implements Comparable { this.principal = principal; this.domain = domain; this.disabled = disabled; + this.memberships = memberships == null ? new ArrayList<>() : memberships; } @Override @@ -80,6 +86,9 @@ public class LdapUser implements Comparable { return disabled; } + public List getMemberships() { + return memberships; + } @Override public int hashCode() { diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUserManager.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUserManager.java index 4e2bcf816b2..c9fcaa23cc0 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUserManager.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUserManager.java @@ -30,17 +30,17 @@ public interface LdapUserManager { MICROSOFTAD, OPENLDAP; } - public LdapUser getUser(final String username, final LdapContext context) throws NamingException, IOException; + public LdapUser getUser(final String username, final LdapContext context, Long domainId) throws NamingException, IOException; - public LdapUser getUser(final String username, final String type, final String name, final LdapContext context) throws NamingException, IOException; + public LdapUser getUser(final String username, final String type, final String name, final LdapContext context, Long domainId) throws NamingException, IOException; - public List getUsers(final LdapContext context) throws NamingException, IOException; + public List getUsers(final LdapContext context, Long domainId) throws NamingException, IOException; - public List getUsers(final String username, final LdapContext context) throws NamingException, IOException; + public List getUsers(final String username, final LdapContext context, Long domainId) throws NamingException, IOException; - public List getUsersInGroup(String groupName, LdapContext context) throws NamingException; + public List getUsersInGroup(String groupName, LdapContext context, Long domainId) throws NamingException; - public List searchUsers(final LdapContext context) throws NamingException, IOException; + public List searchUsers(final LdapContext context, Long domainId) throws NamingException, IOException; - public List searchUsers(final String username, final LdapContext context) throws NamingException, IOException; + public List searchUsers(final String username, final LdapContext context, Long domainId) throws NamingException, IOException; } diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUtils.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUtils.java index d54a6991def..da0859f77ca 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUtils.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUtils.java @@ -16,9 +16,12 @@ // under the License. package org.apache.cloudstack.ldap; +import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; +import java.util.ArrayList; +import java.util.List; public final class LdapUtils { public static String escapeLDAPSearchFilter(final String filter) { @@ -56,6 +59,18 @@ public final class LdapUtils { return null; } + public static List getAttributeValues(final Attributes attributes, final String attributeName) throws NamingException { + ArrayList memberships = new ArrayList<>(); + final Attribute attribute = attributes.get(attributeName); + if (attribute != null) { + NamingEnumeration values = attribute.getAll(); + while(values.hasMore()) { + memberships.add(String.valueOf(values.next())); + } + } + return memberships; + } + private LdapUtils() { } } \ No newline at end of file diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/OpenLdapUserManagerImpl.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/OpenLdapUserManagerImpl.java index 0c3e0d71705..cb3824a2ef0 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/OpenLdapUserManagerImpl.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/OpenLdapUserManagerImpl.java @@ -50,41 +50,46 @@ public class OpenLdapUserManagerImpl implements LdapUserManager { _ldapConfiguration = ldapConfiguration; } - protected LdapUser createUser(final SearchResult result) throws NamingException { + protected LdapUser createUser(final SearchResult result, Long domainId) throws NamingException { final Attributes attributes = result.getAttributes(); - final String username = LdapUtils.getAttributeValue(attributes, _ldapConfiguration.getUsernameAttribute()); - final String email = LdapUtils.getAttributeValue(attributes, _ldapConfiguration.getEmailAttribute()); - final String firstname = LdapUtils.getAttributeValue(attributes, _ldapConfiguration.getFirstnameAttribute()); - final String lastname = LdapUtils.getAttributeValue(attributes, _ldapConfiguration.getLastnameAttribute()); + final String username = LdapUtils.getAttributeValue(attributes, _ldapConfiguration.getUsernameAttribute(domainId)); + final String email = LdapUtils.getAttributeValue(attributes, _ldapConfiguration.getEmailAttribute(domainId)); + final String firstname = LdapUtils.getAttributeValue(attributes, _ldapConfiguration.getFirstnameAttribute(domainId)); + final String lastname = LdapUtils.getAttributeValue(attributes, _ldapConfiguration.getLastnameAttribute(domainId)); final String principal = result.getNameInNamespace(); + final List memberships = LdapUtils.getAttributeValues(attributes, _ldapConfiguration.getUserMemberOfAttribute(domainId)); String domain = principal.replace("cn=" + LdapUtils.getAttributeValue(attributes, _ldapConfiguration.getCommonNameAttribute()) + ",", ""); - domain = domain.replace("," + _ldapConfiguration.getBaseDn(), ""); + domain = domain.replace("," + _ldapConfiguration.getBaseDn(domainId), ""); domain = domain.replace("ou=", ""); boolean disabled = isUserDisabled(result); - return new LdapUser(username, email, firstname, lastname, principal, domain, disabled); + return new LdapUser(username, email, firstname, lastname, principal, domain, disabled, memberships); } - private String generateSearchFilter(final String username) { + private String generateSearchFilter(final String username, Long domainId) { final StringBuilder userObjectFilter = new StringBuilder(); userObjectFilter.append("(objectClass="); - userObjectFilter.append(_ldapConfiguration.getUserObject()); + userObjectFilter.append(_ldapConfiguration.getUserObject(domainId)); userObjectFilter.append(")"); final StringBuilder usernameFilter = new StringBuilder(); usernameFilter.append("("); - usernameFilter.append(_ldapConfiguration.getUsernameAttribute()); + usernameFilter.append(_ldapConfiguration.getUsernameAttribute(domainId)); usernameFilter.append("="); usernameFilter.append((username == null ? "*" : username)); usernameFilter.append(")"); final StringBuilder memberOfFilter = new StringBuilder(); - if (_ldapConfiguration.getSearchGroupPrinciple() != null) { - memberOfFilter.append("(memberof="); - memberOfFilter.append(_ldapConfiguration.getSearchGroupPrinciple()); + if (_ldapConfiguration.getSearchGroupPrinciple(domainId) != null) { + if(s_logger.isDebugEnabled()) { + s_logger.debug("adding search filter for '" + _ldapConfiguration.getSearchGroupPrinciple(domainId) + + "', using " + _ldapConfiguration.getUserMemberOfAttribute(domainId)); + } + memberOfFilter.append("(" + _ldapConfiguration.getUserMemberOfAttribute(domainId) + "="); + memberOfFilter.append(_ldapConfiguration.getSearchGroupPrinciple(domainId)); memberOfFilter.append(")"); } @@ -98,10 +103,10 @@ public class OpenLdapUserManagerImpl implements LdapUserManager { return result.toString(); } - private String generateGroupSearchFilter(final String groupName) { + private String generateGroupSearchFilter(final String groupName, Long domainId) { final StringBuilder groupObjectFilter = new StringBuilder(); groupObjectFilter.append("(objectClass="); - groupObjectFilter.append(_ldapConfiguration.getGroupObject()); + groupObjectFilter.append(_ldapConfiguration.getGroupObject(domainId)); groupObjectFilter.append(")"); final StringBuilder groupNameFilter = new StringBuilder(); @@ -121,8 +126,8 @@ public class OpenLdapUserManagerImpl implements LdapUserManager { } @Override - public LdapUser getUser(final String username, final LdapContext context) throws NamingException, IOException { - List result = searchUsers(username, context); + public LdapUser getUser(final String username, final LdapContext context, Long domainId) throws NamingException, IOException { + List result = searchUsers(username, context, domainId); if (result!= null && result.size() == 1) { return result.get(0); } else { @@ -131,29 +136,29 @@ public class OpenLdapUserManagerImpl implements LdapUserManager { } @Override - public LdapUser getUser(final String username, final String type, final String name, final LdapContext context) throws NamingException, IOException { + public LdapUser getUser(final String username, final String type, final String name, final LdapContext context, Long domainId) throws NamingException, IOException { String basedn; if("OU".equals(type)) { basedn = name; } else { - basedn = _ldapConfiguration.getBaseDn(); + basedn = _ldapConfiguration.getBaseDn(domainId); } final StringBuilder userObjectFilter = new StringBuilder(); userObjectFilter.append("(objectClass="); - userObjectFilter.append(_ldapConfiguration.getUserObject()); + userObjectFilter.append(_ldapConfiguration.getUserObject(domainId)); userObjectFilter.append(")"); final StringBuilder usernameFilter = new StringBuilder(); usernameFilter.append("("); - usernameFilter.append(_ldapConfiguration.getUsernameAttribute()); + usernameFilter.append(_ldapConfiguration.getUsernameAttribute(domainId)); usernameFilter.append("="); usernameFilter.append((username == null ? "*" : username)); usernameFilter.append(")"); final StringBuilder memberOfFilter = new StringBuilder(); if ("GROUP".equals(type)) { - memberOfFilter.append("(").append(getMemberOfAttribute()).append("="); + memberOfFilter.append("(").append(getMemberOfAttribute(domainId)).append("="); memberOfFilter.append(name); memberOfFilter.append(")"); } @@ -165,21 +170,21 @@ public class OpenLdapUserManagerImpl implements LdapUserManager { searchQuery.append(memberOfFilter); searchQuery.append(")"); - return searchUser(basedn, searchQuery.toString(), context); + return searchUser(basedn, searchQuery.toString(), context, domainId); } - protected String getMemberOfAttribute() { - return "memberof"; + protected String getMemberOfAttribute(final Long domainId) { + return _ldapConfiguration.getUserMemberOfAttribute(domainId); } @Override - public List getUsers(final LdapContext context) throws NamingException, IOException { - return getUsers(null, context); + public List getUsers(final LdapContext context, Long domainId) throws NamingException, IOException { + return getUsers(null, context, domainId); } @Override - public List getUsers(final String username, final LdapContext context) throws NamingException, IOException { - List users = searchUsers(username, context); + public List getUsers(final String username, final LdapContext context, Long domainId) throws NamingException, IOException { + List users = searchUsers(username, context, domainId); if (CollectionUtils.isNotEmpty(users)) { Collections.sort(users); @@ -188,13 +193,13 @@ public class OpenLdapUserManagerImpl implements LdapUserManager { } @Override - public List getUsersInGroup(String groupName, LdapContext context) throws NamingException { - String attributeName = _ldapConfiguration.getGroupUniqueMemeberAttribute(); + public List getUsersInGroup(String groupName, LdapContext context, Long domainId) throws NamingException { + String attributeName = _ldapConfiguration.getGroupUniqueMemberAttribute(domainId); final SearchControls controls = new SearchControls(); controls.setSearchScope(_ldapConfiguration.getScope()); controls.setReturningAttributes(new String[] {attributeName}); - NamingEnumeration result = context.search(_ldapConfiguration.getBaseDn(), generateGroupSearchFilter(groupName), controls); + NamingEnumeration result = context.search(_ldapConfiguration.getBaseDn(domainId), generateGroupSearchFilter(groupName, domainId), controls); final List users = new ArrayList(); //Expecting only one result which has all the users @@ -205,7 +210,7 @@ public class OpenLdapUserManagerImpl implements LdapUserManager { while (values.hasMoreElements()) { String userdn = String.valueOf(values.nextElement()); try{ - users.add(getUserForDn(userdn, context)); + users.add(getUserForDn(userdn, context, domainId)); } catch (NamingException e){ s_logger.info("Userdn: " + userdn + " Not Found:: Exception message: " + e.getMessage()); } @@ -217,39 +222,42 @@ public class OpenLdapUserManagerImpl implements LdapUserManager { return users; } - private LdapUser getUserForDn(String userdn, LdapContext context) throws NamingException { + private LdapUser getUserForDn(String userdn, LdapContext context, Long domainId) throws NamingException { final SearchControls controls = new SearchControls(); controls.setSearchScope(_ldapConfiguration.getScope()); - controls.setReturningAttributes(_ldapConfiguration.getReturnAttributes()); + controls.setReturningAttributes(_ldapConfiguration.getReturnAttributes(domainId)); - NamingEnumeration result = context.search(userdn, "(objectClass=" + _ldapConfiguration.getUserObject() + ")", controls); + NamingEnumeration result = context.search(userdn, "(objectClass=" + _ldapConfiguration.getUserObject(domainId) + ")", controls); if (result.hasMoreElements()) { - return createUser(result.nextElement()); + return createUser(result.nextElement(), domainId); } else { throw new NamingException("No user found for dn " + userdn); } } @Override - public List searchUsers(final LdapContext context) throws NamingException, IOException { - return searchUsers(null, context); + public List searchUsers(final LdapContext context, Long domainId) throws NamingException, IOException { + return searchUsers(null, context, domainId); } protected boolean isUserDisabled(SearchResult result) throws NamingException { return false; } - public LdapUser searchUser(final String basedn, final String searchString, final LdapContext context) throws NamingException, IOException { + public LdapUser searchUser(final String basedn, final String searchString, final LdapContext context, Long domainId) throws NamingException, IOException { final SearchControls searchControls = new SearchControls(); searchControls.setSearchScope(_ldapConfiguration.getScope()); - searchControls.setReturningAttributes(_ldapConfiguration.getReturnAttributes()); + searchControls.setReturningAttributes(_ldapConfiguration.getReturnAttributes(domainId)); NamingEnumeration results = context.search(basedn, searchString, searchControls); + if(s_logger.isDebugEnabled()) { + s_logger.debug("searching user(s) with filter: \"" + searchString + "\""); + } final List users = new ArrayList(); while (results.hasMoreElements()) { final SearchResult result = results.nextElement(); - users.add(createUser(result)); + users.add(createUser(result, domainId)); } if (users.size() == 1) { @@ -260,28 +268,28 @@ public class OpenLdapUserManagerImpl implements LdapUserManager { } @Override - public List searchUsers(final String username, final LdapContext context) throws NamingException, IOException { + public List searchUsers(final String username, final LdapContext context, Long domainId) throws NamingException, IOException { final SearchControls searchControls = new SearchControls(); searchControls.setSearchScope(_ldapConfiguration.getScope()); - searchControls.setReturningAttributes(_ldapConfiguration.getReturnAttributes()); + searchControls.setReturningAttributes(_ldapConfiguration.getReturnAttributes(domainId)); - String basedn = _ldapConfiguration.getBaseDn(); + String basedn = _ldapConfiguration.getBaseDn(domainId); if (StringUtils.isBlank(basedn)) { throw new IllegalArgumentException("ldap basedn is not configured"); } byte[] cookie = null; - int pageSize = _ldapConfiguration.getLdapPageSize(); + int pageSize = _ldapConfiguration.getLdapPageSize(domainId); context.setRequestControls(new Control[]{new PagedResultsControl(pageSize, Control.NONCRITICAL)}); final List users = new ArrayList(); NamingEnumeration results; do { - results = context.search(basedn, generateSearchFilter(username), searchControls); + results = context.search(basedn, generateSearchFilter(username, domainId), searchControls); while (results.hasMoreElements()) { final SearchResult result = results.nextElement(); if (!isUserDisabled(result)) { - users.add(createUser(result)); + users.add(createUser(result, domainId)); } } Control[] contextControls = context.getResponseControls(); diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapConfigurationDao.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapConfigurationDao.java index a2d5e65248e..e99c78be9b7 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapConfigurationDao.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapConfigurationDao.java @@ -23,8 +23,19 @@ import org.apache.cloudstack.ldap.LdapConfigurationVO; import com.cloud.utils.Pair; import com.cloud.utils.db.GenericDao; +/** + * TODO the domain value null now searches for that specifically and there is no way to search for all domains + */ public interface LdapConfigurationDao extends GenericDao { + /** + * @deprecated there might well be more then one ldap implementation on a host and or a double binding of several domains + * @param hostname + * @return + */ + @Deprecated LdapConfigurationVO findByHostname(String hostname); - Pair, Integer> searchConfigurations(String hostname, int port); + LdapConfigurationVO find(String hostname, int port, Long domainId); + + Pair, Integer> searchConfigurations(String hostname, int port, Long domainId); } \ No newline at end of file diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapConfigurationDaoImpl.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapConfigurationDaoImpl.java index 8125f8cd2de..fa4c0af236f 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapConfigurationDaoImpl.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapConfigurationDaoImpl.java @@ -32,7 +32,8 @@ import com.cloud.utils.db.SearchCriteria.Op; @Component public class LdapConfigurationDaoImpl extends GenericDaoBase implements LdapConfigurationDao { private final SearchBuilder hostnameSearch; - private final SearchBuilder listAllConfigurationsSearch; + private final SearchBuilder listGlobalConfigurationsSearch; + private final SearchBuilder listDomainConfigurationsSearch; public LdapConfigurationDaoImpl() { super(); @@ -40,10 +41,16 @@ public class LdapConfigurationDaoImpl extends GenericDaoBase, Integer> searchConfigurations(final String hostname, final int port) { - final SearchCriteria sc = listAllConfigurationsSearch.create(); + public LdapConfigurationVO find(String hostname, int port, Long domainId) { + SearchCriteria sc = getSearchCriteria(hostname, port, domainId); + return findOneBy(sc); + } + + @Override + public Pair, Integer> searchConfigurations(final String hostname, final int port, final Long domainId) { + SearchCriteria sc = getSearchCriteria(hostname, port, domainId); + return searchAndCount(sc, null); + } + + private SearchCriteria getSearchCriteria(String hostname, int port, Long domainId) { + SearchCriteria sc; + if (domainId == null) { + sc = listDomainConfigurationsSearch.create(); + } else { + sc = listDomainConfigurationsSearch.create(); + sc.setParameters("domain_id", domainId); + } if (hostname != null) { sc.setParameters("hostname", hostname); } - return searchAndCount(sc, null); + if (port > 0) { + sc.setParameters("port", port); + } + return sc; } } \ No newline at end of file diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapTrustMapDao.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapTrustMapDao.java index 7ef3799b3d8..c3d2f8aedf4 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapTrustMapDao.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapTrustMapDao.java @@ -22,6 +22,11 @@ import org.apache.cloudstack.ldap.LdapTrustMapVO; import com.cloud.utils.db.GenericDao; +import java.util.List; + public interface LdapTrustMapDao extends GenericDao { LdapTrustMapVO findByDomainId(long domainId); + LdapTrustMapVO findByAccount(long domainId, Long accountId); + LdapTrustMapVO findGroupInDomain(long domainId, String group); + List searchByDomainId(long domainId); } diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapTrustMapDaoImpl.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapTrustMapDaoImpl.java index 57830982e70..0ecd3413d14 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapTrustMapDaoImpl.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapTrustMapDaoImpl.java @@ -26,21 +26,55 @@ import org.springframework.stereotype.Component; import com.cloud.utils.db.GenericDaoBase; +import java.util.List; + @Component public class LdapTrustMapDaoImpl extends GenericDaoBase implements LdapTrustMapDao { private final SearchBuilder domainIdSearch; + private final SearchBuilder groupSearch; public LdapTrustMapDaoImpl() { super(); domainIdSearch = createSearchBuilder(); domainIdSearch.and("domainId", domainIdSearch.entity().getDomainId(), SearchCriteria.Op.EQ); + domainIdSearch.and("account_id", domainIdSearch.entity().getAccountId(),SearchCriteria.Op.EQ); domainIdSearch.done(); + groupSearch = createSearchBuilder(); + groupSearch.and("domainId", groupSearch.entity().getDomainId(), SearchCriteria.Op.EQ); + groupSearch.and("name", groupSearch.entity().getName(),SearchCriteria.Op.EQ); + groupSearch.done(); } @Override public LdapTrustMapVO findByDomainId(long domainId) { final SearchCriteria sc = domainIdSearch.create(); sc.setParameters("domainId", domainId); + sc.setParameters("account_id", 0); return findOneBy(sc); } + + @Override + public LdapTrustMapVO findByAccount(long domainId, Long accountId) { + final SearchCriteria sc = domainIdSearch.create(); + sc.setParameters("domainId", domainId); + sc.setParameters("account_id", accountId); + return findOneBy(sc); + } + + @Override + public LdapTrustMapVO findGroupInDomain(long domainId, String group){ + final SearchCriteria sc = groupSearch.create(); + sc.setParameters("domainId", domainId); + sc.setParameters("name", group); + return findOneBy(sc); + + } + + @Override + public List searchByDomainId(long domainId) { + final SearchCriteria sc = domainIdSearch.create(); + sc.setParameters("domainId", domainId); + return search(sc,null); + } + } diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/ADLdapUserManagerImplSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/ADLdapUserManagerImplSpec.groovy index 93b1b17a460..4b631b44e3b 100644 --- a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/ADLdapUserManagerImplSpec.groovy +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/ADLdapUserManagerImplSpec.groovy @@ -69,13 +69,13 @@ class ADLdapUserManagerImplSpec extends spock.lang.Specification { def "test getUsersInGroup null group"() { ldapConfiguration.getScope() >> SearchControls.SUBTREE_SCOPE - ldapConfiguration.getReturnAttributes() >> ["username", "firstname", "lastname", "email"] - ldapConfiguration.getBaseDn() >>> [null, null, "DC=cloud,DC=citrix,DC=com"] + ldapConfiguration.getReturnAttributes(null) >> ["username", "firstname", "lastname", "email"] + ldapConfiguration.getBaseDn(null) >>> [null, null, "DC=cloud,DC=citrix,DC=com"] LdapContext context = Mock(LdapContext); when: - def result = adLdapUserManager.getUsersInGroup(group, context) + def result = adLdapUserManager.getUsersInGroup(group, context,null) then: thrown(IllegalArgumentException) where: diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapAuthenticatorSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapAuthenticatorSpec.groovy index ca19e8c633b..1ff5fce4243 100644 --- a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapAuthenticatorSpec.groovy +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapAuthenticatorSpec.groovy @@ -49,8 +49,8 @@ class LdapAuthenticatorSpec extends spock.lang.Specification { def ldapUser = Mock(LdapUser) ldapUser.isDisabled() >> false ldapManager.isLdapEnabled() >> true - ldapManager.getUser("rmurphy") >> ldapUser - ldapManager.canAuthenticate(_, _) >> false + ldapManager.getUser("rmurphy", null) >> ldapUser + ldapManager.canAuthenticate(_, _, _) >> false UserAccountDao userAccountDao = Mock(UserAccountDao) userAccountDao.getUserAccount(_, _) >> new UserAccountVO() @@ -84,8 +84,8 @@ class LdapAuthenticatorSpec extends spock.lang.Specification { def ldapUser = Mock(LdapUser) ldapUser.isDisabled() >> false ldapManager.isLdapEnabled() >> true - ldapManager.canAuthenticate(_, _) >> true - ldapManager.getUser("rmurphy") >> ldapUser + ldapManager.canAuthenticate(_, _, _) >> true + ldapManager.getUser("rmurphy", null) >> ldapUser UserAccountDao userAccountDao = Mock(UserAccountDao) userAccountDao.getUserAccount(_, _) >> new UserAccountVO() @@ -144,7 +144,7 @@ class LdapAuthenticatorSpec extends spock.lang.Specification { userAccountDao.getUserAccount(username, domainId) >> userAccount userAccount.getId() >> 1 ldapManager.getDomainLinkedToLdap(domainId) >> new LdapTrustMapVO(domainId, type, name, (short)2) - ldapManager.getUser(username, type.toString(), name) >> new LdapUser(username, "email", "firstname", "lastname", "principal", "domain", true) + ldapManager.getUser(username, type.toString(), name) >> new LdapUser(username, "email", "firstname", "lastname", "principal", "domain", true, null) //user should be disabled in cloudstack accountManager.disableUser(1) >> userAccount @@ -173,8 +173,8 @@ class LdapAuthenticatorSpec extends spock.lang.Specification { ldapManager.isLdapEnabled() >> true userAccountDao.getUserAccount(username, domainId) >> null ldapManager.getDomainLinkedToLdap(domainId) >> new LdapTrustMapVO(domainId, type, name, (short)0) - ldapManager.getUser(username, type.toString(), name) >> new LdapUser(username, "email", "firstname", "lastname", "principal", "domain", false) - ldapManager.canAuthenticate(_,_) >> true + ldapManager.getUser(username, type.toString(), name) >> new LdapUser(username, "email", "firstname", "lastname", "principal", "domain", false, null) + ldapManager.canAuthenticate(_, _, _) >> true //user should be created in cloudstack accountManager.createUserAccount(username, "", "firstname", "lastname", "email", null, username, (short) 2, domainId, username, null, _, _, User.Source.LDAP) >> Mock(UserAccount) @@ -206,8 +206,8 @@ class LdapAuthenticatorSpec extends spock.lang.Specification { userAccount.getId() >> 1 userAccount.getState() >> Account.State.disabled.toString() ldapManager.getDomainLinkedToLdap(domainId) >> new LdapTrustMapVO(domainId, type, name, (short)2) - ldapManager.getUser(username, type.toString(), name) >> new LdapUser(username, "email", "firstname", "lastname", "principal", "domain", false) - ldapManager.canAuthenticate(_,_) >> true + ldapManager.getUser(username, type.toString(), name) >> new LdapUser(username, "email", "firstname", "lastname", "principal", "domain", false, null) + ldapManager.canAuthenticate(_, _, _) >> true //user should be enabled in cloudstack if disabled accountManager.enableUser(1) >> userAccount @@ -237,8 +237,8 @@ class LdapAuthenticatorSpec extends spock.lang.Specification { UserAccount userAccount = Mock(UserAccount) userAccountDao.getUserAccount(username, domainId) >> userAccount ldapManager.getDomainLinkedToLdap(domainId) >> new LdapTrustMapVO(domainId, type, name, (short)2) - ldapManager.getUser(username, type.toString(), name) >> new LdapUser(username, "email", "firstname", "lastname", "principal", "domain", false) - ldapManager.canAuthenticate(_,_) >> false + ldapManager.getUser(username, type.toString(), name) >> new LdapUser(username, "email", "firstname", "lastname", "principal", "domain", false, null) + ldapManager.canAuthenticate(_, _, _) >> false when: Pair result = ldapAuthenticator.authenticate(username, "password", domainId, null) diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationDaoImplSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationDaoImplSpec.groovy index 144890957f2..e94b0d40fb4 100644 --- a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationDaoImplSpec.groovy +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationDaoImplSpec.groovy @@ -22,8 +22,8 @@ class LdapConfigurationDaoImplSpec extends spock.lang.Specification { def "Test setting up of a LdapConfigurationDao"() { given: "We have an LdapConfigurationDao implementation" def ldapConfigurationDaoImpl = new LdapConfigurationDaoImpl(); - expect: "that hostnameSearch and listAllConfigurationsSearch is configured" + expect: "that hostnameSearch and listDomainConfigurationsSearch is configured" ldapConfigurationDaoImpl.hostnameSearch != null; - ldapConfigurationDaoImpl.listAllConfigurationsSearch != null + ldapConfigurationDaoImpl.listDomainConfigurationsSearch != null } } diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationSpec.groovy index 6f967cc6d8b..ec84d38e125 100644 --- a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationSpec.groovy +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationSpec.groovy @@ -19,55 +19,26 @@ package groovy.org.apache.cloudstack.ldap import org.apache.cloudstack.framework.config.ConfigKey import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import com.cloud.utils.Pair -import org.apache.cloudstack.api.ServerApiException import org.apache.cloudstack.framework.config.impl.ConfigDepotImpl import org.apache.cloudstack.framework.config.impl.ConfigurationVO import org.apache.cloudstack.ldap.LdapConfiguration import org.apache.cloudstack.ldap.LdapConfigurationVO -import org.apache.cloudstack.ldap.LdapManager import org.apache.cloudstack.ldap.LdapUserManager import org.apache.cloudstack.ldap.dao.LdapConfigurationDao -import org.apache.cxf.common.util.StringUtils import javax.naming.directory.SearchControls class LdapConfigurationSpec extends spock.lang.Specification { def "Test that getAuthentication returns none"() { given: "We have a ConfigDao, LdapManager and LdapConfiguration" - def configDao = Mock(ConfigurationDao) def ldapConfigurationDao = Mock(LdapConfigurationDao) - def ldapConfiguration = new LdapConfiguration(configDao, ldapConfigurationDao) + def ldapConfiguration = new LdapConfiguration(ldapConfigurationDao) when: "Get authentication is called" String authentication = ldapConfiguration.getAuthentication() then: "none should be returned" authentication == "none" } - def "Test that getAuthentication returns simple"() { - given: "We have a configDao, LdapManager and LdapConfiguration with bind principle and password set" - def configDao = Mock(ConfigurationDao) - def ldapConfigurationDao = Mock(LdapConfigurationDao) - def ldapConfiguration = new LdapConfiguration(configDao, ldapConfigurationDao) - configDao.getValue("ldap.bind.password") >> "password" - configDao.getValue("ldap.bind.principal") >> "cn=rmurphy,dc=cloudstack,dc=org" - when: "Get authentication is called" - String authentication = ldapConfiguration.getAuthentication() - then: "authentication should be set to simple" - authentication == "simple" - } - - def "Test that getBaseDn returns dc=cloudstack,dc=org"() { - given: "We have a ConfigDao, LdapManager and ldapConfiguration with a baseDn value set." - def configDao = Mock(ConfigurationDao) - configDao.getValue("ldap.basedn") >> "dc=cloudstack,dc=org" - def ldapConfigurationDao = Mock(LdapConfigurationDao) - def ldapConfiguration = new LdapConfiguration(configDao, ldapConfigurationDao) - when: "Get basedn is called" - String baseDn = ldapConfiguration.getBaseDn(); - then: "The set baseDn should be returned" - baseDn == "dc=cloudstack,dc=org" - } - def "Test that getEmailAttribute returns mail"() { given: "Given that we have a ConfigDao, LdapManager and LdapConfiguration" def configDao = Mock(ConfigurationDao) @@ -178,87 +149,12 @@ class LdapConfigurationSpec extends spock.lang.Specification { LdapConfiguration ldapConfiguration = new LdapConfiguration(configDao, ldapConfigurationDao) when: "A request is made to get the providerUrl" - String providerUrl = ldapConfiguration.getProviderUrl() + String providerUrl = ldapConfiguration.getProviderUrl(_) then: "The providerUrl should be given." providerUrl == "ldap://localhost:389" } - def "Test that get search group principle returns successfully"() { - given: "We have a ConfigDao with a value for ldap.search.group.principle and an LdapConfiguration" - def configDao = Mock(ConfigurationDao) - configDao.getValue("ldap.search.group.principle") >> "cn=cloudstack,cn=users,dc=cloudstack,dc=org" - def ldapConfigurationDao = Mock(LdapConfigurationDao) - LdapConfiguration ldapConfiguration = new LdapConfiguration(configDao, ldapConfigurationDao) - - when: "A request is made to get the search group principle" - String result = ldapConfiguration.getSearchGroupPrinciple(); - - then: "The result holds the same value configDao did" - result == "cn=cloudstack,cn=users,dc=cloudstack,dc=org" - } - - def "Test that getTrustStorePassword resopnds"() { - given: "We have a ConfigDao with a value for truststore password" - def configDao = Mock(ConfigurationDao) - configDao.getValue("ldap.truststore.password") >> "password" - def ldapConfigurationDao = Mock(LdapConfigurationDao) - LdapConfiguration ldapConfiguration = new LdapConfiguration(configDao, ldapConfigurationDao) - - when: "A request is made to get the truststore password" - String result = ldapConfiguration.getTrustStorePassword() - - then: "The result is password" - result == "password"; - } - - def "Test that getSSLStatus can be true"() { - given: "We have a ConfigDao with values for truststore and truststore password set" - def configDao = Mock(ConfigurationDao) - configDao.getValue("ldap.truststore") >> "/tmp/ldap.ts" - configDao.getValue("ldap.truststore.password") >> "password" - def ldapConfigurationDao = Mock(LdapConfigurationDao) - LdapConfiguration ldapConfiguration = new LdapConfiguration(configDao, ldapConfigurationDao) - - when: "A request is made to get the status of SSL" - boolean result = ldapConfiguration.getSSLStatus(); - - then: "The response should be true" - result == true - } - - def "Test getgroupobject"() { - given: "We have configdao for ldap group object" - def configDao = Mock(ConfigurationDao) - configDao.getValue("ldap.group.object") >> groupObject - - def ldapConfigurationDao = Mock(LdapConfigurationDao) - LdapConfiguration ldapConfiguration = new LdapConfiguration(configDao, ldapConfigurationDao) - def expectedResult = groupObject == null ? "groupOfUniqueNames" : groupObject - - def result = ldapConfiguration.getGroupObject() - expect: - result == expectedResult - where: - groupObject << [null, "", "groupOfUniqueNames"] - } - - def "Test getGroupUniqueMemeberAttribute"() { - given: "We have configdao for ldap group object" - def configDao = Mock(ConfigurationDao) - configDao.getValue("ldap.group.user.uniquemember") >> groupObject - - def ldapConfigurationDao = Mock(LdapConfigurationDao) - LdapConfiguration ldapConfiguration = new LdapConfiguration(configDao, ldapConfigurationDao) - def expectedResult = groupObject == null ? "uniquemember" : groupObject - - def result = ldapConfiguration.getGroupUniqueMemeberAttribute() - expect: - result == expectedResult - where: - groupObject << [null, "", "uniquemember"] - } - def "Test getReadTimeout"() { given: "We have configdao for ldap group object" def configDao = Mock(ConfigurationDao) @@ -275,7 +171,7 @@ class LdapConfigurationSpec extends spock.lang.Specification { def expected = timeout == null ? 1000 : timeout.toLong() //1000 is the default value - def result = ldapConfiguration.getReadTimeout() + def result = ldapConfiguration.getReadTimeout(null) expect: result == expected where: @@ -298,7 +194,7 @@ class LdapConfigurationSpec extends spock.lang.Specification { def expected = provider.equalsIgnoreCase("microsoftad") ? LdapUserManager.Provider.MICROSOFTAD : LdapUserManager.Provider.OPENLDAP //"openldap" is the default value - def result = ldapConfiguration.getLdapProvider() + def result = ldapConfiguration.getLdapProvider(null) expect: println "asserting for provider configuration: " + provider result == expected diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapContextFactorySpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapContextFactorySpec.groovy index 15408833a65..eead0bcd28c 100644 --- a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapContextFactorySpec.groovy +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapContextFactorySpec.groovy @@ -22,7 +22,6 @@ import spock.lang.Shared import javax.naming.NamingException import javax.naming.directory.SearchControls -import javax.naming.ldap.LdapContext class LdapContextFactorySpec extends spock.lang.Specification { @Shared @@ -41,7 +40,7 @@ class LdapContextFactorySpec extends spock.lang.Specification { ldapConfiguration = Mock(LdapConfiguration) ldapConfiguration.getFactory() >> "com.sun.jndi.ldap.LdapCtxFactory" - ldapConfiguration.getProviderUrl() >> "ldap://localhost:389" + ldapConfiguration.getProviderUrl(_) >> "ldap://localhost:389" ldapConfiguration.getAuthentication() >> "none" ldapConfiguration.getScope() >> SearchControls.SUBTREE_SCOPE ldapConfiguration.getReturnAttributes() >> ["uid", "mail", "cn"] @@ -49,11 +48,11 @@ class LdapContextFactorySpec extends spock.lang.Specification { ldapConfiguration.getEmailAttribute() >> "mail" ldapConfiguration.getFirstnameAttribute() >> "givenname" ldapConfiguration.getLastnameAttribute() >> "sn" - ldapConfiguration.getBaseDn() >> "dc=cloudstack,dc=org" + ldapConfiguration.getBaseDn(_) >> "dc=cloudstack,dc=org" ldapConfiguration.getSSLStatus() >> true ldapConfiguration.getTrustStore() >> "/tmp/ldap.ts" ldapConfiguration.getTrustStorePassword() >> "password" - ldapConfiguration.getReadTimeout() >> 1000 + ldapConfiguration.getReadTimeout(_) >> 1000 ldapConfiguration.getLdapPageSize() >> 1 username = "rmurphy" @@ -87,7 +86,7 @@ class LdapContextFactorySpec extends spock.lang.Specification { def result = ldapContextFactory.getEnvironment(null, null, null, true) then: "The resulting values should be set" - result['java.naming.provider.url'] == ldapConfiguration.getProviderUrl() + result['java.naming.provider.url'] == ldapConfiguration.getProviderUrl(null) result['java.naming.factory.initial'] == ldapConfiguration.getFactory() result['java.naming.security.principal'] == null result['java.naming.security.authentication'] == ldapConfiguration.getAuthentication() @@ -102,7 +101,7 @@ class LdapContextFactorySpec extends spock.lang.Specification { def result = ldapContextFactory.getEnvironment(principal, password, null, false) then: "The resulting values should be set" - result['java.naming.provider.url'] == ldapConfiguration.getProviderUrl() + result['java.naming.provider.url'] == ldapConfiguration.getProviderUrl(null) result['java.naming.factory.initial'] == ldapConfiguration.getFactory() result['java.naming.security.principal'] == principal result['java.naming.security.authentication'] == "simple" diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapCreateAccountCmdSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapCreateAccountCmdSpec.groovy index a0b20bbcb13..db4fa232b50 100644 --- a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapCreateAccountCmdSpec.groovy +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapCreateAccountCmdSpec.groovy @@ -16,54 +16,13 @@ // under the License. package groovy.org.apache.cloudstack.ldap -import com.cloud.exception.InvalidParameterValueException -import org.apache.cloudstack.api.ServerApiException -import org.apache.cloudstack.api.command.LdapAddConfigurationCmd -import org.apache.cloudstack.api.response.LdapConfigurationResponse - import org.apache.cloudstack.ldap.LdapUser; import org.apache.cloudstack.ldap.LdapManager; -import org.apache.cloudstack.api.command.LdapCreateAccountCmd; -import org.apache.cloudstack.context.CallContext; - -import com.cloud.user.AccountService; -import com.cloud.user.UserAccount; -import com.cloud.user.UserAccountVO -import org.apache.cloudstack.ldap.NoLdapUserMatchingQueryException; - -import javax.naming.NamingException +import org.apache.cloudstack.api.command.LdapCreateAccountCmd +import com.cloud.user.AccountService class LdapCreateAccountCmdSpec extends spock.lang.Specification { - - def "Test failure to retrive LDAP user"() { - given: "We have an LdapManager, AccountService and LdapCreateAccountCmd and LDAP user that doesn't exist" - LdapManager ldapManager = Mock(LdapManager) - ldapManager.getUser(_) >> { throw new NoLdapUserMatchingQueryException() } - AccountService accountService = Mock(AccountService) - def ldapCreateAccountCmd = Spy(LdapCreateAccountCmd, constructorArgs: [ldapManager, accountService]) - ldapCreateAccountCmd.getCurrentContext() >> Mock(CallContext) - CallContext context = ldapCreateAccountCmd.getCurrentContext() - when: "An an account is created" - ldapCreateAccountCmd.execute() - then: "It fails and an exception is thrown" - thrown ServerApiException - } - - def "Test failed creation due to a null response from cloudstack account creater"() { - given: "We have an LdapManager, AccountService and LdapCreateAccountCmd" - LdapManager ldapManager = Mock(LdapManager) - ldapManager.getUser(_) >> new LdapUser("rmurphy", "rmurphy@cloudstack.org", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering", false) - AccountService accountService = Mock(AccountService) - def ldapCreateAccountCmd = Spy(LdapCreateAccountCmd, constructorArgs: [ldapManager, accountService]) - ldapCreateAccountCmd.getCurrentContext() >> Mock(CallContext) - ldapCreateAccountCmd.createCloudstackUserAccount(_, _, _) >> null - when: "Cloudstack fail to create the user" - ldapCreateAccountCmd.execute() - then: "An exception is thrown" - thrown ServerApiException - } - def "Test command name"() { given: "We have an LdapManager, AccountService and LdapCreateAccountCmd" LdapManager ldapManager = Mock(LdapManager) @@ -105,7 +64,7 @@ class LdapCreateAccountCmdSpec extends spock.lang.Specification { AccountService accountService = Mock(AccountService) def ldapCreateAccountCmd = new LdapCreateAccountCmd(ldapManager, accountService); when: "a user with an username, email, firstname and lastname is validated" - def result = ldapCreateAccountCmd.validateUser(new LdapUser("username", "email", "firstname", "lastname", "principal", "domain", false)) + def result = ldapCreateAccountCmd.validateUser(new LdapUser("username", "email", "firstname", "lastname", "principal", "domain", false, null)) then: "the result is true" result == true } @@ -116,7 +75,7 @@ class LdapCreateAccountCmdSpec extends spock.lang.Specification { AccountService accountService = Mock(AccountService) def ldapCreateAccountCmd = new LdapCreateAccountCmd(ldapManager, accountService) when: "A user with no email address attempts to validate" - ldapCreateAccountCmd.validateUser(new LdapUser("username", null, "firstname", "lastname", "principal", "domain", false)) + ldapCreateAccountCmd.validateUser(new LdapUser("username", null, "firstname", "lastname", "principal", "domain", false, null)) then: "An exception is thrown" thrown Exception } @@ -138,7 +97,7 @@ class LdapCreateAccountCmdSpec extends spock.lang.Specification { AccountService accountService = Mock(AccountService) def ldapCreateAccountCmd = new LdapCreateAccountCmd(ldapManager, accountService) when: "A user with no lastname attempts to validate" - ldapCreateAccountCmd.validateUser(new LdapUser("username", "email", "firstname", null, "principal", "domain", false)) + ldapCreateAccountCmd.validateUser(new LdapUser("username", "email", "firstname", null, "principal", "domain", false, null)) then: "An exception is thown" thrown Exception } diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapDeleteConfigurationCmdSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapDeleteConfigurationCmdSpec.groovy index 31d56ef68cb..caa524701b2 100644 --- a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapDeleteConfigurationCmdSpec.groovy +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapDeleteConfigurationCmdSpec.groovy @@ -27,7 +27,7 @@ class LdapDeleteConfigurationCmdSpec extends spock.lang.Specification { def "Test failed response from execute"() { given: "We have an LdapManager and LdapDeleteConfigurationCmd" def ldapManager = Mock(LdapManager) - ldapManager.deleteConfiguration(_) >> { throw new InvalidParameterValueException() } + ldapManager.deleteConfiguration(_, 0, null) >> { throw new InvalidParameterValueException() } def ldapDeleteConfigurationCmd = new LdapDeleteConfigurationCmd(ldapManager) when:"LdapDeleteConfigurationCmd is executed and no configuration exists" ldapDeleteConfigurationCmd.execute() @@ -48,7 +48,7 @@ class LdapDeleteConfigurationCmdSpec extends spock.lang.Specification { def "Test successful response from execute"() { given: "We have an LdapManager and LdapDeleteConfigurationCmd" def ldapManager = Mock(LdapManager) - ldapManager.deleteConfiguration(_) >> new LdapConfigurationResponse("localhost") + ldapManager.deleteConfiguration(_, 0, null) >> new LdapConfigurationResponse("localhost") def ldapDeleteConfigurationCmd = new LdapDeleteConfigurationCmd(ldapManager) when: "LdapDeleteConfigurationCmd is executed" ldapDeleteConfigurationCmd.execute() diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapImportUsersCmdSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapImportUsersCmdSpec.groovy index 434151ae234..68b910811c7 100644 --- a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapImportUsersCmdSpec.groovy +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapImportUsersCmdSpec.groovy @@ -24,10 +24,8 @@ import com.cloud.user.DomainService import com.cloud.user.User import com.cloud.user.UserAccountVO import com.cloud.user.UserVO -import org.apache.cloudstack.api.command.LdapCreateAccountCmd import org.apache.cloudstack.api.command.LdapImportUsersCmd import org.apache.cloudstack.api.response.LdapUserResponse -import org.apache.cloudstack.context.CallContext import org.apache.cloudstack.ldap.LdapManager import org.apache.cloudstack.ldap.LdapUser @@ -53,9 +51,9 @@ class LdapImportUsersCmdSpec extends spock.lang.Specification { def accountService = Mock(AccountService) List users = new ArrayList() - users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering", false)) - users.add(new LdapUser("bob", "bob@test.com", "Robert", "Young", "cn=bob,ou=engineering,dc=cloudstack,dc=org", "engineering", false)) - ldapManager.getUsers() >> users + users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering", false, null)) + users.add(new LdapUser("bob", "bob@test.com", "Robert", "Young", "cn=bob,ou=engineering,dc=cloudstack,dc=org", "engineering", false, null)) + ldapManager.getUsers(null) >> users LdapUserResponse response1 = new LdapUserResponse("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering") LdapUserResponse response2 = new LdapUserResponse("bob", "bob@test.com", "Robert", "Young", "cn=bob,ou=engineering,dc=cloudstack,dc=org", "engineering") ldapManager.createLdapUserResponse(_) >>> [response1, response2] @@ -81,9 +79,9 @@ class LdapImportUsersCmdSpec extends spock.lang.Specification { def accountService = Mock(AccountService) List users = new ArrayList() - users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering", false)) - users.add(new LdapUser("bob", "bob@test.com", "Robert", "Young", "cn=bob,ou=engineering,dc=cloudstack,dc=org", "engineering", false)) - ldapManager.getUsersInGroup("TestGroup") >> users + users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering", false, null)) + users.add(new LdapUser("bob", "bob@test.com", "Robert", "Young", "cn=bob,ou=engineering,dc=cloudstack,dc=org", "engineering", false, null)) + ldapManager.getUsersInGroup("TestGroup", null) >> users LdapUserResponse response1 = new LdapUserResponse("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering") LdapUserResponse response2 = new LdapUserResponse("bob", "bob@test.com", "Robert", "Young", "cn=bob,ou=engineering,dc=cloudstack,dc=org", "engineering") ldapManager.createLdapUserResponse(_) >>> [response1, response2] @@ -110,9 +108,9 @@ class LdapImportUsersCmdSpec extends spock.lang.Specification { def accountService = Mock(AccountService) List users = new ArrayList() - users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering", false)) - users.add(new LdapUser("bob", "bob@test.com", "Robert", "Young", "cn=bob,ou=engineering,dc=cloudstack,dc=org", "engineering", false)) - ldapManager.getUsersInGroup("TestGroup") >> users + users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering", false, null)) + users.add(new LdapUser("bob", "bob@test.com", "Robert", "Young", "cn=bob,ou=engineering,dc=cloudstack,dc=org", "engineering", false, null)) + ldapManager.getUsersInGroup("TestGroup", null) >> users LdapUserResponse response1 = new LdapUserResponse("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering") LdapUserResponse response2 = new LdapUserResponse("bob", "bob@test.com", "Robert", "Young", "cn=bob,ou=engineering,dc=cloudstack,dc=org", "engineering") ldapManager.createLdapUserResponse(_) >>> [response1, response2] @@ -139,9 +137,9 @@ class LdapImportUsersCmdSpec extends spock.lang.Specification { def accountService = Mock(AccountService) List users = new ArrayList() - users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering", false)) - users.add(new LdapUser("bob", "bob@test.com", "Robert", "Young", "cn=bob,ou=engineering,dc=cloudstack,dc=org", "engineering", false)) - ldapManager.getUsers() >> users + users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering", false, null)) + users.add(new LdapUser("bob", "bob@test.com", "Robert", "Young", "cn=bob,ou=engineering,dc=cloudstack,dc=org", "engineering", false, null)) + ldapManager.getUsers(null) >> users LdapUserResponse response1 = new LdapUserResponse("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering") LdapUserResponse response2 = new LdapUserResponse("bob", "bob@test.com", "Robert", "Young", "cn=bob,ou=engineering,dc=cloudstack,dc=org", "engineering") ldapManager.createLdapUserResponse(_) >>> [response1, response2] @@ -169,8 +167,8 @@ class LdapImportUsersCmdSpec extends spock.lang.Specification { ldapImportUsersCmd.domainId = varDomainId ldapImportUsersCmd.groupName = varGroupName - def ldapUser1 = new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering", false) - def ldapUser2 = new LdapUser("bob", "bob@test.com", "Robert", "Young", "cn=bob,ou=engineering,dc=cloudstack,dc=org", "engineering", false); + def ldapUser1 = new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering", false, null) + def ldapUser2 = new LdapUser("bob", "bob@test.com", "Robert", "Young", "cn=bob,ou=engineering,dc=cloudstack,dc=org", "engineering", false, null); Domain domain = new DomainVO(expectedDomainName, 1L, 1L, expectedDomainName, UUID.randomUUID().toString()); if (varDomainId != null) { @@ -204,8 +202,8 @@ class LdapImportUsersCmdSpec extends spock.lang.Specification { given: "We have an LdapManager, DomainService, two users and a LdapImportUsersCmd" def ldapManager = Mock(LdapManager) List users = new ArrayList() - users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering", false)) - ldapManager.getUsers() >> users + users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering", false, null)) + ldapManager.getUsers(null) >> users LdapUserResponse response1 = new LdapUserResponse("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering") ldapManager.createLdapUserResponse(_) >>> response1 @@ -234,8 +232,8 @@ class LdapImportUsersCmdSpec extends spock.lang.Specification { given: "We have an LdapManager, DomainService, two users and a LdapImportUsersCmd" def ldapManager = Mock(LdapManager) List users = new ArrayList() - users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering", false)) - ldapManager.getUsers() >> users + users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering", false, null)) + ldapManager.getUsers(null) >> users LdapUserResponse response1 = new LdapUserResponse("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering") ldapManager.createLdapUserResponse(_) >>> response1 @@ -263,8 +261,8 @@ class LdapImportUsersCmdSpec extends spock.lang.Specification { given: "We have an LdapManager, DomainService, two users and a LdapImportUsersCmd" def ldapManager = Mock(LdapManager) List users = new ArrayList() - users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering", false)) - ldapManager.getUsers() >> users + users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering", false, null)) + ldapManager.getUsers(null) >> users LdapUserResponse response1 = new LdapUserResponse("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering") ldapManager.createLdapUserResponse(_) >>> response1 diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapListUsersCmdSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapListUsersCmdSpec.groovy index 5247a1ec895..d6410d96866 100644 --- a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapListUsersCmdSpec.groovy +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapListUsersCmdSpec.groovy @@ -40,7 +40,7 @@ class LdapListUsersCmdSpec extends spock.lang.Specification { def "Test successful empty response from execute"() { given: "We have a LdapManager with no users, QueryService and a LdapListUsersCmd" def ldapManager = Mock(LdapManager) - ldapManager.getUsers() >> {throw new NoLdapUserMatchingQueryException()} + ldapManager.getUsers(null) >> {throw new NoLdapUserMatchingQueryException()} def queryService = Mock(QueryService) def ldapListUsersCmd = new LdapListUsersCmd(ldapManager, queryService) when: "LdapListUsersCmd is executed" @@ -53,8 +53,8 @@ class LdapListUsersCmdSpec extends spock.lang.Specification { given: "We have an LdapManager, one user, QueryService and a LdapListUsersCmd" def ldapManager = Mock(LdapManager) List users = new ArrayList() - users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null, false)) - ldapManager.getUsers() >> users + users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null, false, null)) + ldapManager.getUsers(null) >> users LdapUserResponse response = new LdapUserResponse("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null) ldapManager.createLdapUserResponse(_) >> response def queryService = Mock(QueryService) @@ -92,7 +92,7 @@ class LdapListUsersCmdSpec extends spock.lang.Specification { queryService.searchForUsers(_) >> queryServiceResponse - def ldapUser = new LdapUser("rmurphy", "rmurphy@cloudstack.org", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null, false) + def ldapUser = new LdapUser("rmurphy", "rmurphy@cloudstack.org", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null, false, null) def ldapListUsersCmd = new LdapListUsersCmd(ldapManager,queryService) when: "isACloudstackUser is executed" @@ -109,7 +109,7 @@ class LdapListUsersCmdSpec extends spock.lang.Specification { queryService.searchForUsers(_) >> new ListResponse() - def ldapUser = new LdapUser("rmurphy", "rmurphy@cloudstack.org", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null, false) + def ldapUser = new LdapUser("rmurphy", "rmurphy@cloudstack.org", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null, false, null) def ldapListUsersCmd = new LdapListUsersCmd(ldapManager,queryService) when: "isACloudstackUser is executed" diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapManagerImplSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapManagerImplSpec.groovy index c9af0020848..238b2790836 100644 --- a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapManagerImplSpec.groovy +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapManagerImplSpec.groovy @@ -52,7 +52,7 @@ class LdapManagerImplSpec extends spock.lang.Specification { ldapContextFactory.createBindContext() >> { throw new NoLdapUserMatchingQueryException() } def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManagerFactory, ldapConfiguration) when: "We search for a user but there is a bind issue" - ldapManager.getUser("rmurphy") + ldapManager.getUser("rmurphy", null) then: "an exception is thrown" thrown NoLdapUserMatchingQueryException } @@ -68,7 +68,7 @@ class LdapManagerImplSpec extends spock.lang.Specification { ldapContextFactory.createBindContext() >> { throw new NamingException() } def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManagerFactory, ldapConfiguration) when: "We search for a group of users but there is a bind issue" - ldapManager.getUsers() + ldapManager.getUsers(null) then: "An exception is thrown" thrown NoLdapUserMatchingQueryException } @@ -116,7 +116,7 @@ class LdapManagerImplSpec extends spock.lang.Specification { def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManagerFactory, ldapConfiguration) when: "A ldap user response is generated" def result = ldapManager.createLdapUserResponse(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", - "engineering", false)) + "engineering", false, null)) then: "The result of the response should match the given ldap user" result.username == "rmurphy" result.email == "rmurphy@test.com" @@ -136,11 +136,11 @@ class LdapManagerImplSpec extends spock.lang.Specification { ldapUserManagerFactory.getInstance(_) >> ldapUserManager ldapContextFactory.createBindContext() >> null List users = new ArrayList<>(); - users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null, false)) + users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null, false, null)) ldapUserManager.getUsers(_) >> users; def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManagerFactory, ldapConfiguration) when: "We search for a group of users" - def result = ldapManager.getUsers() + def result = ldapManager.getUsers(null) then: "A list greater than 0 is returned" result.size() > 0; } @@ -154,10 +154,10 @@ class LdapManagerImplSpec extends spock.lang.Specification { def ldapConfiguration = Mock(LdapConfiguration) ldapUserManagerFactory.getInstance(_) >> ldapUserManager ldapContextFactory.createBindContext() >> null - ldapUserManager.getUser(_, _) >> new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null, false) + ldapUserManager.getUser(_, _, _) >> new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null, false, null) def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManagerFactory, ldapConfiguration) when: "We search for a user" - def result = ldapManager.getUser("rmurphy") + def result = ldapManager.getUser("rmurphy", null) then: "The user is returned" result.username == "rmurphy" result.email == "rmurphy@test.com" @@ -192,9 +192,9 @@ class LdapManagerImplSpec extends spock.lang.Specification { ldapUserManagerFactory.getInstance(_) >> ldapUserManager def ldapConfiguration = Mock(LdapConfiguration) def ldapManager = Spy(LdapManagerImpl, constructorArgs: [ldapConfigurationDao, ldapContextFactory, ldapUserManagerFactory, ldapConfiguration]) - ldapManager.getUser(_) >> { new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null) } + ldapManager.getUser(_, null) >> { new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null) } when: "The user attempts to authenticate with a bad password" - def result = ldapManager.canAuthenticate("rmurphy", "password") + def result = ldapManager.canAuthenticate("rmurphy", "password", null) then: "The authentication fails" result == false } @@ -210,7 +210,7 @@ class LdapManagerImplSpec extends spock.lang.Specification { ldapConfigurationDao.findByHostname(_) >> null def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManagerFactory, ldapConfiguration) when: "A ldap configuration that doesn't exist is deleted" - ldapManager.deleteConfiguration("localhost") + ldapManager.deleteConfiguration("localhost", 0, null) then: "A exception is thrown" thrown InvalidParameterValueException } @@ -242,9 +242,9 @@ class LdapManagerImplSpec extends spock.lang.Specification { def ldapConfiguration = Mock(LdapConfiguration) ldapUserManagerFactory.getInstance(_) >> ldapUserManager def ldapManager = Spy(LdapManagerImpl, constructorArgs: [ldapConfigurationDao, ldapContextFactory, ldapUserManagerFactory, ldapConfiguration]) - ldapManager.getUser(_) >> { new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null) } + ldapManager.getUser(_, null) >> { new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null) } when: "A user authenticates" - def result = ldapManager.canAuthenticate("rmurphy", "password") + def result = ldapManager.canAuthenticate("rmurphy", "password", null) then: "The result is true" result == true } @@ -265,7 +265,7 @@ class LdapManagerImplSpec extends spock.lang.Specification { ldapConfigurationDao.remove(_) >> null def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManagerFactory, ldapConfiguration) when: "A ldap configuration is deleted" - def result = ldapManager.deleteConfiguration("localhost") + def result = ldapManager.deleteConfiguration("localhost", 0, null) then: "The deleted configuration is returned" result.hostname == "localhost" result.port == 389 @@ -282,7 +282,7 @@ class LdapManagerImplSpec extends spock.lang.Specification { ldapContextFactory.createBindContext() >> null; List users = new ArrayList(); - users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering", false)) + users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering", false, null)) ldapUserManager.getUsers(_, _) >> users; def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManagerFactory, ldapConfiguration) @@ -424,11 +424,11 @@ class LdapManagerImplSpec extends spock.lang.Specification { ldapUserManagerFactory.getInstance(_) >> ldapUserManager ldapContextFactory.createBindContext() >> null List users = new ArrayList<>(); - users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", "engineering", false)) + users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", "engineering", false, null)) ldapUserManager.getUsersInGroup("engineering", _) >> users; def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManagerFactory, ldapConfiguration) when: "We search for a group of users" - def result = ldapManager.getUsersInGroup("engineering") + def result = ldapManager.getUsersInGroup("engineering", null) then: "A list greater of size one is returned" result.size() == 1; } @@ -524,7 +524,7 @@ class LdapManagerImplSpec extends spock.lang.Specification { def type = "GROUP" def name = "CN=test,DC=citrix,DC=com" - ldapUserManager.getUser(username, type, name, _) >> new LdapUser(username, "email", "firstname", "lastname", "principal", "domain", true) + ldapUserManager.getUser(username, type, name, _) >> new LdapUser(username, "email", "firstname", "lastname", "principal", "domain", true, null) when: LdapUser user = ldapManager.getUser(username, type, name) @@ -574,7 +574,7 @@ class LdapManagerImplSpec extends spock.lang.Specification { def type = "GROUP" def name = "CN=test,DC=citrix,DC=com" - ldapUserManager.getUser(username, type, name, _) >> new LdapUser(username, "email", "firstname", "lastname", "principal", "domain", false) + ldapUserManager.getUser(username, type, name, _) >> new LdapUser(username, "email", "firstname", "lastname", "principal", "domain", false, null) when: LdapUser user = ldapManager.getUser(username, type, name) diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapSearchUserCmdSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapSearchUserCmdSpec.groovy index 55510875899..8936024c01b 100644 --- a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapSearchUserCmdSpec.groovy +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapSearchUserCmdSpec.groovy @@ -48,7 +48,7 @@ class LdapSearchUserCmdSpec extends spock.lang.Specification { given: "We have an Ldap manager and ldap user search cmd" def ldapManager = Mock(LdapManager) List users = new ArrayList() - users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null, false)) + users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null, false, null)) ldapManager.searchUsers(_) >> users LdapUserResponse response = new LdapUserResponse("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null) ldapManager.createLdapUserResponse(_) >> response diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserSpec.groovy index 8ddfc9a23b8..36b37cad9d0 100644 --- a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserSpec.groovy +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserSpec.groovy @@ -22,7 +22,7 @@ class LdapUserSpec extends spock.lang.Specification { def "Testing LdapUsers hashCode generation"() { given: - def userA = new LdapUser(usernameA, "", "", "", "", "", false) + def userA = new LdapUser(usernameA, "", "", "", "", "", false, null) expect: userA.hashCode() == usernameA.hashCode() where: @@ -31,8 +31,8 @@ class LdapUserSpec extends spock.lang.Specification { def "Testing that LdapUser successfully gives the correct result for a compare to"() { given: "You have created two LDAP user objects" - def userA = new LdapUser(usernameA, "", "", "", "", "", false) - def userB = new LdapUser(usernameB, "", "", "", "", "", false) + def userA = new LdapUser(usernameA, "", "", "", "", "", false, null) + def userB = new LdapUser(usernameB, "", "", "", "", "", false, null) expect: "That when compared the result is less than or equal to 0" userA.compareTo(userB) <= 0 where: "The following values are used" @@ -43,8 +43,8 @@ class LdapUserSpec extends spock.lang.Specification { def "Testing that LdapUsers equality"() { given: - def userA = new LdapUser(usernameA, "", "", "", "", "", false) - def userB = new LdapUser(usernameB, "", "", "", "", "", false) + def userA = new LdapUser(usernameA, "", "", "", "", "", false, null) + def userB = new LdapUser(usernameB, "", "", "", "", "", false, null) expect: userA.equals(userA) == true userA.equals(new Object()) == false @@ -56,7 +56,7 @@ class LdapUserSpec extends spock.lang.Specification { def "Testing that the username is correctly set with the ldap object"() { given: "You have created a LDAP user object with a username" - def user = new LdapUser(username, "", "", "", "", "", false) + def user = new LdapUser(username, "", "", "", "", "", false, null) expect: "The username is equal to the given data source" user.getUsername() == username where: "The username is set to " @@ -65,7 +65,7 @@ class LdapUserSpec extends spock.lang.Specification { def "Testing the email is correctly set with the ldap object"() { given: "You have created a LDAP user object with a email" - def user = new LdapUser("", email, "", "", "", "", false) + def user = new LdapUser("", email, "", "", "", "", false, null) expect: "The email is equal to the given data source" user.getEmail() == email where: "The email is set to " @@ -74,7 +74,7 @@ class LdapUserSpec extends spock.lang.Specification { def "Testing the firstname is correctly set with the ldap object"() { given: "You have created a LDAP user object with a firstname" - def user = new LdapUser("", "", firstname, "", "", "", false) + def user = new LdapUser("", "", firstname, "", "", "", false, null) expect: "The firstname is equal to the given data source" user.getFirstname() == firstname where: "The firstname is set to " @@ -83,7 +83,7 @@ class LdapUserSpec extends spock.lang.Specification { def "Testing the lastname is correctly set with the ldap object"() { given: "You have created a LDAP user object with a lastname" - def user = new LdapUser("", "", "", lastname, "", "", false) + def user = new LdapUser("", "", "", lastname, "", "", false, null) expect: "The lastname is equal to the given data source" user.getLastname() == lastname where: "The lastname is set to " @@ -92,7 +92,7 @@ class LdapUserSpec extends spock.lang.Specification { def "Testing the principal is correctly set with the ldap object"() { given: "You have created a LDAP user object with a principal" - def user = new LdapUser("", "", "", "", principal, "", false) + def user = new LdapUser("", "", "", "", principal, "", false, null) expect: "The principal is equal to the given data source" user.getPrincipal() == principal where: "The principal is set to " @@ -101,7 +101,7 @@ class LdapUserSpec extends spock.lang.Specification { def "Testing the domain is correctly set with the ldap object"() { given: "You have created a LDAP user object with a principal" - def user = new LdapUser("", "", "", "", "", domain, false) + def user = new LdapUser("", "", "", "", "", domain, false, null) expect: "The principal is equal to the given data source" user.getDomain() == domain where: "The username is set to " diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LinkDomainToLdapCmdSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LinkDomainToLdapCmdSpec.groovy index bad41e82805..46b00a93d6c 100644 --- a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LinkDomainToLdapCmdSpec.groovy +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LinkDomainToLdapCmdSpec.groovy @@ -79,7 +79,7 @@ class LinkDomainToLdapCmdSpec extends Specification { LinkDomainToLdapResponse response = new LinkDomainToLdapResponse(domainId, type, name, (short)accountType) _ldapManager.linkDomainToLdap(_,_,_,_) >> response - _ldapManager.getUser(username, type, name) >> new LdapUser(username, "admin@ccp.citrix.com", "Admin", "Admin", name, "ccp", true) + _ldapManager.getUser(username, type, name) >> new LdapUser(username, "admin@ccp.citrix.com", "Admin", "Admin", name, "ccp", true, null) linkDomainToLdapCmd.admin = username linkDomainToLdapCmd.type = type @@ -107,7 +107,7 @@ class LinkDomainToLdapCmdSpec extends Specification { LinkDomainToLdapResponse response = new LinkDomainToLdapResponse(domainId.toString(), type, name, (short)accountType) _ldapManager.linkDomainToLdap(_,_,_,_) >> response - _ldapManager.getUser(username, type, name) >> new LdapUser(username, "admin@ccp.citrix.com", "Admin", "Admin", name, "ccp", false) + _ldapManager.getUser(username, type, name) >> new LdapUser(username, "admin@ccp.citrix.com", "Admin", "Admin", name, "ccp", false, null) _accountService.getActiveAccountByName(username, domainId) >> Mock(Account) @@ -204,7 +204,7 @@ class LinkDomainToLdapCmdSpec extends Specification { LinkDomainToLdapResponse response = new LinkDomainToLdapResponse(domainId.toString(), type, name, (short)accountType) _ldapManager.linkDomainToLdap(_,_,_,_) >> response - _ldapManager.getUser(username, type, name) >> new LdapUser(username, "admin@ccp.citrix.com", "Admin", "Admin", name, "ccp", false) + _ldapManager.getUser(username, type, name) >> new LdapUser(username, "admin@ccp.citrix.com", "Admin", "Admin", name, "ccp", false, null) _accountService.getActiveAccountByName(username, domainId) >> null UserAccount userAccount = Mock(UserAccount) diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/OpenLdapUserManagerSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/OpenLdapUserManagerSpec.groovy index cb08c8fd47c..40daa4110fc 100644 --- a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/OpenLdapUserManagerSpec.groovy +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/OpenLdapUserManagerSpec.groovy @@ -17,14 +17,12 @@ package groovy.org.apache.cloudstack.ldap import org.apache.cloudstack.ldap.LdapConfiguration -import org.apache.cloudstack.ldap.LdapUserManager import org.apache.cloudstack.ldap.OpenLdapUserManagerImpl import spock.lang.Shared import javax.naming.NamingException import javax.naming.directory.Attribute import javax.naming.directory.Attributes -import javax.naming.directory.InitialDirContext import javax.naming.directory.SearchControls import javax.naming.directory.SearchResult import javax.naming.ldap.InitialLdapContext @@ -167,12 +165,12 @@ class OpenLdapUserManagerSpec extends spock.lang.Specification { ldapConfiguration.getEmailAttribute() >> "mail" ldapConfiguration.getFirstnameAttribute() >> "givenname" ldapConfiguration.getLastnameAttribute() >> "sn" - ldapConfiguration.getBaseDn() >> "dc=cloudstack,dc=org" + ldapConfiguration.getBaseDn(_) >> "dc=cloudstack,dc=org" ldapConfiguration.getCommonNameAttribute() >> "cn" ldapConfiguration.getGroupObject() >> "groupOfUniqueNames" - ldapConfiguration.getGroupUniqueMemeberAttribute() >> "uniquemember" + ldapConfiguration.getGroupUniqueMemberAttribute(_) >> "uniquemember" ldapConfiguration.getLdapPageSize() >> 1 - ldapConfiguration.getReadTimeout() >> 1000 + ldapConfiguration.getReadTimeout(_) >> 1000 username = "rmurphy" email = "rmurphy@test.com" @@ -186,7 +184,7 @@ class OpenLdapUserManagerSpec extends spock.lang.Specification { def attributes = createUserAttributes(username, email, firstname, lastname) def search = createSearchResult(attributes) def userManager = new OpenLdapUserManagerImpl(ldapConfiguration) - def result = userManager.createUser(search) + def result = userManager.createUser(search,) expect: "The crated user the data supplied from LDAP" @@ -290,7 +288,7 @@ class OpenLdapUserManagerSpec extends spock.lang.Specification { def ldapUserManager = new OpenLdapUserManagerImpl(ldapConfiguration) when: "A request for users is made" - def result = ldapUserManager.getUsersInGroup("engineering", createGroupSearchContextOneUser()) + def result = ldapUserManager.getUsersInGroup("engineering", createGroupSearchContextOneUser(),) then: "one user is returned" result.size() == 1 } @@ -300,7 +298,7 @@ class OpenLdapUserManagerSpec extends spock.lang.Specification { def ldapUserManager = new OpenLdapUserManagerImpl(ldapConfiguration) when: "A request for users is made" - def result = ldapUserManager.getUsersInGroup("engineering", createGroupSearchContextNoUser()) + def result = ldapUserManager.getUsersInGroup("engineering", createGroupSearchContextNoUser(),) then: "no user is returned" result.size() == 0 } diff --git a/plugins/user-authenticators/ldap/test/org/apache/cloudstack/api/command/LdapConfigurationChanger.java b/plugins/user-authenticators/ldap/test/org/apache/cloudstack/api/command/LdapConfigurationChanger.java new file mode 100644 index 00000000000..61aa959e81a --- /dev/null +++ b/plugins/user-authenticators/ldap/test/org/apache/cloudstack/api/command/LdapConfigurationChanger.java @@ -0,0 +1,56 @@ +// 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 org.apache.cloudstack.api.command; + +import java.lang.reflect.Field; + +interface LdapConfigurationChanger { + /** + * sets a possibly not accessible field of the target object. + * @param target the object to set a hidden fields value in. + * @param name the name of the field to set. + * @param o intended value for the field "name" + * @throws IllegalAccessException + * @throws NoSuchFieldException + */ + default void setHiddenField(Object target, final String name, final Object o) throws IllegalAccessException, NoSuchFieldException { + Class klas = target.getClass(); + Field f = getFirstFoundField(name, klas); + f.setAccessible(true); + f.set(target, o); + } + + /** + * the first field found by this name in the class "klas" or any of it's superclasses except for {@code Object}. Implementers of this interface can decide to also return any field in implemented interfaces or in {@code Object}. + * + * @param name of the field to find + * @param klas class to gat a field by name "name" from + * @return a {@code Field} by the name "name" + * @throws NoSuchFieldException + */ + default Field getFirstFoundField(String name, Class klas) throws NoSuchFieldException { + try { + return klas.getDeclaredField(name); + } catch (NoSuchFieldException e) { + Class parent = klas.getSuperclass(); + if(parent.equals(Object.class)) { + throw e; + } + return getFirstFoundField(name, parent); + } + } +} \ No newline at end of file diff --git a/plugins/user-authenticators/ldap/test/org/apache/cloudstack/api/command/LdapCreateAccountCmdTest.java b/plugins/user-authenticators/ldap/test/org/apache/cloudstack/api/command/LdapCreateAccountCmdTest.java new file mode 100644 index 00000000000..a4eccbf0856 --- /dev/null +++ b/plugins/user-authenticators/ldap/test/org/apache/cloudstack/api/command/LdapCreateAccountCmdTest.java @@ -0,0 +1,72 @@ +// 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 org.apache.cloudstack.api.command; + +import com.cloud.user.Account; +import com.cloud.user.AccountService; +import org.apache.cloudstack.acl.RoleService; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.ldap.LdapManager; +import org.apache.cloudstack.ldap.LdapUser; +import org.apache.cloudstack.ldap.NoLdapUserMatchingQueryException; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import static org.junit.Assert.fail; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.isNull; +import static org.powermock.api.mockito.PowerMockito.spy; +import static org.powermock.api.mockito.PowerMockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class LdapCreateAccountCmdTest implements LdapConfigurationChanger { + @Mock + LdapManager ldapManager; + @Mock + AccountService accountService; + @Mock + RoleService roleService; + + LdapCreateAccountCmd ldapCreateAccountCmd; + + @Before + public void setUp() throws NoSuchFieldException, IllegalAccessException { + ldapCreateAccountCmd = spy(new LdapCreateAccountCmd(ldapManager, accountService)); + ldapCreateAccountCmd.roleService = roleService; + setHiddenField(ldapCreateAccountCmd,"accountType", Account.ACCOUNT_TYPE_DOMAIN_ADMIN); + } + + @Test(expected = ServerApiException.class) + public void failureToRetrieveLdapUser() throws Exception { + // We have an LdapManager, AccountService and LdapCreateAccountCmd and LDAP user that doesn't exist + when(ldapManager.getUser(anyString(), isNull(Long.class))).thenThrow(NoLdapUserMatchingQueryException.class); + ldapCreateAccountCmd.execute(); + fail("An exception should have been thrown: " + ServerApiException.class); + } + + @Test(expected = ServerApiException.class) + public void failedCreationDueToANullResponseFromCloudstackAccountCreater() throws Exception { + // We have an LdapManager, AccountService and LdapCreateAccountCmd + LdapUser mrMurphy = new LdapUser("rmurphy", "rmurphy@cloudstack.org", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering", false, null); + when(ldapManager.getUser(anyString(), isNull(Long.class))).thenReturn(mrMurphy); + ldapCreateAccountCmd.execute(); + fail("An exception should have been thrown: " + ServerApiException.class); + } +} \ No newline at end of file diff --git a/plugins/user-authenticators/ldap/test/org/apache/cloudstack/api/command/LdapImportUsersCmdTest.java b/plugins/user-authenticators/ldap/test/org/apache/cloudstack/api/command/LdapImportUsersCmdTest.java new file mode 100644 index 00000000000..8db26733210 --- /dev/null +++ b/plugins/user-authenticators/ldap/test/org/apache/cloudstack/api/command/LdapImportUsersCmdTest.java @@ -0,0 +1,85 @@ +// 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 org.apache.cloudstack.api.command; + +import com.cloud.domain.Domain; +import com.cloud.domain.DomainVO; +import com.cloud.user.Account; +import com.cloud.user.AccountService; +import com.cloud.user.DomainService; +import org.apache.cloudstack.acl.RoleService; +import org.apache.cloudstack.api.response.ListResponse; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.apache.cloudstack.api.response.LdapUserResponse; +import org.apache.cloudstack.ldap.LdapManager; +import org.apache.cloudstack.ldap.LdapUser; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import static junit.framework.TestCase.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.powermock.api.mockito.PowerMockito.spy; +import static org.powermock.api.mockito.PowerMockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class LdapImportUsersCmdTest implements LdapConfigurationChanger { + @Mock + LdapManager ldapManager; + @Mock + AccountService accountService; + @Mock + DomainService domainService; + @Mock + RoleService roleService; + + LdapImportUsersCmd ldapImportUsersCmd; + + @Before + public void setUp() throws NoSuchFieldException, IllegalAccessException { + ldapImportUsersCmd = spy(new LdapImportUsersCmd(ldapManager, domainService, accountService)); + ldapImportUsersCmd.roleService = roleService; + setHiddenField(ldapImportUsersCmd, "accountType", Account.ACCOUNT_TYPE_DOMAIN_ADMIN); + } + + @Test + public void successfulResponseFromExecute() throws Exception { + List users = new ArrayList(); + users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering", false, null)); + users.add(new LdapUser("bob", "bob@test.com", "Robert", "Young", "cn=bob,ou=engineering,dc=cloudstack,dc=org", "engineering", false, null)); + when(ldapManager.getUsers(null)).thenReturn(users); + LdapUserResponse response1 = new LdapUserResponse("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering"); + LdapUserResponse response2 = new LdapUserResponse("bob", "bob@test.com", "Robert", "Young", "cn=bob,ou=engineering,dc=cloudstack,dc=org", "engineering"); + when(ldapManager.createLdapUserResponse(any(LdapUser.class))).thenReturn(response1).thenReturn(response2); + + + Domain domain = new DomainVO("engineering", 1L, 1L, "engineering", UUID.randomUUID().toString()); + when(domainService.getDomainByName("engineering", 1L)).thenReturn(null, domain); + when(domainService.createDomain(eq("engineering"), eq(1L), eq("engineering"), anyString())).thenReturn(domain); + + ldapImportUsersCmd.execute(); + ListResponse resp = (ListResponse)ldapImportUsersCmd.getResponseObject(); + assertEquals(" when LdapListUsersCmd is executed, a list of size 2 should be returned", 2, resp.getResponses().size()); + } +} \ No newline at end of file diff --git a/plugins/user-authenticators/ldap/test/org/apache/cloudstack/api/command/LinkAccountToLdapCmdTest.java b/plugins/user-authenticators/ldap/test/org/apache/cloudstack/api/command/LinkAccountToLdapCmdTest.java new file mode 100644 index 00000000000..35aed6a50cf --- /dev/null +++ b/plugins/user-authenticators/ldap/test/org/apache/cloudstack/api/command/LinkAccountToLdapCmdTest.java @@ -0,0 +1,97 @@ +// 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 org.apache.cloudstack.api.command; + +import com.cloud.user.Account; +import com.cloud.user.AccountService; +import com.cloud.user.User; +import com.cloud.user.UserAccountVO; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.response.LinkAccountToLdapResponse; +import org.apache.cloudstack.ldap.LdapManager; +import org.apache.cloudstack.ldap.LdapUser; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isNull; +import static org.powermock.api.mockito.PowerMockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class LinkAccountToLdapCmdTest implements LdapConfigurationChanger { + + @Mock + LdapManager ldapManager; + @Mock + AccountService accountService; + + LinkAccountToLdapCmd linkAccountToLdapCmd; + + @Before + public void setUp() throws NoSuchFieldException, IllegalAccessException { + linkAccountToLdapCmd = new LinkAccountToLdapCmd(); + setHiddenField(linkAccountToLdapCmd, "_ldapManager", ldapManager); + setHiddenField(linkAccountToLdapCmd, "_accountService", accountService); + } + + @Test + public void execute() throws Exception { + // test with valid params and with admin who doesnt exist in cloudstack + long domainId = 1; + String type = "GROUP"; + String ldapDomain = "CN=test,DC=ccp,DC=Citrix,DC=com"; + short accountType = Account.ACCOUNT_TYPE_DOMAIN_ADMIN; + String username = "admin"; + long accountId = 24; + String accountName = "test"; + + setHiddenField(linkAccountToLdapCmd, "ldapDomain", ldapDomain); + setHiddenField(linkAccountToLdapCmd, "admin", username); + setHiddenField(linkAccountToLdapCmd, "type", type); + setHiddenField(linkAccountToLdapCmd, "domainId", domainId); + setHiddenField(linkAccountToLdapCmd, "accountType", accountType); + setHiddenField(linkAccountToLdapCmd, "accountName", accountName); + + + LinkAccountToLdapResponse response = new LinkAccountToLdapResponse(String.valueOf(domainId), type, ldapDomain, (short)accountType, username, accountName); + when(ldapManager.linkAccountToLdap(linkAccountToLdapCmd)).thenReturn(response); + when(ldapManager.getUser(username, type, ldapDomain, 1L)) + .thenReturn(new LdapUser(username, "admin@ccp.citrix.com", "Admin", "Admin", ldapDomain, "ccp", false, null)); + + when(accountService.getActiveAccountByName(username, domainId)).thenReturn(null); + UserAccountVO userAccount = new UserAccountVO(); + userAccount.setAccountId(24); + when(accountService.createUserAccount(eq(username), eq(""), eq("Admin"), eq("Admin"), eq("admin@ccp.citrix.com"), isNull(String.class), + eq(username), eq(Account.ACCOUNT_TYPE_DOMAIN_ADMIN), eq(RoleType.DomainAdmin.getId()), eq(domainId), isNull(String.class), + (java.util.Map)isNull(), anyString(), anyString(), eq(User.Source.LDAP))).thenReturn(userAccount); + + linkAccountToLdapCmd.execute(); + LinkAccountToLdapResponse result = (LinkAccountToLdapResponse)linkAccountToLdapCmd.getResponseObject(); + assertEquals("objectName", linkAccountToLdapCmd.APINAME, result.getObjectName()); + assertEquals("commandName", linkAccountToLdapCmd.getCommandName(), result.getResponseName()); + assertEquals("domainId", String.valueOf(domainId), result.getDomainId()); + assertEquals("type", type, result.getType()); + assertEquals("name", ldapDomain, result.getLdapDomain()); + assertEquals("accountId", String.valueOf(accountId), result.getAdminId()); + } +} diff --git a/plugins/user-authenticators/ldap/test/org/apache/cloudstack/api/command/LinkDomainToLdapCmdTest.java b/plugins/user-authenticators/ldap/test/org/apache/cloudstack/api/command/LinkDomainToLdapCmdTest.java new file mode 100644 index 00000000000..8f1fa677cb9 --- /dev/null +++ b/plugins/user-authenticators/ldap/test/org/apache/cloudstack/api/command/LinkDomainToLdapCmdTest.java @@ -0,0 +1,98 @@ +// 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 org.apache.cloudstack.api.command; + +import com.cloud.user.Account; +import com.cloud.user.AccountService; +import com.cloud.user.User; +import com.cloud.user.UserAccountVO; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.response.LinkDomainToLdapResponse; +import org.apache.cloudstack.ldap.LdapManager; +import org.apache.cloudstack.ldap.LdapUser; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isNull; +import static org.powermock.api.mockito.PowerMockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class LinkDomainToLdapCmdTest implements LdapConfigurationChanger +{ + @Mock + LdapManager ldapManager; + @Mock + AccountService accountService; + + LinkDomainToLdapCmd linkDomainToLdapCmd; + + @Before + public void setUp() throws NoSuchFieldException, IllegalAccessException { + linkDomainToLdapCmd = new LinkDomainToLdapCmd(); + setHiddenField(linkDomainToLdapCmd, "_ldapManager", ldapManager); + setHiddenField(linkDomainToLdapCmd, "_accountService", accountService); + } + + @After + public void tearDown() { + } + + @Test + public void execute() throws Exception { +// test with valid params and with admin who doesnt exist in cloudstack + Long domainId = 1L; + String type = "GROUP"; + String ldapDomain = "CN=test,DC=ccp,DC=Citrix,DC=com"; + short accountType = Account.ACCOUNT_TYPE_DOMAIN_ADMIN; + String username = "admin"; + long accountId = 24; + setHiddenField(linkDomainToLdapCmd, "ldapDomain", ldapDomain); + setHiddenField(linkDomainToLdapCmd, "admin", username); + setHiddenField(linkDomainToLdapCmd, "type", type); + setHiddenField(linkDomainToLdapCmd, "domainId", domainId); + setHiddenField(linkDomainToLdapCmd, "accountType", accountType); + + LinkDomainToLdapResponse response = new LinkDomainToLdapResponse(domainId.toString(), type, ldapDomain, (short)accountType); + when(ldapManager.linkDomainToLdap(linkDomainToLdapCmd)).thenReturn(response); + when(ldapManager.getUser(username, type, ldapDomain, 1L)).thenReturn(new LdapUser(username, "admin@ccp.citrix.com", "Admin", "Admin", ldapDomain, "ccp", false, null)); + + when(accountService.getActiveAccountByName(username, domainId)).thenReturn(null); + UserAccountVO userAccount = new UserAccountVO(); + userAccount.setAccountId(24); + when(accountService.createUserAccount(eq(username), eq(""), eq("Admin"), eq("Admin"), eq("admin@ccp.citrix.com"), isNull(String.class), + eq(username), eq(Account.ACCOUNT_TYPE_DOMAIN_ADMIN), eq(RoleType.DomainAdmin.getId()), eq(domainId), isNull(String.class), + (java.util.Map)isNull(), anyString(), anyString(), eq(User.Source.LDAP))).thenReturn(userAccount); + + + linkDomainToLdapCmd.execute(); + LinkDomainToLdapResponse result = (LinkDomainToLdapResponse)linkDomainToLdapCmd.getResponseObject(); + assertEquals("objectName", "LinkDomainToLdap", result.getObjectName()); + assertEquals("commandName", linkDomainToLdapCmd.getCommandName(), result.getResponseName()); + assertEquals("domainId", domainId.toString(), result.getDomainId()); + assertEquals("type", type, result.getType()); + assertEquals("name", ldapDomain, result.getLdapDomain()); + assertEquals("accountId", String.valueOf(accountId), result.getAdminId()); + } + +} diff --git a/plugins/user-authenticators/ldap/test/org/apache/cloudstack/ldap/LdapConfigurationTest.java b/plugins/user-authenticators/ldap/test/org/apache/cloudstack/ldap/LdapConfigurationTest.java new file mode 100644 index 00000000000..52c70ac0d19 --- /dev/null +++ b/plugins/user-authenticators/ldap/test/org/apache/cloudstack/ldap/LdapConfigurationTest.java @@ -0,0 +1,141 @@ +// 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 org.apache.cloudstack.ldap; + +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.ldap.dao.LdapConfigurationDao; +import org.apache.cloudstack.ldap.dao.LdapConfigurationDaoImpl; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +@RunWith(MockitoJUnitRunner.class) +public class LdapConfigurationTest { + + LdapConfigurationDao ldapConfigurationDao; + LdapConfiguration ldapConfiguration; + + private void overrideConfigValue(final String configKeyName, final Object o) throws IllegalAccessException, NoSuchFieldException { + Field configKey = LdapConfiguration.class.getDeclaredField(configKeyName); + configKey.setAccessible(true); + + ConfigKey key = (ConfigKey)configKey.get(ldapConfiguration); + + Field modifiersField = Field.class.getDeclaredField("modifiers"); + modifiersField.setAccessible(true); + modifiersField.setInt(configKey, configKey.getModifiers() & ~Modifier.FINAL); + + Field f = ConfigKey.class.getDeclaredField("_value"); + f.setAccessible(true); + modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL); + f.set(key, o); + + Field dynamic = ConfigKey.class.getDeclaredField("_isDynamic"); + dynamic.setAccessible(true); + modifiersField.setInt(dynamic, dynamic.getModifiers() & ~Modifier.FINAL); + dynamic.setBoolean(key, false); + } + + @Before + public void init() throws Exception { + ldapConfigurationDao = new LdapConfigurationDaoImpl(); + ldapConfiguration = new LdapConfiguration(ldapConfigurationDao);; + } + + @Test + public void getAuthenticationReturnsSimple() throws Exception { + overrideConfigValue("ldapBindPrincipal", "cn=bla"); + overrideConfigValue("ldapBindPassword", "pw"); + String authentication = ldapConfiguration.getAuthentication(null); + assertEquals("authentication should be set to simple", "simple", authentication); + } + + + @Test + public void getBaseDnReturnsABaseDn() throws Exception { + overrideConfigValue("ldapBaseDn", "dc=cloudstack,dc=org"); + String baseDn = ldapConfiguration.getBaseDn(null); + assertEquals("The set baseDn should be returned","dc=cloudstack,dc=org", baseDn); + } + + @Test + public void getGroupUniqueMemberAttribute() throws Exception { + String [] groupNames = {"bla", "uniquemember", "memberuid", "", null}; + for (String groupObject: groupNames) { + overrideConfigValue("ldapGroupUniqueMemberAttribute", groupObject); + String expectedResult = null; + if(groupObject == null) { + expectedResult = "uniquemember"; + } else { + expectedResult = groupObject; + }; + String result = ldapConfiguration.getGroupUniqueMemberAttribute(null); + assertEquals("testing for " + groupObject, expectedResult, result); + } + } + + @Test + public void getSSLStatusCanBeTrue() throws Exception { +// given: "We have a ConfigDao with values for truststore and truststore password set" + overrideConfigValue("ldapTrustStore", "/tmp/ldap.ts"); + overrideConfigValue("ldapTrustStorePassword", "password"); + + assertTrue("A request is made to get the status of SSL should result in true", ldapConfiguration.getSSLStatus()); + } + @Test + public void getSearchGroupPrincipleReturnsSuccessfully() throws Exception { + // We have a ConfigDao with a value for ldap.search.group.principle and an LdapConfiguration + overrideConfigValue("ldapSearchGroupPrinciple", "cn=cloudstack,cn=users,dc=cloudstack,dc=org"); + String result = ldapConfiguration.getSearchGroupPrinciple(null); + + assertEquals("The result holds the same value configDao did", "cn=cloudstack,cn=users,dc=cloudstack,dc=org",result); + } + + @Test + public void getTrustStorePasswordResopnds() throws Exception { + // We have a ConfigDao with a value for truststore password + overrideConfigValue("ldapTrustStorePassword", "password"); + + String result = ldapConfiguration.getTrustStorePassword(); + + assertEquals("The result is password", "password", result); + } + + + @Test + public void getGroupObject() throws Exception { + String [] groupNames = {"bla", "groupOfUniqueNames", "groupOfNames", "", null}; + for (String groupObject: groupNames) { + overrideConfigValue("ldapGroupObject", groupObject); + String expectedResult = null; + if(groupObject == null) { + expectedResult = "groupOfUniqueNames"; + } else { + expectedResult = groupObject; + }; + String result = ldapConfiguration.getGroupObject(null); + assertEquals("testing for " + groupObject, expectedResult, result); + } + } +} \ No newline at end of file diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index a8e24a47806..16be9cee799 100644 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -39,6 +39,10 @@ import com.cloud.template.TemplateManager; import com.cloud.vm.UserVmManager; import com.cloud.vm.snapshot.VMSnapshotManager; +/** + * @deprecated use the more dynamic ConfigKey + */ +@Deprecated public enum Config { // Alert @@ -1814,42 +1818,6 @@ public enum Config { + "If it is set to -1, then it means always use single-part upload to upload object to S3. ", null), - // Ldap - LdapBasedn("Advanced", ManagementServer.class, String.class, "ldap.basedn", null, "Sets the basedn for LDAP", null), - LdapBindPassword("Advanced", ManagementServer.class, String.class, "ldap.bind.password", null, "Sets the bind password for LDAP", null), - LdapBindPrincipal("Advanced", ManagementServer.class, String.class, "ldap.bind.principal", null, "Sets the bind principal for LDAP", null), - LdapEmailAttribute("Advanced", ManagementServer.class, String.class, "ldap.email.attribute", "mail", "Sets the email attribute used within LDAP", null), - LdapFirstnameAttribute( - "Advanced", - ManagementServer.class, - String.class, - "ldap.firstname.attribute", - "givenname", - "Sets the firstname attribute used within LDAP", - null), - LdapLastnameAttribute("Advanced", ManagementServer.class, String.class, "ldap.lastname.attribute", "sn", "Sets the lastname attribute used within LDAP", null), - LdapUsernameAttribute("Advanced", ManagementServer.class, String.class, "ldap.username.attribute", "uid", "Sets the username attribute used within LDAP", null), - LdapUserObject("Advanced", ManagementServer.class, String.class, "ldap.user.object", "inetOrgPerson", "Sets the object type of users within LDAP", null), - LdapSearchGroupPrinciple( - "Advanced", - ManagementServer.class, - String.class, - "ldap.search.group.principle", - null, - "Sets the principle of the group that users must be a member of", - null), - LdapTrustStore("Advanced", ManagementServer.class, String.class, "ldap.truststore", null, "Sets the path to the truststore to use for SSL", null), - LdapTrustStorePassword("Advanced", ManagementServer.class, String.class, "ldap.truststore.password", null, "Sets the password for the truststore", null), - LdapGroupObject("Advanced", ManagementServer.class, String.class, "ldap.group.object", "groupOfUniqueNames", "Sets the object type of groups within LDAP", null), - LdapGroupUniqueMemberAttribute( - "Advanced", - ManagementServer.class, - String.class, - "ldap.group.user.uniquemember", - "uniquemember", - "Sets the attribute for uniquemembers within a group", - null), - // VMSnapshots VMSnapshotMax("Advanced", VMSnapshotManager.class, Integer.class, "vmsnapshot.max", "10", "Maximum vm snapshots for a vm", null), VMSnapshotCreateWait("Advanced", VMSnapshotManager.class, Integer.class, "vmsnapshot.create.wait", "1800", "In second, timeout for create vm snapshot", null), @@ -1979,17 +1947,6 @@ public enum Config { _scope = ConfigKey.Scope.Global.toString(); } - private Config(String category, Class componentClass, Class type, String name, String defaultValue, String description, String range, String scope) { - _category = category; - _componentClass = componentClass; - _type = type; - _name = name; - _defaultValue = defaultValue; - _description = description; - _range = range; - _scope = scope; - } - public String getCategory() { return _category; } @@ -2010,10 +1967,6 @@ public enum Config { return _type; } - public Class getComponentClass() { - return _componentClass; - } - public String getScope() { return _scope; } @@ -2081,8 +2034,4 @@ public enum Config { } return categories; } - - public static List getConfigListByScope(String scope) { - return s_scopeLevelConfigsMap.get(scope); - } } diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index c3e9e11441e..9d9ac52fae5 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -131,8 +131,10 @@ import com.cloud.dc.dao.VlanDao; import com.cloud.deploy.DataCenterDeployment; import com.cloud.deploy.DeploymentClusterPlanner; import com.cloud.domain.Domain; +import com.cloud.domain.DomainDetailVO; import com.cloud.domain.DomainVO; import com.cloud.domain.dao.DomainDao; +import com.cloud.domain.dao.DomainDetailsDao; import com.cloud.event.ActionEvent; import com.cloud.event.EventTypes; import com.cloud.event.UsageEventUtils; @@ -327,6 +329,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati @Inject AccountDetailsDao _accountDetailsDao; @Inject + DomainDetailsDao _domainDetailsDao; + @Inject PrimaryDataStoreDao _storagePoolDao; @Inject NicSecondaryIpDao _nicSecondaryIpDao; @@ -548,6 +552,21 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati _imageStoreDetailsDao.addDetail(resourceId, name, value, true); break; + case Domain: + final DomainVO domain = _domainDao.findById(resourceId); + if (domain == null) { + throw new InvalidParameterValueException("unable to find domain by id " + resourceId); + } + DomainDetailVO domainDetailVO = _domainDetailsDao.findDetail(resourceId, name); + if (domainDetailVO == null) { + domainDetailVO = new DomainDetailVO(resourceId, name, value); + _domainDetailsDao.persist(domainDetailVO); + } else { + domainDetailVO.setValue(value); + _domainDetailsDao.update(domainDetailVO.getId(), domainDetailVO); + } + break; + default: throw new InvalidParameterValueException("Scope provided is invalid"); } @@ -655,6 +674,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati final Long storagepoolId = cmd.getStoragepoolId(); final Long accountId = cmd.getAccountId(); final Long imageStoreId = cmd.getImageStoreId(); + final Long domainId = cmd.getDomainId(); CallContext.current().setEventDetails(" Name: " + name + " New Value: " + (name.toLowerCase().contains("password") ? "*****" : value == null ? "" : value)); // check if config value exists final ConfigurationVO config = _configDao.findByName(name); @@ -700,6 +720,11 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati id = accountId; paramCountCheck++; } + if (domainId != null) { + scope = ConfigKey.Scope.Domain.toString(); + id = domainId; + paramCountCheck++; + } if (storagepoolId != null) { scope = ConfigKey.Scope.StoragePool.toString(); id = storagepoolId; diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index c855c34b60f..82a37529b25 100644 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -1686,6 +1686,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe final Long clusterId = cmd.getClusterId(); final Long storagepoolId = cmd.getStoragepoolId(); final Long accountId = cmd.getAccountId(); + final Long domainId = cmd.getDomainId(); final Long imageStoreId = cmd.getImageStoreId(); String scope = null; Long id = null; @@ -1706,6 +1707,11 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe id = accountId; paramCountCheck++; } + if (domainId != null) { + scope = ConfigKey.Scope.Domain.toString(); + id = domainId; + paramCountCheck++; + } if (storagepoolId != null) { scope = ConfigKey.Scope.StoragePool.toString(); id = storagepoolId; diff --git a/server/src/com/cloud/user/AccountManager.java b/server/src/com/cloud/user/AccountManager.java index e0e7d3bf12d..e708b040ed9 100644 --- a/server/src/com/cloud/user/AccountManager.java +++ b/server/src/com/cloud/user/AccountManager.java @@ -215,4 +215,6 @@ public interface AccountManager extends AccountService, Configurable{ "false", "This parameter allows the users to enable or disable of showing secret key as a part of response for various APIs. By default it is set to false.", true); + + boolean moveUser(long id, Long domainId, long accountId); } diff --git a/server/src/com/cloud/user/AccountManagerImpl.java b/server/src/com/cloud/user/AccountManagerImpl.java index fea8b47a0c5..dc9fdc0a9e5 100644 --- a/server/src/com/cloud/user/AccountManagerImpl.java +++ b/server/src/com/cloud/user/AccountManagerImpl.java @@ -1702,17 +1702,32 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M @ActionEvent(eventType = EventTypes.EVENT_USER_MOVE, eventDescription = "moving User to a new account") public boolean moveUser(MoveUserCmd cmd) { - UserVO user = getValidUserVO(cmd.getId()); + final Long id = cmd.getId(); + UserVO user = getValidUserVO(id); Account oldAccount = _accountDao.findById(user.getAccountId()); checkAccountAndAccess(user, oldAccount); long domainId = oldAccount.getDomainId(); - long newAccountId = getNewAccountId(cmd, domainId); + long newAccountId = getNewAccountId(domainId, cmd.getAccountName(), cmd.getAccountId()); + return moveUser(user, newAccountId); + } + + public boolean moveUser(long id, Long domainId, long accountId) { + UserVO user = getValidUserVO(id); + Account oldAccount = _accountDao.findById(user.getAccountId()); + checkAccountAndAccess(user, oldAccount); + Account newAccount = _accountDao.findById(accountId); + checkIfNotMovingAcrossDomains(domainId, newAccount); + return moveUser(user , accountId); + } + + private boolean moveUser(UserVO user, long newAccountId) { if(newAccountId == user.getAccountId()) { // could do a not silent fail but the objective of the user is reached return true; // no need to create a new user object for this user } + return Transaction.execute(new TransactionCallback() { @Override public Boolean doInTransaction(TransactionStatus status) { @@ -1721,34 +1736,37 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M user.setUuid(UUID.randomUUID().toString()); _userDao.update(user.getId(),user); newUser.setAccountId(newAccountId); - boolean success = _userDao.remove(cmd.getId()); + boolean success = _userDao.remove(user.getId()); UserVO persisted = _userDao.persist(newUser); return success && persisted.getUuid().equals(user.getExternalEntity()); } }); } - private long getNewAccountId(MoveUserCmd cmd, long domainId) { + private long getNewAccountId(long domainId, String accountName, Long accountId) { Account newAccount = null; - if (StringUtils.isNotBlank(cmd.getAccountName())) { + if (StringUtils.isNotBlank(accountName)) { if(s_logger.isDebugEnabled()) { - s_logger.debug("Getting id for account by name '" + cmd.getAccountName() + "' in domain " + domainId); + s_logger.debug("Getting id for account by name '" + accountName + "' in domain " + domainId); } - newAccount = _accountDao.findEnabledAccount(cmd.getAccountName(), domainId); + newAccount = _accountDao.findEnabledAccount(accountName, domainId); } - if (newAccount == null && cmd.getAccountId() != null) { - newAccount = _accountDao.findById(cmd.getAccountId()); + if (newAccount == null && accountId != null) { + newAccount = _accountDao.findById(accountId); } if (newAccount == null) { throw new CloudRuntimeException("no account name or account id. this should have been caught before this point"); } - long newAccountId = newAccount.getAccountId(); + checkIfNotMovingAcrossDomains(domainId, newAccount); + return newAccount.getAccountId(); + } + + private void checkIfNotMovingAcrossDomains(long domainId, Account newAccount) { if(newAccount.getDomainId() != domainId) { // not in scope throw new InvalidParameterValueException("moving a user from an account in one domain to an account in annother domain is not supported!"); } - return newAccountId; } private void checkAccountAndAccess(UserVO user, Account account) { diff --git a/server/test/com/cloud/user/MockAccountManagerImpl.java b/server/test/com/cloud/user/MockAccountManagerImpl.java index 8ea0473aa36..0a92c1446db 100644 --- a/server/test/com/cloud/user/MockAccountManagerImpl.java +++ b/server/test/com/cloud/user/MockAccountManagerImpl.java @@ -123,7 +123,13 @@ public class MockAccountManagerImpl extends ManagerBase implements Manager, Acco return false; } - @Override public boolean moveUser(MoveUserCmd moveUserCmd) { + @Override + public boolean moveUser(MoveUserCmd moveUserCmd) { + return false; + } + + @Override + public boolean moveUser(long id, Long domainId, long accountId) { return false; } diff --git a/server/test/org/apache/cloudstack/networkoffering/ChildTestConfiguration.java b/server/test/org/apache/cloudstack/networkoffering/ChildTestConfiguration.java index 92d421c0efb..3ffec4cd020 100644 --- a/server/test/org/apache/cloudstack/networkoffering/ChildTestConfiguration.java +++ b/server/test/org/apache/cloudstack/networkoffering/ChildTestConfiguration.java @@ -19,34 +19,6 @@ package org.apache.cloudstack.networkoffering; import java.io.IOException; -import com.cloud.network.dao.FirewallRulesDcidrsDaoImpl; -import com.cloud.storage.StorageManager; -import org.mockito.Mockito; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.ComponentScan.Filter; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.FilterType; -import org.springframework.core.type.classreading.MetadataReader; -import org.springframework.core.type.classreading.MetadataReaderFactory; -import org.springframework.core.type.filter.TypeFilter; - -import org.apache.cloudstack.acl.SecurityChecker; -import org.apache.cloudstack.affinity.AffinityGroupService; -import org.apache.cloudstack.affinity.dao.AffinityGroupDao; -import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; -import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; -import org.apache.cloudstack.framework.config.ConfigDepot; -import org.apache.cloudstack.framework.config.dao.ConfigurationDao; -import org.apache.cloudstack.region.PortableIpDaoImpl; -import org.apache.cloudstack.region.PortableIpRangeDaoImpl; -import org.apache.cloudstack.region.dao.RegionDaoImpl; -import org.apache.cloudstack.storage.datastore.db.ImageStoreDaoImpl; -import org.apache.cloudstack.storage.datastore.db.ImageStoreDetailsDaoImpl; -import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDaoImpl; -import org.apache.cloudstack.test.utils.SpringUtils; - import com.cloud.agent.AgentManager; import com.cloud.alert.AlertManager; import com.cloud.api.query.dao.UserAccountJoinDaoImpl; @@ -67,6 +39,7 @@ import com.cloud.dc.dao.PodVlanDaoImpl; import com.cloud.dc.dao.PodVlanMapDaoImpl; import com.cloud.dc.dao.VlanDaoImpl; import com.cloud.domain.dao.DomainDaoImpl; +import com.cloud.domain.dao.DomainDetailsDao; import com.cloud.event.dao.UsageEventDaoImpl; import com.cloud.host.dao.HostDaoImpl; import com.cloud.host.dao.HostDetailsDaoImpl; @@ -79,6 +52,7 @@ import com.cloud.network.StorageNetworkManager; import com.cloud.network.dao.AccountGuestVlanMapDaoImpl; import com.cloud.network.dao.FirewallRulesCidrsDaoImpl; import com.cloud.network.dao.FirewallRulesDaoImpl; +import com.cloud.network.dao.FirewallRulesDcidrsDaoImpl; import com.cloud.network.dao.IPAddressDaoImpl; import com.cloud.network.dao.LoadBalancerDaoImpl; import com.cloud.network.dao.NetworkDao; @@ -107,6 +81,7 @@ import com.cloud.server.ConfigurationServer; import com.cloud.server.ManagementService; import com.cloud.service.dao.ServiceOfferingDaoImpl; import com.cloud.service.dao.ServiceOfferingDetailsDaoImpl; +import com.cloud.storage.StorageManager; import com.cloud.storage.dao.DiskOfferingDaoImpl; import com.cloud.storage.dao.SnapshotDaoImpl; import com.cloud.storage.dao.StoragePoolDetailsDaoImpl; @@ -123,6 +98,30 @@ import com.cloud.vm.dao.NicDaoImpl; import com.cloud.vm.dao.NicSecondaryIpDaoImpl; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDaoImpl; +import org.apache.cloudstack.acl.SecurityChecker; +import org.apache.cloudstack.affinity.AffinityGroupService; +import org.apache.cloudstack.affinity.dao.AffinityGroupDao; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; +import org.apache.cloudstack.framework.config.ConfigDepot; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.region.PortableIpDaoImpl; +import org.apache.cloudstack.region.PortableIpRangeDaoImpl; +import org.apache.cloudstack.region.dao.RegionDaoImpl; +import org.apache.cloudstack.storage.datastore.db.ImageStoreDaoImpl; +import org.apache.cloudstack.storage.datastore.db.ImageStoreDetailsDaoImpl; +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDaoImpl; +import org.apache.cloudstack.test.utils.SpringUtils; +import org.mockito.Mockito; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.ComponentScan.Filter; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.FilterType; +import org.springframework.core.type.classreading.MetadataReader; +import org.springframework.core.type.classreading.MetadataReaderFactory; +import org.springframework.core.type.filter.TypeFilter; @Configuration @ComponentScan(basePackageClasses = {AccountVlanMapDaoImpl.class, DomainVlanMapDaoImpl.class, VolumeDaoImpl.class, HostPodDaoImpl.class, DomainDaoImpl.class, ServiceOfferingDaoImpl.class, @@ -320,6 +319,11 @@ public class return Mockito.mock(AccountDetailsDao.class); } + @Bean + public DomainDetailsDao domainDetailsDao() { + return Mockito.mock(DomainDetailsDao.class); + } + @Bean public DataStoreManager dataStoreManager() { return Mockito.mock(DataStoreManager.class); diff --git a/ui/scripts/domains.js b/ui/scripts/domains.js index 704195e08a9..1f65ff4bdae 100644 --- a/ui/scripts/domains.js +++ b/ui/scripts/domains.js @@ -457,6 +457,15 @@ } } }, + + tabFilter: function(args) { + var hiddenTabs = []; + if(!isAdmin()) { + hiddenTabs.push('settings'); + } + return hiddenTabs; + }, + tabs: { details: { title: 'label.details', @@ -638,36 +647,6 @@ domainObj["vmTotal"] = totalVMs; domainObj["volumeTotal"] = totalVolumes; - /* $.ajax({ - url: createURL("listVirtualMachines&details=min&domainid=" + domainObj.id), - async: false, - dataType: "json", - success: function(json) { - var items = json.listvirtualmachinesresponse.virtualmachine; - var total; - if (items != null) - total = items.length; - else - total = 0; - domainObj["vmTotal"] = total; - } - }); - - $.ajax({ - url: createURL("listVolumes&domainid=" + domainObj.id), - async: false, - dataType: "json", - success: function(json) { - var items = json.listvolumesresponse.volume; - var total; - if (items != null) - total = items.length; - else - total = 0; - domainObj["volumeTotal"] = total; - } - });*/ - $.ajax({ url: createURL("listResourceLimits&domainid=" + domainObj.id), async: false, @@ -722,7 +701,58 @@ actionFilter: domainActionfilter }); } + }, + // Granular settings for domains + settings: { + title: 'label.settings', + custom: cloudStack.uiCustom.granularSettings({ + dataProvider: function(args) { + $.ajax({ + url: createURL('listConfigurations&domainid=' + args.context.domains[0].id), + data: listViewDataProvider(args, {}, { searchBy: 'name' }), + success: function(json) { + args.response.success({ + data: json.listconfigurationsresponse.configuration + }); + + }, + + error: function(json) { + args.response.error(parseXMLHttpResponse(json)); + + } + }); + + }, + actions: { + edit: function(args) { + // call updateDomainLevelParameters + var data = { + name: args.data.jsonObj.name, + value: args.data.value + }; + + $.ajax({ + url: createURL('updateConfiguration&domainid=' + args.context.domains[0].id), + data: data, + success: function(json) { + var item = json.updateconfigurationresponse.configuration; + args.response.success({ + data: item + }); + }, + + error: function(json) { + args.response.error(parseXMLHttpResponse(json)); + } + + }); + + } + } + }) } + } }, labelField: 'name',