diff --git a/api/pom.xml b/api/pom.xml
index dfb35728d5f..013fdfafbd0 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -56,6 +56,11 @@
cloud-framework-ca
${project.version}
+
+ org.apache.commons
+ commons-lang3
+ ${cs.commons-lang3.version}
+
diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java
index 4ecdbf42861..d9127303b67 100755
--- a/api/src/com/cloud/event/EventTypes.java
+++ b/api/src/com/cloud/event/EventTypes.java
@@ -197,6 +197,7 @@ public class EventTypes {
public static final String EVENT_USER_CREATE = "USER.CREATE";
public static final String EVENT_USER_DELETE = "USER.DELETE";
public static final String EVENT_USER_DISABLE = "USER.DISABLE";
+ public static final String EVENT_USER_MOVE = "USER.MOVE";
public static final String EVENT_USER_UPDATE = "USER.UPDATE";
public static final String EVENT_USER_ENABLE = "USER.ENABLE";
public static final String EVENT_USER_LOCK = "USER.LOCK";
diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java
index 4f0e59b2eae..6e822f75178 100755
--- a/api/src/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/org/apache/cloudstack/api/ApiConstants.java
@@ -646,6 +646,8 @@ public class ApiConstants {
public static final String UTILIZATION = "utilization";
public static final String HAS_ANNOTATION = "hasannotation";
public static final String LAST_ANNOTATED = "lastannotated";
+ public static final String ADMIN = "admin";
+ public static final String LDAP_DOMAIN = "ldapdomain";
public enum HostDetails {
all, capacity, events, stats, min;
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 a34bc3eb622..ccfc432870b 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;
@@ -76,6 +77,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;
+
// ///////////////////////////////////////////////////
// ///////////////// Accessors ///////////////////////
// ///////////////////////////////////////////////////
@@ -104,6 +111,10 @@ public class ListCfgsByCmd extends BaseListCmd {
return accountId;
}
+ public Long getDomainId() {
+ return domainId;
+ }
+
@Override
public Long getPageSizeVal() {
Long defaultPageSize = 500L;
@@ -147,6 +158,9 @@ public class ListCfgsByCmd extends BaseListCmd {
if (getAccountId() != null) {
cfgResponse.setScope("account");
}
+ if (getDomainId() != null) {
+ cfgResponse.setScope("domain");
+ }
configResponses.add(cfgResponse);
}
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 45f790fb70b..b4e35d0f59d 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;
@@ -75,6 +76,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;
+
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@@ -107,6 +114,10 @@ public class UpdateCfgCmd extends BaseCmd {
return accountId;
}
+ public Long getDomainId() {
+ return domainId;
+ }
+
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@@ -145,6 +156,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/api/src/org/apache/cloudstack/api/command/admin/user/MoveUserCmd.java b/api/src/org/apache/cloudstack/api/command/admin/user/MoveUserCmd.java
new file mode 100644
index 00000000000..b32aa2f1326
--- /dev/null
+++ b/api/src/org/apache/cloudstack/api/command/admin/user/MoveUserCmd.java
@@ -0,0 +1,126 @@
+// 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.admin.user;
+
+import com.cloud.user.Account;
+import com.cloud.user.User;
+import com.google.common.base.Preconditions;
+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.AccountResponse;
+import org.apache.cloudstack.api.response.SuccessResponse;
+import org.apache.cloudstack.api.response.UserResponse;
+import org.apache.cloudstack.context.CallContext;
+import org.apache.cloudstack.region.RegionService;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.log4j.Logger;
+
+import javax.inject.Inject;
+
+@APICommand(name = "moveUser",
+ description = "Moves a user to another account",
+ responseObject = SuccessResponse.class,
+ requestHasSensitiveInfo = false,
+ responseHasSensitiveInfo = false,
+ since = "4.11",
+ authorized = {RoleType.Admin})
+public class MoveUserCmd extends BaseCmd {
+ public static final Logger s_logger = Logger.getLogger(UpdateUserCmd.class.getName());
+
+ public static final String APINAME = "moveUser";
+
+ /////////////////////////////////////////////////////
+ //////////////// API parameters /////////////////////
+ /////////////////////////////////////////////////////
+ @Parameter(name = ApiConstants.ID,
+ type = CommandType.UUID,
+ entityType = UserResponse.class,
+ required = true,
+ description = "id of the user to be deleted")
+ private Long id;
+
+ @Parameter(name = ApiConstants.ACCOUNT,
+ type = CommandType.STRING,
+ description = "Creates the user under the specified account. If no account is specified, the username will be used as the account name.")
+ private String accountName;
+
+ @Parameter(name = ApiConstants.ACCOUNT_ID,
+ type = CommandType.UUID,
+ entityType = AccountResponse.class,
+ description = "Creates the user under the specified domain. Has to be accompanied with the account parameter")
+ private Long accountId;
+
+ @Inject
+ RegionService _regionService;
+
+ /////////////////////////////////////////////////////
+ /////////////////// Accessors ///////////////////////
+ /////////////////////////////////////////////////////
+
+ public Long getId() {
+ return id;
+ }
+
+ public String getAccountName() {
+ return accountName;
+ }
+
+ public Long getAccountId() {
+ return accountId;
+ }
+
+ /////////////////////////////////////////////////////
+ /////////////// API Implementation///////////////////
+ /////////////////////////////////////////////////////
+
+ @Override
+ public String getCommandName() {
+ return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
+ }
+
+ @Override
+ public long getEntityOwnerId() {
+ User user = _entityMgr.findById(User.class, getId());
+ if (user != null) {
+ return user.getAccountId();
+ }
+
+ return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked
+ }
+
+ @Override
+ public void execute() {
+ Preconditions.checkNotNull(getId(),"I have to have an user to move!");
+ Preconditions.checkState(ObjectUtils.anyNotNull(getAccountId(),getAccountName()),"provide either an account name or an account id!");
+
+ CallContext.current().setEventDetails("UserId: " + getId());
+ boolean result =
+ _regionService.moveUser(this);
+ if (result) {
+ SuccessResponse response = new SuccessResponse(getCommandName());
+ this.setResponseObject(response);
+ } else {
+ throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to move the user to a new account");
+ }
+ }
+
+}
diff --git a/api/src/org/apache/cloudstack/region/RegionService.java b/api/src/org/apache/cloudstack/region/RegionService.java
index afefcc7672e..bee66910b4d 100644
--- a/api/src/org/apache/cloudstack/region/RegionService.java
+++ b/api/src/org/apache/cloudstack/region/RegionService.java
@@ -27,6 +27,7 @@ import org.apache.cloudstack.api.command.admin.domain.UpdateDomainCmd;
import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd;
import org.apache.cloudstack.api.command.admin.user.DisableUserCmd;
import org.apache.cloudstack.api.command.admin.user.EnableUserCmd;
+import org.apache.cloudstack.api.command.admin.user.MoveUserCmd;
import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd;
import org.apache.cloudstack.api.command.user.region.ListRegionsCmd;
@@ -110,10 +111,17 @@ public interface RegionService {
*/
boolean deleteUser(DeleteUserCmd deleteUserCmd);
+ /**
+ * Deletes user by Id
+ * @param moveUserCmd
+ * @return true if delete was successful, false otherwise
+ */
+ boolean moveUser(MoveUserCmd moveUserCmd);
+
/**
* update an existing domain
*
- * @param cmd
+ * @param updateDomainCmd
* - the command containing domainId and new domainName
* @return Domain object if the command succeeded
*/
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 066d93ea03f..5f0802e4d73 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
@@ -146,6 +146,7 @@
+
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/engine/schema/src/com/cloud/user/UserVO.java b/engine/schema/src/com/cloud/user/UserVO.java
index da7811ecc5b..9b744617fcd 100644
--- a/engine/schema/src/com/cloud/user/UserVO.java
+++ b/engine/schema/src/com/cloud/user/UserVO.java
@@ -126,7 +126,25 @@ public class UserVO implements User, Identity, InternalIdentity {
this.source = source;
}
- @Override
+ public UserVO(UserVO user) {
+ this.setAccountId(user.getAccountId());
+ this.setUsername(user.getUsername());
+ this.setPassword(user.getPassword());
+ this.setFirstname(user.getFirstname());
+ this.setLastname(user.getLastname());
+ this.setEmail(user.getEmail());
+ this.setTimezone(user.getTimezone());
+ this.setUuid(user.getUuid());
+ this.setSource(user.getSource());
+ this.setApiKey(user.getApiKey());
+ this.setSecretKey(user.getSecretKey());
+ this.setExternalEntity(user.getExternalEntity());
+ this.setRegistered(user.isRegistered());
+ this.setRegistrationToken(user.getRegistrationToken());
+ this.setState(user.getState());
+ }
+
+ @Override
public long getId() {
return id;
}
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 d39bb5d3562..2f63a2837fc 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
+ Global, Zone, Cluster, StoragePool, Account, ManagementServer, Domain
}
private final String _category;
@@ -133,7 +133,8 @@ public class ConfigKey {
public T value() {
if (_value == null || isDynamic()) {
ConfigurationVO vo = s_depot != null ? s_depot.global().findById(key()) : null;
- _value = valueOf((vo != null && vo.getValue() != null) ? vo.getValue() : defaultValue());
+ final String value = (vo != null && vo.getValue() != null) ? vo.getValue() : defaultValue();
+ _value = ((value == null) ? (T)defaultValue() : valueOf(value));
}
return _value;
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 4631bb9d0e8..07514f61f4a 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
@@ -84,6 +84,7 @@ public class ConfigDepotImpl implements ConfigDepot, ConfigDepotAdmin {
_scopeLevelConfigsMap.put(ConfigKey.Scope.Cluster, new HashSet>());
_scopeLevelConfigsMap.put(ConfigKey.Scope.StoragePool, new HashSet>());
_scopeLevelConfigsMap.put(ConfigKey.Scope.Account, 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 954a37e1fe6..3e6677d0b0a 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 f9f0d2e2c9f..857e6266c8b 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
@@ -25,6 +25,7 @@ import javax.naming.ConfigurationException;
import org.apache.log4j.Logger;
+import org.apache.cloudstack.api.command.admin.user.MoveUserCmd;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
@@ -311,6 +312,16 @@ public class MockAccountManager extends ManagerBase implements AccountManager {
return false;
}
+ @Override
+ public boolean moveUser(MoveUserCmd moveUserCmd) {
+ return false;
+ }
+
+ @Override
+ public boolean moveUser(long id, Long domainId, long accountId) {
+ return false;
+ }
+
@Override
public boolean deleteUserAccount(long arg0) {
// TODO Auto-generated method stub
diff --git a/plugins/user-authenticators/ldap/pom.xml b/plugins/user-authenticators/ldap/pom.xml
index ec2813e00da..41d5eebb711 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/resources/META-INF/cloudstack/ldap/spring-ldap-context.xml b/plugins/user-authenticators/ldap/resources/META-INF/cloudstack/ldap/spring-ldap-context.xml
index 34a2befe971..07d6b381328 100644
--- a/plugins/user-authenticators/ldap/resources/META-INF/cloudstack/ldap/spring-ldap-context.xml
+++ b/plugins/user-authenticators/ldap/resources/META-INF/cloudstack/ldap/spring-ldap-context.xml
@@ -30,10 +30,11 @@
-
+
+
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 4da04166b1b..272a8a22f09 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
@@ -138,7 +138,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 c38dd7031fe..de8bafeeae6 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
@@ -141,9 +141,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
new file mode 100644
index 00000000000..00140952051
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LinkDomainToLdapCmd.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.api.command;
+
+import javax.inject.Inject;
+
+import com.cloud.exception.InvalidParameterValueException;
+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.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 com.cloud.user.Account;
+
+import java.util.UUID;
+
+@APICommand(name = "linkDomainToLdap", description = "link an existing cloudstack domain to group or OU in ldap", responseObject = LinkDomainToLdapResponse.class, since = "4.6.0",
+ requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
+public class LinkDomainToLdapCmd extends BaseCmd {
+ public static final Logger s_logger = Logger.getLogger(LinkDomainToLdapCmd.class.getName());
+ private static final String s_name = "linkdomaintoldapresponse";
+
+ @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, required = true, entityType = DomainResponse.class, description = "The id of the domain which has to be "
+ + "linked to LDAP.")
+ private Long domainId;
+
+ @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;
+
+ @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;
+
+ 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(this);
+ if(admin!=null) {
+ LdapUser ldapUser = null;
+ try {
+ 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);
+ }
+ 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()));
+ s_logger.info("created an account with name " + admin + " in the given domain " + domainId);
+ } catch (Exception e) {
+ s_logger.info("an exception occurred while creating account with name " + admin +" in domain " + domainId, e);
+ }
+ } else {
+ s_logger.debug("an account with name " + admin + " already exists in the domain " + domainId);
+ }
+ } else {
+ s_logger.debug("ldap user with username "+admin+" is disabled in the given group/ou");
+ }
+ }
+ response.setObjectName("LinkDomainToLdap");
+ response.setResponseName(getCommandName());
+ setResponseObject(response);
+ } catch (final InvalidParameterValueException e) {
+ throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.toString());
+ }
+ }
+
+ @Override
+ public String getCommandName() {
+ return s_name;
+ }
+
+ @Override
+ public long getEntityOwnerId() {
+ return Account.ACCOUNT_ID_SYSTEM;
+ }
+}
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
new file mode 100644
index 00000000000..d6d4b55e257
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LinkDomainToLdapResponse.java
@@ -0,0 +1,84 @@
+/*
+ * 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 LinkDomainToLdapResponse extends BaseResponse {
+
+ @SerializedName(ApiConstants.DOMAIN_ID)
+ @Param(description = "id of the Domain which is linked to LDAP")
+ private String domainId;
+
+ @Deprecated
+ @SerializedName(ApiConstants.NAME)
+ @Param(description = "name of the group or OU in LDAP which is linked to the domain")
+ private String name;
+
+ @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;
+
+ public LinkDomainToLdapResponse(String domainId, String type, String ldapDomain, short accountType) {
+ this.domainId = domainId;
+ this.name = ldapDomain;
+ this.ldapDomain = ldapDomain;
+ this.type = type;
+ this.accountType = accountType;
+ }
+
+ public String getDomainId() {
+ return domainId;
+ }
+
+ public String getLdapDomain() {
+ return ldapDomain == null ? name : 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/ldap/ADLdapUserManagerImpl.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/ADLdapUserManagerImpl.java
new file mode 100644
index 00000000000..e844df57c1c
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/ADLdapUserManagerImpl.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 org.apache.cloudstack.ldap;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Logger;
+
+public class ADLdapUserManagerImpl extends OpenLdapUserManagerImpl implements LdapUserManager {
+ public static final Logger s_logger = Logger.getLogger(ADLdapUserManagerImpl.class.getName());
+ private static final String MICROSOFT_AD_NESTED_MEMBERS_FILTER = "memberOf:1.2.840.113556.1.4.1941:";
+ private static final String MICROSOFT_AD_MEMBERS_FILTER = "memberOf";
+
+ @Override
+ 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(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(domainId));
+
+ 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, domainId));
+ }
+ return users;
+ }
+
+ private String generateADGroupSearchFilter(String groupName, Long domainId) {
+ final StringBuilder userObjectFilter = new StringBuilder();
+ userObjectFilter.append("(objectClass=");
+ userObjectFilter.append(_ldapConfiguration.getUserObject(domainId));
+ userObjectFilter.append(")");
+
+ final StringBuilder memberOfFilter = new StringBuilder();
+ String groupCnName = _ldapConfiguration.getCommonNameAttribute() + "=" +groupName + "," + _ldapConfiguration.getBaseDn(domainId);
+ memberOfFilter.append("(").append(getMemberOfAttribute(domainId)).append("=");
+ memberOfFilter.append(groupCnName);
+ memberOfFilter.append(")");
+
+ final StringBuilder result = new StringBuilder();
+ result.append("(&");
+ result.append(userObjectFilter);
+ result.append(memberOfFilter);
+ result.append(")");
+
+ s_logger.debug("group search filter = " + result);
+ return result.toString();
+ }
+
+ protected boolean isUserDisabled(SearchResult result) throws NamingException {
+ boolean isDisabledUser = false;
+ String userAccountControl = LdapUtils.getAttributeValue(result.getAttributes(), _ldapConfiguration.getUserAccountControlAttribute());
+ if (userAccountControl != null) {
+ int control = Integer.parseInt(userAccountControl);
+ // second bit represents disabled user flag in AD
+ if ((control & 2) > 0) {
+ isDisabledUser = true;
+ }
+ }
+ return isDisabledUser;
+ }
+
+ 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 8c6820f8458..d3452314cf5 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,23 +16,35 @@
// under the License.
package org.apache.cloudstack.ldap;
-import com.cloud.server.auth.DefaultUserAuthenticator;
-import com.cloud.user.UserAccount;
-import com.cloud.user.dao.UserAccountDao;
-import com.cloud.utils.Pair;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.acl.RoleType;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
-import javax.inject.Inject;
-import java.util.Map;
+import com.cloud.server.auth.UserAuthenticator;
+import com.cloud.user.Account;
+import com.cloud.user.AccountManager;
+import com.cloud.user.User;
+import com.cloud.user.UserAccount;
+import com.cloud.user.dao.UserAccountDao;
+import com.cloud.utils.Pair;
+import com.cloud.utils.component.AdapterBase;
-public class LdapAuthenticator extends DefaultUserAuthenticator {
+public class LdapAuthenticator extends AdapterBase implements UserAuthenticator {
private static final Logger s_logger = Logger.getLogger(LdapAuthenticator.class.getName());
@Inject
private LdapManager _ldapManager;
@Inject
private UserAccountDao _userAccountDao;
+ @Inject
+ private AccountManager _accountManager;
public LdapAuthenticator() {
super();
@@ -46,27 +58,214 @@ public class LdapAuthenticator extends DefaultUserAuthenticator {
@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;
}
- final UserAccount user = _userAccountDao.getUserAccount(username, domainId);
-
- if (user == null) {
- s_logger.debug("Unable to find user with " + username + " in domain " + domainId);
- return new Pair(false, null);
- } else if (_ldapManager.isLdapEnabled()) {
- boolean result = _ldapManager.canAuthenticate(username, password);
- ActionOnFailedAuthentication action = null;
- if (result == false) {
- action = ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT;
+ if (_ldapManager.isLdapEnabled()) {
+ final UserAccount user = _userAccountDao.getUserAccount(username, domainId);
+ 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
+ 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) {
+ if(s_logger.isDebugEnabled()) {
+ s_logger.debug("trying to log on '" + username + "' to ldap in linked account");
+ }
+ 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 {
- return new Pair(false, ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT);
+ 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) {
+ if(s_logger.isDebugEnabled()) {
+ s_logger.debug("trying to log on '" + username + "' to ldap in linked domain");
+ }
+ 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) {
+ if(s_logger.isDebugEnabled()) {
+ s_logger.debug("trying to log on '" + username + "' as linked ldap 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) {
+ if(user != null && (user.getState().equalsIgnoreCase(Account.State.disabled.toString()))) {
+ _accountManager.enableUser(user.getId());
+ }
+ }
+
+ private void createCloudStackUserAccount(LdapUser user, long domainId, short accountType) {
+ String username = user.getUsername();
+ _accountManager.createUserAccount(username, "", user.getFirstname(), user.getLastname(), user.getEmail(), null, username,
+ accountType, RoleType.getByAccountType(accountType).getId(), domainId, null, null,
+ UUID.randomUUID().toString(), UUID.randomUUID().toString(), User.Source.LDAP);
+ }
+
+ private void disableUserInCloudStack(UserAccount user) {
+ if (user != null) {
+ _accountManager.disableUser(user.getId());
+ }
+ }
+
+ private void removeUserInCloudStack(UserAccount user) {
+ if (user != null) {
+ _accountManager.disableUser(user.getId());
}
}
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 c171ebfcc0b..b87478056bc 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
@@ -21,80 +21,224 @@ import java.util.List;
import javax.inject.Inject;
import javax.naming.directory.SearchControls;
-import org.apache.cloudstack.api.command.LdapListConfigurationCmd;
+import com.cloud.utils.Pair;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.Configurable;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
-
-import com.cloud.utils.Pair;
+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 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(
+ "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 LdapManager _ldapManager;
+ private LdapConfigurationDao _ldapConfigurationDao;
public LdapConfiguration() {
}
- public LdapConfiguration(final ConfigurationDao configDao, final LdapManager ldapManager) {
- _configDao = configDao;
- _ldapManager = ldapManager;
+ 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 = _ldapManager.listConfigurations(new LdapListConfigurationCmd(_ldapManager));
+ final Pair, Integer> result = _ldapConfigurationDao.searchConfigurations(null, 0, domainId);
final StringBuilder providerUrls = new StringBuilder();
String delim = "";
for (final LdapConfigurationVO resource : result.first()) {
@@ -105,16 +249,24 @@ public class LdapConfiguration implements Configurable{
return providerUrls.toString();
}
- public String[] getReturnAttributes() {
- return new String[] {getUsernameAttribute(), getEmailAttribute(), getFirstnameAttribute(), getLastnameAttribute(), getCommonNameAttribute()};
+ 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() {
@@ -126,43 +278,64 @@ 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";
}
- public Long getReadTimeout() {
- return ldapReadTimeout.value();
+ // TODO remove hard-coding
+ public String getUserAccountControlAttribute() {
+ return "userAccountControl";
}
- public Integer getLdapPageSize() {
- return ldapPageSize.value();
+ public Long getReadTimeout(final Long domainId) {
+ return ldapReadTimeout.valueIn(domainId);
+ }
+
+ public Integer getLdapPageSize(final Long domainId) {
+ return ldapPageSize.valueIn(domainId);
+ }
+
+ public LdapUserManager.Provider getLdapProvider(final Long domainId) {
+ LdapUserManager.Provider provider;
+ try {
+ provider = LdapUserManager.Provider.valueOf(ldapProvider.valueIn(domainId).toUpperCase());
+ } catch (IllegalArgumentException ex) {
+ //openldap is the default
+ provider = LdapUserManager.Provider.OPENLDAP;
+ }
+ return provider;
+ }
+
+ public boolean isNestedGroupsEnabled(final Long domainId) {
+ return ldapEnableNestedGroups.valueIn(domainId);
+ }
+
+ public static String getUserMemberOfAttribute(final Long domainId) {
+ return ldapMemberOfAttribute.valueIn(domainId);
}
@Override
@@ -172,6 +345,25 @@ public class LdapConfiguration implements Configurable{
@Override
public ConfigKey>[] getConfigKeys() {
- return new ConfigKey>[] {ldapReadTimeout, ldapPageSize};
+ 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 31205c457ad..a49777d43ed 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,7 +18,11 @@ 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;
@@ -26,27 +30,53 @@ 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 {
- LdapConfigurationResponse addConfiguration(String hostname, int port) throws InvalidParameterValueException;
+ enum LinkType { GROUP, OU;}
- boolean canAuthenticate(String username, String password);
+ LdapConfigurationResponse addConfiguration(final LdapAddConfigurationCmd cmd) throws InvalidParameterValueException;
+
+ @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;
- List getUsers() 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 getUsersInGroup(String groupName) throws NoLdapUserMatchingQueryException;
+ LdapUser getUser(String username, String type, String name, Long domainId) throws NoLdapUserMatchingQueryException;
+
+ List getUsers(Long domainId) throws NoLdapUserMatchingQueryException;
+
+ List getUsersInGroup(String groupName, Long domainId) throws NoLdapUserMatchingQueryException;
boolean isLdapEnabled();
Pair, Integer> listConfigurations(LdapListConfigurationCmd cmd);
List searchUsers(String query) throws NoLdapUserMatchingQueryException;
+
+ LinkDomainToLdapResponse linkDomainToLdap(LinkDomainToLdapCmd cmd);
+
+ 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 87c0443a3c4..5e789f03b90 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
@@ -19,15 +19,19 @@ package org.apache.cloudstack.ldap;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
+import java.util.UUID;
-import javax.ejb.Local;
import javax.inject.Inject;
import javax.naming.NamingException;
import javax.naming.ldap.LdapContext;
-import org.apache.log4j.Logger;
-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;
import org.apache.cloudstack.api.LdapValidator;
import org.apache.cloudstack.api.command.LDAPConfigCmd;
import org.apache.cloudstack.api.command.LDAPRemoveCmd;
@@ -38,70 +42,111 @@ 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 com.cloud.exception.InvalidParameterValueException;
-import com.cloud.utils.Pair;
+import org.apache.cloudstack.ldap.dao.LdapTrustMapDao;
+import org.apache.commons.lang.Validate;
+import org.apache.log4j.Logger;
+import org.springframework.stereotype.Component;
@Component
-@Local(value = LdapManager.class)
public class LdapManagerImpl implements LdapManager, LdapValidator {
private static final Logger s_logger = Logger.getLogger(LdapManagerImpl.class.getName());
@Inject
private LdapConfigurationDao _ldapConfigurationDao;
+ @Inject
+ private DomainDao domainDao;
+
+ @Inject
+ private AccountDao accountDao;
+
@Inject
private LdapContextFactory _ldapContextFactory;
@Inject
- private LdapUserManager _ldapUserManager;
+ private LdapConfiguration _ldapConfiguration;
+
+ @Inject LdapUserManagerFactory _ldapUserManagerFactory;
+
+ @Inject
+ LdapTrustMapDao _ldapTrustMapDao;
+
public LdapManagerImpl() {
super();
}
- public LdapManagerImpl(final LdapConfigurationDao ldapConfigurationDao, final LdapContextFactory ldapContextFactory, final LdapUserManager ldapUserManager) {
+ public LdapManagerImpl(final LdapConfigurationDao ldapConfigurationDao, final LdapContextFactory ldapContextFactory, final LdapUserManagerFactory ldapUserManagerFactory,
+ final LdapConfiguration ldapConfiguration) {
super();
_ldapConfigurationDao = ldapConfigurationDao;
_ldapContextFactory = ldapContextFactory;
- _ldapUserManager = ldapUserManager;
+ _ldapUserManagerFactory = ldapUserManagerFactory;
+ _ldapConfiguration = ldapConfiguration;
}
@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;
- _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");
+ } finally {
+ closeContext(context);
}
} else {
throw new InvalidParameterValueException("Duplicate configuration");
}
}
+ /**
+ * 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 username, final String password) {
- final String escapedUsername = LdapUtils.escapeLDAPSearchFilter(username);
+ public boolean canAuthenticate(final String principal, final String password, final Long domainId) {
try {
- final LdapUser user = getUser(escapedUsername);
- final String principal = user.getPrincipal();
- 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 | NoLdapUserMatchingQueryException e) {
- s_logger.debug("Exception while doing an LDAP bind for user "+" "+username, e);
- s_logger.info("Failed to authenticate user: " + username + ". incorrect password.");
+ } catch (NamingException | IOException e) {
+ s_logger.debug("Exception while doing an LDAP bind for user "+" "+principal, e);
+ s_logger.info("Failed to authenticate user: " + principal + ". incorrect password.");
return false;
}
}
@@ -112,16 +157,17 @@ public class LdapManagerImpl implements LdapManager, LdapValidator {
context.close();
}
} catch (final NamingException e) {
- s_logger.warn(e.getMessage(),e);
+ s_logger.warn(e.getMessage(), e);
}
}
@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
@@ -137,14 +183,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);
}
}
@@ -160,17 +215,19 @@ public class LdapManagerImpl implements LdapManager, LdapValidator {
cmdList.add(LdapImportUsersCmd.class);
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 _ldapUserManager.getUser(escapedUsername, context);
+ return _ldapUserManagerFactory.getInstance(_ldapConfiguration.getLdapProvider(null)).getUser(escapedUsername, context, domainId);
} catch (NamingException | IOException e) {
s_logger.debug("ldap Exception: ",e);
@@ -181,11 +238,26 @@ public class LdapManagerImpl implements LdapManager, LdapValidator {
}
@Override
- public List getUsers() 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();
- return _ldapUserManager.getUsers(context);
+ context = _ldapContextFactory.createBindContext(domainId);
+ final String escapedUsername = LdapUtils.escapeLDAPSearchFilter(username);
+ 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 + " in group: " + name + " of type: " + type);
+ } finally {
+ closeContext(context);
+ }
+ }
+
+ @Override
+ public List getUsers(Long domainId) throws NoLdapUserMatchingQueryException {
+ LdapContext context = null;
+ try {
+ 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("*");
@@ -195,11 +267,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 _ldapUserManager.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);
@@ -217,7 +289,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());
}
@@ -225,9 +298,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 _ldapUserManager.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);
@@ -235,4 +309,77 @@ public class LdapManagerImpl implements LdapManager, LdapValidator {
closeContext(context);
}
}
+
+ @Override
+ 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, 0));
+ 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();
+ }
+ LinkDomainToLdapResponse response = new LinkDomainToLdapResponse(domainUuid, vo.getType().toString(), vo.getName(), vo.getAccountType());
+ return response;
+ }
+
+ @Override
+ 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
new file mode 100644
index 00000000000..c402747b813
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapTrustMapVO.java
@@ -0,0 +1,127 @@
+/*
+ * 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 javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+import org.apache.cloudstack.api.InternalIdentity;
+
+@Entity
+@Table(name = "ldap_trust_map")
+public class LdapTrustMapVO implements InternalIdentity {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(name = "id")
+ private Long id;
+
+ @Column(name = "type")
+ private LdapManager.LinkType type;
+
+ @Column(name = "name")
+ private String name;
+
+ @Column(name = "domain_id")
+ private long domainId;
+
+ @Column(name = "account_id")
+ private long accountId;
+
+ @Column(name = "account_type")
+ private short accountType;
+
+
+ public LdapTrustMapVO() {
+ }
+
+ 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
+ public long getId() {
+ return id;
+ }
+
+ public LdapManager.LinkType getType() {
+ return type;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public long getDomainId() {
+ return domainId;
+ }
+
+ public short getAccountType() {
+ return accountType;
+ }
+
+ public long getAccountId() {
+ return accountId;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ LdapTrustMapVO that = (LdapTrustMapVO) o;
+
+ if (domainId != that.domainId) {
+ return false;
+ }
+ if (accountId != that.accountId) {
+ return false;
+ }
+ if (accountType != that.accountType) {
+ return false;
+ }
+ if (type != that.type) {
+ return false;
+ }
+ return name.equals(that.name);
+
+ }
+
+ @Override
+ public int hashCode() {
+ 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 0a998f2655a..ac5aca7bd26 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;
@@ -23,14 +26,19 @@ public class LdapUser implements Comparable {
private final String lastname;
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) {
+ 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;
this.lastname = lastname;
this.principal = principal;
this.domain = domain;
+ this.disabled = disabled;
+ this.memberships = memberships == null ? new ArrayList() : memberships;
}
@Override
@@ -74,6 +82,14 @@ public class LdapUser implements Comparable {
return domain;
}
+ public boolean isDisabled() {
+ return disabled;
+ }
+
+ public List getMemberships() {
+ return memberships;
+ }
+
@Override
public int hashCode() {
return getUsername().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 654a601a476..fd18d47a05d 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
@@ -17,211 +17,28 @@
package org.apache.cloudstack.ldap;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
-import javax.inject.Inject;
-import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
-import javax.naming.directory.Attribute;
-import javax.naming.directory.Attributes;
-import javax.naming.directory.SearchControls;
-import javax.naming.directory.SearchResult;
-import javax.naming.ldap.Control;
import javax.naming.ldap.LdapContext;
-import javax.naming.ldap.PagedResultsControl;
-import javax.naming.ldap.PagedResultsResponseControl;
-import org.apache.commons.collections.CollectionUtils;
-import org.apache.commons.lang.StringUtils;
-import org.apache.log4j.Logger;
+public interface LdapUserManager {
-public class LdapUserManager {
- private static final Logger s_logger = Logger.getLogger(LdapUserManager.class.getName());
-
- @Inject
- private LdapConfiguration _ldapConfiguration;
-
- public LdapUserManager() {
+ enum Provider {
+ MICROSOFTAD, OPENLDAP;
}
- public LdapUserManager(final LdapConfiguration ldapConfiguration) {
- _ldapConfiguration = ldapConfiguration;
- }
+ LdapUser getUser(final String username, final LdapContext context, Long domainId) throws NamingException, IOException;
- private LdapUser createUser(final SearchResult result) throws NamingException {
- final Attributes attributes = result.getAttributes();
+ LdapUser getUser(final String username, final String type, final String name, final LdapContext context, Long domainId) throws NamingException, IOException;
- 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 principal = result.getNameInNamespace();
+ List getUsers(final LdapContext context, Long domainId) throws NamingException, IOException;
- String domain = principal.replace("cn=" + LdapUtils.getAttributeValue(attributes, _ldapConfiguration.getCommonNameAttribute()) + ",", "");
- domain = domain.replace("," + _ldapConfiguration.getBaseDn(), "");
- domain = domain.replace("ou=", "");
+ List getUsers(final String username, final LdapContext context, Long domainId) throws NamingException, IOException;
- return new LdapUser(username, email, firstname, lastname, principal, domain);
- }
+ List getUsersInGroup(String groupName, LdapContext context, Long domainId) throws NamingException;
- private String generateSearchFilter(final String username) {
- final StringBuilder userObjectFilter = new StringBuilder();
- userObjectFilter.append("(objectClass=");
- userObjectFilter.append(_ldapConfiguration.getUserObject());
- userObjectFilter.append(")");
+ List searchUsers(final LdapContext context, Long domainId) throws NamingException, IOException;
- final StringBuilder usernameFilter = new StringBuilder();
- usernameFilter.append("(");
- usernameFilter.append(_ldapConfiguration.getUsernameAttribute());
- 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());
- memberOfFilter.append(")");
- }
-
- final StringBuilder result = new StringBuilder();
- result.append("(&");
- result.append(userObjectFilter);
- result.append(usernameFilter);
- result.append(memberOfFilter);
- result.append(")");
-
- return result.toString();
- }
-
- private String generateGroupSearchFilter(final String groupName) {
- final StringBuilder groupObjectFilter = new StringBuilder();
- groupObjectFilter.append("(objectClass=");
- groupObjectFilter.append(_ldapConfiguration.getGroupObject());
- groupObjectFilter.append(")");
-
- final StringBuilder groupNameFilter = new StringBuilder();
- groupNameFilter.append("(");
- groupNameFilter.append(_ldapConfiguration.getCommonNameAttribute());
- groupNameFilter.append("=");
- groupNameFilter.append((groupName == null ? "*" : groupName));
- groupNameFilter.append(")");
-
- final StringBuilder result = new StringBuilder();
- result.append("(&");
- result.append(groupObjectFilter);
- result.append(groupNameFilter);
- result.append(")");
-
- return result.toString();
- }
-
- public LdapUser getUser(final String username, final LdapContext context) throws NamingException, IOException {
- List result = searchUsers(username, context);
- if (result!= null && result.size() == 1) {
- return result.get(0);
- } else {
- throw new NamingException("No user found for username " + username);
- }
- }
-
- public List getUsers(final LdapContext context) throws NamingException, IOException {
- return getUsers(null, context);
- }
-
- public List getUsers(final String username, final LdapContext context) throws NamingException, IOException {
- List users = searchUsers(username, context);
-
- if (CollectionUtils.isNotEmpty(users)) {
- Collections.sort(users);
- }
- return users;
- }
-
- public List getUsersInGroup(String groupName, LdapContext context) throws NamingException {
- String attributeName = _ldapConfiguration.getGroupUniqueMemeberAttribute();
- final SearchControls controls = new SearchControls();
- controls.setSearchScope(_ldapConfiguration.getScope());
- controls.setReturningAttributes(new String[] {attributeName});
-
- NamingEnumeration result = context.search(_ldapConfiguration.getBaseDn(), generateGroupSearchFilter(groupName), controls);
-
- final List users = new ArrayList();
- //Expecting only one result which has all the users
- if (result.hasMoreElements()) {
- Attribute attribute = result.nextElement().getAttributes().get(attributeName);
- NamingEnumeration> values = attribute.getAll();
-
- while (values.hasMoreElements()) {
- String userdn = String.valueOf(values.nextElement());
- try{
- users.add(getUserForDn(userdn, context));
- } catch (NamingException e){
- s_logger.info("Userdn: " + userdn + " Not Found:: Exception message: " + e.getMessage());
- }
- }
- }
-
- Collections.sort(users);
-
- return users;
- }
-
- private LdapUser getUserForDn(String userdn, LdapContext context) throws NamingException {
- final SearchControls controls = new SearchControls();
- controls.setSearchScope(_ldapConfiguration.getScope());
- controls.setReturningAttributes(_ldapConfiguration.getReturnAttributes());
-
- NamingEnumeration result = context.search(userdn, "(objectClass=" + _ldapConfiguration.getUserObject() + ")", controls);
- if (result.hasMoreElements()) {
- return createUser(result.nextElement());
- } else {
- throw new NamingException("No user found for dn " + userdn);
- }
- }
-
- public List searchUsers(final LdapContext context) throws NamingException, IOException {
- return searchUsers(null, context);
- }
-
- public List searchUsers(final String username, final LdapContext context) throws NamingException, IOException {
-
- final SearchControls searchControls = new SearchControls();
-
- searchControls.setSearchScope(_ldapConfiguration.getScope());
- searchControls.setReturningAttributes(_ldapConfiguration.getReturnAttributes());
-
- String basedn = _ldapConfiguration.getBaseDn();
- if (StringUtils.isBlank(basedn)) {
- throw new IllegalArgumentException("ldap basedn is not configured");
- }
- byte[] cookie = null;
- int pageSize = _ldapConfiguration.getLdapPageSize();
- context.setRequestControls(new Control[]{new PagedResultsControl(pageSize, Control.NONCRITICAL)});
- final List users = new ArrayList();
- NamingEnumeration results;
- do {
- results = context.search(basedn, generateSearchFilter(username), searchControls);
- while (results.hasMoreElements()) {
- final SearchResult result = results.nextElement();
- users.add(createUser(result));
- }
- Control[] contextControls = context.getResponseControls();
- if (contextControls != null) {
- for (Control control : contextControls) {
- if (control instanceof PagedResultsResponseControl) {
- PagedResultsResponseControl prrc = (PagedResultsResponseControl) control;
- cookie = prrc.getCookie();
- }
- }
- } else {
- s_logger.info("No controls were sent from the ldap server");
- }
- context.setRequestControls(new Control[] {new PagedResultsControl(pageSize, cookie, Control.CRITICAL)});
- } while (cookie != null);
-
- return users;
- }
-}
\ No newline at end of file
+ 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/LdapUserManagerFactory.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUserManagerFactory.java
new file mode 100644
index 00000000000..f796ce23b4e
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUserManagerFactory.java
@@ -0,0 +1,64 @@
+/*
+ * 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.log4j.Logger;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class LdapUserManagerFactory implements ApplicationContextAware {
+
+
+ public static final Logger s_logger = Logger.getLogger(LdapUserManagerFactory.class.getName());
+
+ private static Map ldapUserManagerMap = new HashMap<>();
+
+ private ApplicationContext applicationCtx;
+
+ public LdapUserManager getInstance(LdapUserManager.Provider provider) {
+ LdapUserManager ldapUserManager;
+ if (provider == LdapUserManager.Provider.MICROSOFTAD) {
+ ldapUserManager = ldapUserManagerMap.get(LdapUserManager.Provider.MICROSOFTAD);
+ if (ldapUserManager == null) {
+ ldapUserManager = new ADLdapUserManagerImpl();
+ applicationCtx.getAutowireCapableBeanFactory().autowireBeanProperties(ldapUserManager, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true);
+ ldapUserManagerMap.put(LdapUserManager.Provider.MICROSOFTAD, ldapUserManager);
+ }
+ } else {
+ //defaults to openldap
+ ldapUserManager = ldapUserManagerMap.get(LdapUserManager.Provider.OPENLDAP);
+ if (ldapUserManager == null) {
+ ldapUserManager = new OpenLdapUserManagerImpl();
+ applicationCtx.getAutowireCapableBeanFactory().autowireBeanProperties(ldapUserManager, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true);
+ ldapUserManagerMap.put(LdapUserManager.Provider.OPENLDAP, ldapUserManager);
+ }
+ }
+ return ldapUserManager;
+ }
+
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+ applicationCtx = applicationContext;
+ }
+}
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
new file mode 100644
index 00000000000..cb3824a2ef0
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/OpenLdapUserManagerImpl.java
@@ -0,0 +1,311 @@
+// 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 java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.Control;
+import javax.naming.ldap.LdapContext;
+import javax.naming.ldap.PagedResultsControl;
+import javax.naming.ldap.PagedResultsResponseControl;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Logger;
+
+public class OpenLdapUserManagerImpl implements LdapUserManager {
+ private static final Logger s_logger = Logger.getLogger(OpenLdapUserManagerImpl.class.getName());
+
+ @Inject
+ protected LdapConfiguration _ldapConfiguration;
+
+ public OpenLdapUserManagerImpl() {
+ }
+
+ public OpenLdapUserManagerImpl(final LdapConfiguration ldapConfiguration) {
+ _ldapConfiguration = ldapConfiguration;
+ }
+
+ protected LdapUser createUser(final SearchResult result, Long domainId) throws NamingException {
+ final Attributes attributes = result.getAttributes();
+
+ 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(domainId), "");
+ domain = domain.replace("ou=", "");
+
+ boolean disabled = isUserDisabled(result);
+
+ return new LdapUser(username, email, firstname, lastname, principal, domain, disabled, memberships);
+ }
+
+ private String generateSearchFilter(final String username, Long domainId) {
+ final StringBuilder userObjectFilter = new StringBuilder();
+ userObjectFilter.append("(objectClass=");
+ userObjectFilter.append(_ldapConfiguration.getUserObject(domainId));
+ userObjectFilter.append(")");
+
+ final StringBuilder usernameFilter = new StringBuilder();
+ usernameFilter.append("(");
+ usernameFilter.append(_ldapConfiguration.getUsernameAttribute(domainId));
+ usernameFilter.append("=");
+ usernameFilter.append((username == null ? "*" : username));
+ usernameFilter.append(")");
+
+ final StringBuilder memberOfFilter = new StringBuilder();
+ 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(")");
+ }
+
+ final StringBuilder result = new StringBuilder();
+ result.append("(&");
+ result.append(userObjectFilter);
+ result.append(usernameFilter);
+ result.append(memberOfFilter);
+ result.append(")");
+
+ return result.toString();
+ }
+
+ private String generateGroupSearchFilter(final String groupName, Long domainId) {
+ final StringBuilder groupObjectFilter = new StringBuilder();
+ groupObjectFilter.append("(objectClass=");
+ groupObjectFilter.append(_ldapConfiguration.getGroupObject(domainId));
+ groupObjectFilter.append(")");
+
+ final StringBuilder groupNameFilter = new StringBuilder();
+ groupNameFilter.append("(");
+ groupNameFilter.append(_ldapConfiguration.getCommonNameAttribute());
+ groupNameFilter.append("=");
+ groupNameFilter.append((groupName == null ? "*" : groupName));
+ groupNameFilter.append(")");
+
+ final StringBuilder result = new StringBuilder();
+ result.append("(&");
+ result.append(groupObjectFilter);
+ result.append(groupNameFilter);
+ result.append(")");
+
+ return result.toString();
+ }
+
+ @Override
+ 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 {
+ throw new NamingException("No user found for username " + username);
+ }
+ }
+
+ @Override
+ 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(domainId);
+ }
+
+ final StringBuilder userObjectFilter = new StringBuilder();
+ userObjectFilter.append("(objectClass=");
+ userObjectFilter.append(_ldapConfiguration.getUserObject(domainId));
+ userObjectFilter.append(")");
+
+ final StringBuilder usernameFilter = new StringBuilder();
+ usernameFilter.append("(");
+ 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(domainId)).append("=");
+ memberOfFilter.append(name);
+ memberOfFilter.append(")");
+ }
+
+ final StringBuilder searchQuery = new StringBuilder();
+ searchQuery.append("(&");
+ searchQuery.append(userObjectFilter);
+ searchQuery.append(usernameFilter);
+ searchQuery.append(memberOfFilter);
+ searchQuery.append(")");
+
+ return searchUser(basedn, searchQuery.toString(), context, domainId);
+ }
+
+ protected String getMemberOfAttribute(final Long domainId) {
+ return _ldapConfiguration.getUserMemberOfAttribute(domainId);
+ }
+
+ @Override
+ 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, Long domainId) throws NamingException, IOException {
+ List users = searchUsers(username, context, domainId);
+
+ if (CollectionUtils.isNotEmpty(users)) {
+ Collections.sort(users);
+ }
+ return users;
+ }
+
+ @Override
+ 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(domainId), generateGroupSearchFilter(groupName, domainId), controls);
+
+ final List users = new ArrayList();
+ //Expecting only one result which has all the users
+ if (result.hasMoreElements()) {
+ Attribute attribute = result.nextElement().getAttributes().get(attributeName);
+ NamingEnumeration> values = attribute.getAll();
+
+ while (values.hasMoreElements()) {
+ String userdn = String.valueOf(values.nextElement());
+ try{
+ users.add(getUserForDn(userdn, context, domainId));
+ } catch (NamingException e){
+ s_logger.info("Userdn: " + userdn + " Not Found:: Exception message: " + e.getMessage());
+ }
+ }
+ }
+
+ Collections.sort(users);
+
+ return users;
+ }
+
+ private LdapUser getUserForDn(String userdn, LdapContext context, Long domainId) throws NamingException {
+ final SearchControls controls = new SearchControls();
+ controls.setSearchScope(_ldapConfiguration.getScope());
+ controls.setReturningAttributes(_ldapConfiguration.getReturnAttributes(domainId));
+
+ NamingEnumeration result = context.search(userdn, "(objectClass=" + _ldapConfiguration.getUserObject(domainId) + ")", controls);
+ if (result.hasMoreElements()) {
+ return createUser(result.nextElement(), domainId);
+ } else {
+ throw new NamingException("No user found for dn " + userdn);
+ }
+ }
+
+ @Override
+ 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, Long domainId) throws NamingException, IOException {
+ final SearchControls searchControls = new SearchControls();
+
+ searchControls.setSearchScope(_ldapConfiguration.getScope());
+ 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, domainId));
+ }
+
+ if (users.size() == 1) {
+ return users.get(0);
+ } else {
+ throw new NamingException("No user found for basedn " + basedn + " and searchString " + searchString);
+ }
+ }
+
+ @Override
+ 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(domainId));
+
+ String basedn = _ldapConfiguration.getBaseDn(domainId);
+ if (StringUtils.isBlank(basedn)) {
+ throw new IllegalArgumentException("ldap basedn is not configured");
+ }
+ byte[] cookie = null;
+ 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, domainId), searchControls);
+ while (results.hasMoreElements()) {
+ final SearchResult result = results.nextElement();
+ if (!isUserDisabled(result)) {
+ users.add(createUser(result, domainId));
+ }
+ }
+ Control[] contextControls = context.getResponseControls();
+ if (contextControls != null) {
+ for (Control control : contextControls) {
+ if (control instanceof PagedResultsResponseControl) {
+ PagedResultsResponseControl prrc = (PagedResultsResponseControl) control;
+ cookie = prrc.getCookie();
+ }
+ }
+ } else {
+ s_logger.info("No controls were sent from the ldap server");
+ }
+ context.setRequestControls(new Control[] {new PagedResultsControl(pageSize, cookie, Control.CRITICAL)});
+ } while (cookie != null);
+
+ return users;
+ }
+}
\ No newline at end of file
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 0f2a0150eba..88fff7c0fb8 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
@@ -34,7 +34,8 @@ import com.cloud.utils.db.SearchCriteria.Op;
@Local(value = {LdapConfigurationDao.class})
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();
@@ -42,10 +43,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
new file mode 100644
index 00000000000..c3d2f8aedf4
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapTrustMapDao.java
@@ -0,0 +1,32 @@
+/*
+ * 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.dao;
+
+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
new file mode 100644
index 00000000000..0ecd3413d14
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapTrustMapDaoImpl.java
@@ -0,0 +1,80 @@
+/*
+ * 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.dao;
+
+
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+import org.apache.cloudstack.ldap.LdapTrustMapVO;
+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
new file mode 100644
index 00000000000..4b631b44e3b
--- /dev/null
+++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/ADLdapUserManagerImplSpec.groovy
@@ -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 groovy.org.apache.cloudstack.ldap
+
+import org.apache.cloudstack.ldap.ADLdapUserManagerImpl
+import org.apache.cloudstack.ldap.LdapConfiguration
+import spock.lang.Shared
+
+import javax.naming.directory.SearchControls
+import javax.naming.ldap.LdapContext
+
+class ADLdapUserManagerImplSpec extends spock.lang.Specification {
+
+ @Shared
+ ADLdapUserManagerImpl adLdapUserManager;
+
+ @Shared
+ LdapConfiguration ldapConfiguration;
+
+ def setup() {
+ adLdapUserManager = new ADLdapUserManagerImpl();
+ ldapConfiguration = Mock(LdapConfiguration);
+ adLdapUserManager._ldapConfiguration = ldapConfiguration;
+ }
+
+ def "test generate AD search filter with nested groups enabled"() {
+ ldapConfiguration.getUserObject() >> "user"
+ ldapConfiguration.getCommonNameAttribute() >> "CN"
+ ldapConfiguration.getBaseDn() >> "DC=cloud,DC=citrix,DC=com"
+ ldapConfiguration.isNestedGroupsEnabled() >> true
+
+ def result = adLdapUserManager.generateADGroupSearchFilter(group);
+ expect:
+ assert result.contains("memberOf:1.2.840.113556.1.4.1941:=")
+ result == "(&(objectClass=user)(memberOf:1.2.840.113556.1.4.1941:=CN=" + group + ",DC=cloud,DC=citrix,DC=com))"
+ where:
+ group << ["dev", "dev-hyd"]
+ }
+
+ def "test generate AD search filter with nested groups disabled"() {
+ ldapConfiguration.getUserObject() >> "user"
+ ldapConfiguration.getCommonNameAttribute() >> "CN"
+ ldapConfiguration.getBaseDn() >> "DC=cloud,DC=citrix,DC=com"
+ ldapConfiguration.isNestedGroupsEnabled() >> false
+
+ def result = adLdapUserManager.generateADGroupSearchFilter(group);
+ expect:
+ assert result.contains("memberOf=")
+ result == "(&(objectClass=user)(memberOf=CN=" + group + ",DC=cloud,DC=citrix,DC=com))"
+ where:
+ group << ["dev", "dev-hyd"]
+ }
+
+ def "test getUsersInGroup null group"() {
+ ldapConfiguration.getScope() >> SearchControls.SUBTREE_SCOPE
+ 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,null)
+ then:
+ thrown(IllegalArgumentException)
+ where:
+ group << [null, "group", null]
+
+ }
+}
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 51f8e84559a..96eb435f655 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
@@ -16,12 +16,16 @@
// under the License.
package groovy.org.apache.cloudstack.ldap
+import com.cloud.server.auth.UserAuthenticator
+import com.cloud.user.AccountManager
+import com.cloud.user.UserAccount
import com.cloud.user.UserAccountVO
import com.cloud.user.dao.UserAccountDao
import com.cloud.utils.Pair
import org.apache.cloudstack.ldap.LdapAuthenticator
-import org.apache.cloudstack.ldap.LdapConfigurationVO
import org.apache.cloudstack.ldap.LdapManager
+import org.apache.cloudstack.ldap.LdapTrustMapVO
+import org.apache.cloudstack.ldap.LdapUser
class LdapAuthenticatorSpec extends spock.lang.Specification {
@@ -38,10 +42,13 @@ class LdapAuthenticatorSpec extends spock.lang.Specification {
}
def "Test failed authentication due to ldap bind being unsuccessful"() {
- given: "We have an LdapManager, LdapConfiguration, userAccountDao and LdapAuthenticator"
- def ldapManager = Mock(LdapManager)
- ldapManager.isLdapEnabled() >> true
- ldapManager.canAuthenticate(_, _) >> false
+ given: "We have an LdapManager, LdapConfiguration, userAccountDao and LdapAuthenticator"
+ def ldapManager = Mock(LdapManager)
+ def ldapUser = Mock(LdapUser)
+ ldapUser.isDisabled() >> false
+ ldapManager.isLdapEnabled() >> true
+ ldapManager.getUser("rmurphy", null) >> ldapUser
+ ldapManager.canAuthenticate(_, _, _) >> false
UserAccountDao userAccountDao = Mock(UserAccountDao)
userAccountDao.getUserAccount(_, _) >> new UserAccountVO()
@@ -69,11 +76,14 @@ class LdapAuthenticatorSpec extends spock.lang.Specification {
result.first() == false
}
- def "Test successful authentication"() {
- given: "We have an LdapManager, LdapConfiguration, userAccountDao and LdapAuthenticator"
- def ldapManager = Mock(LdapManager)
- ldapManager.isLdapEnabled() >> true
- ldapManager.canAuthenticate(_, _) >> true
+ def "Test successful authentication"() {
+ given: "We have an LdapManager, LdapConfiguration, userAccountDao and LdapAuthenticator"
+ def ldapManager = Mock(LdapManager)
+ def ldapUser = Mock(LdapUser)
+ ldapUser.isDisabled() >> false
+ ldapManager.isLdapEnabled() >> true
+ ldapManager.canAuthenticate(_, _, _) >> true
+ ldapManager.getUser("rmurphy", null) >> ldapUser
UserAccountDao userAccountDao = Mock(UserAccountDao)
userAccountDao.getUserAccount(_, _) >> new UserAccountVO()
@@ -87,13 +97,151 @@ class LdapAuthenticatorSpec extends spock.lang.Specification {
}
def "Test that encode doesn't change the input"() {
- given: "We have an LdapManager, userAccountDao and LdapAuthenticator"
- LdapManager ldapManager = Mock(LdapManager)
- UserAccountDao userAccountDao = Mock(UserAccountDao)
- def ldapAuthenticator = new LdapAuthenticator(ldapManager, userAccountDao)
- when: "a users password is encoded"
- def result = ldapAuthenticator.encode("password")
- then: "it doesn't change"
- result == "password"
+ given: "We have an LdapManager, userAccountDao and LdapAuthenticator"
+ LdapManager ldapManager = Mock(LdapManager)
+ UserAccountDao userAccountDao = Mock(UserAccountDao)
+ def ldapAuthenticator = new LdapAuthenticator(ldapManager, userAccountDao)
+ when: "a users password is encoded"
+ def result = ldapAuthenticator.encode("password")
+ then: "it doesn't change"
+ result == "password"
+ }
+
+ def "test authentication when ldap is disabled"(){
+ LdapManager ldapManager = Mock(LdapManager)
+ UserAccountDao userAccountDao = Mock(UserAccountDao)
+ def ldapAuthenticator = new LdapAuthenticator(ldapManager, userAccountDao)
+ ldapManager.isLdapEnabled() >> false
+
+ when:
+ Pair result = ldapAuthenticator.authenticate("rajanik", "password", 1, null)
+ then:
+ result.first() == false
+ result.second() == null
+
+ }
+
+ // tests when domain is linked to LDAP
+ def "test authentication when domain is linked and user disabled in ldap"(){
+ LdapManager ldapManager = Mock(LdapManager)
+ UserAccountDao userAccountDao = Mock(UserAccountDao)
+ AccountManager accountManager = Mock(AccountManager)
+
+ def ldapAuthenticator = new LdapAuthenticator()
+ ldapAuthenticator._ldapManager = ldapManager
+ ldapAuthenticator._userAccountDao = userAccountDao
+ ldapAuthenticator._accountManager = accountManager
+
+ long domainId = 1;
+ String username = "rajanik"
+ LdapManager.LinkType type = LdapManager.LinkType.GROUP
+ String name = "CN=test,DC=ccp,DC=citrix,DC=com"
+
+ ldapManager.isLdapEnabled() >> true
+ UserAccount userAccount = Mock(UserAccount)
+ 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, null)
+ //user should be disabled in cloudstack
+ accountManager.disableUser(1) >> userAccount
+
+ when:
+ Pair result = ldapAuthenticator.authenticate(username, "password", domainId, null)
+ then:
+ result.first() == false
+ result.second() == UserAuthenticator.ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT
+ }
+
+ def "test authentication when domain is linked and first time user can authenticate in ldap"(){
+ LdapManager ldapManager = Mock(LdapManager)
+ UserAccountDao userAccountDao = Mock(UserAccountDao)
+ AccountManager accountManager = Mock(AccountManager)
+
+ def ldapAuthenticator = new LdapAuthenticator()
+ ldapAuthenticator._ldapManager = ldapManager
+ ldapAuthenticator._userAccountDao = userAccountDao
+ ldapAuthenticator._accountManager = accountManager
+
+ long domainId = 1;
+ String username = "rajanik"
+ LdapManager.LinkType type = LdapManager.LinkType.GROUP
+ String name = "CN=test,DC=ccp,DC=citrix,DC=com"
+
+ 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, 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)
+
+ when:
+ Pair result = ldapAuthenticator.authenticate(username, "password", domainId, null)
+ then:
+ result.first() == true
+ result.second() == null
+ }
+
+ def "test authentication when domain is linked and existing user can authenticate in ldap"(){
+ LdapManager ldapManager = Mock(LdapManager)
+ UserAccountDao userAccountDao = Mock(UserAccountDao)
+ AccountManager accountManager = Mock(AccountManager)
+
+ def ldapAuthenticator = new LdapAuthenticator()
+ ldapAuthenticator._ldapManager = ldapManager
+ ldapAuthenticator._userAccountDao = userAccountDao
+ ldapAuthenticator._accountManager = accountManager
+
+ long domainId = 1;
+ String username = "rajanik"
+ LdapManager.LinkType type = LdapManager.LinkType.GROUP
+ String name = "CN=test,DC=ccp,DC=citrix,DC=com"
+
+ ldapManager.isLdapEnabled() >> true
+ UserAccount userAccount = Mock(UserAccount)
+ userAccountDao.getUserAccount(username, domainId) >> userAccount
+ 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, null)
+ ldapManager.canAuthenticate(_, _, _) >> true
+ //user should be enabled in cloudstack if disabled
+ accountManager.enableUser(1) >> userAccount
+
+ when:
+ Pair result = ldapAuthenticator.authenticate(username, "password", domainId, null)
+ then:
+ result.first() == true
+ result.second() == null
+ }
+
+ def "test authentication when domain is linked and user cannot authenticate in ldap"(){
+ LdapManager ldapManager = Mock(LdapManager)
+ UserAccountDao userAccountDao = Mock(UserAccountDao)
+ AccountManager accountManager = Mock(AccountManager)
+
+ def ldapAuthenticator = new LdapAuthenticator()
+ ldapAuthenticator._ldapManager = ldapManager
+ ldapAuthenticator._userAccountDao = userAccountDao
+ ldapAuthenticator._accountManager = accountManager
+
+ long domainId = 1;
+ String username = "rajanik"
+ LdapManager.LinkType type = LdapManager.LinkType.GROUP
+ String name = "CN=test,DC=ccp,DC=citrix,DC=com"
+
+ ldapManager.isLdapEnabled() >> true
+ 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, null)
+ ldapManager.canAuthenticate(_, _, _) >> false
+
+ when:
+ Pair result = ldapAuthenticator.authenticate(username, "password", domainId, null)
+ then:
+ result.first() == false
+ result.second() == UserAuthenticator.ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT
}
}
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 adc3463afde..dab18105d7e 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,53 +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.cxf.common.util.StringUtils
+import org.apache.cloudstack.ldap.LdapUserManager
+import org.apache.cloudstack.ldap.dao.LdapConfigurationDao
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 ldapManager = Mock(LdapManager)
- def ldapConfiguration = new LdapConfiguration(configDao, ldapManager)
+ def ldapConfigurationDao = Mock(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 ldapManager = Mock(LdapManager)
- def ldapConfiguration = new LdapConfiguration(configDao, ldapManager)
- 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 ldapManager = Mock(LdapManager)
- def ldapConfiguration = new LdapConfiguration(configDao, ldapManager)
- 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)
@@ -176,87 +149,12 @@ class LdapConfigurationSpec extends spock.lang.Specification {
LdapConfiguration ldapConfiguration = new LdapConfiguration(configDao, ldapManager)
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 ldapManager = Mock(LdapManager)
- LdapConfiguration ldapConfiguration = new LdapConfiguration(configDao, ldapManager)
-
- 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 ldapManager = Mock(LdapManager)
- LdapConfiguration ldapConfiguration = new LdapConfiguration(configDao, ldapManager)
-
- 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 ldapManager = Mock(LdapManager)
- LdapConfiguration ldapConfiguration = new LdapConfiguration(configDao, ldapManager)
-
- 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 ldapManger = Mock(LdapManager)
- LdapConfiguration ldapConfiguration = new LdapConfiguration(configDao, ldapManger)
- 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 ldapManger = Mock(LdapManager)
- LdapConfiguration ldapConfiguration = new LdapConfiguration(configDao, ldapManger)
- 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)
@@ -273,11 +171,34 @@ 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:
timeout << ["1000000", "1000", null]
}
+ def "Test getLdapProvider()"() {
+ given: "We have configdao for ldap group object"
+ def configDao = Mock(ConfigurationDao)
+ ConfigurationVO configurationVo = new ConfigurationVO("ldap.read.timeout", LdapConfiguration.ldapProvider);
+ configurationVo.setValue(provider)
+ configDao.findById("ldap.provider") >> configurationVo
+
+ def configDepotImpl = Mock(ConfigDepotImpl)
+ configDepotImpl.global() >> configDao
+ ConfigKey.init(configDepotImpl)
+
+ def ldapConfigurationDao = Mock(LdapConfigurationDao)
+ LdapConfiguration ldapConfiguration = new LdapConfiguration(configDao, ldapConfigurationDao)
+
+ def expected = provider.equalsIgnoreCase("microsoftad") ? LdapUserManager.Provider.MICROSOFTAD : LdapUserManager.Provider.OPENLDAP //"openldap" is the default value
+
+ def result = ldapConfiguration.getLdapProvider(null)
+ expect:
+ println "asserting for provider configuration: " + provider
+ result == expected
+ where:
+ provider << ["openldap", "microsoftad", "", " ", "xyz", "MicrosoftAd", "OpenLdap", "MicrosoftAD"]
+ }
}
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 2a665283edd..05d4aa1945f 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 5805e0b6591..9fe64aec786 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")
- 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)
@@ -100,48 +59,48 @@ class LdapCreateAccountCmdSpec extends spock.lang.Specification {
}
def "Test validate User"() {
- given: "We have an LdapManager, AccountService andL dapCreateAccount"
- LdapManager ldapManager = Mock(LdapManager)
- 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"))
- then: "the result is true"
- result == true
- }
+ given: "We have an LdapManager, AccountService andL dapCreateAccount"
+ LdapManager ldapManager = Mock(LdapManager)
+ 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, null))
+ then: "the result is true"
+ result == true
+ }
def "Test validate User empty email"() {
- given: "We have an LdapManager, AccountService andL dapCreateAccount"
- LdapManager ldapManager = Mock(LdapManager)
- 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"))
- then: "An exception is thrown"
- thrown Exception
- }
+ given: "We have an LdapManager, AccountService andL dapCreateAccount"
+ LdapManager ldapManager = Mock(LdapManager)
+ 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, null))
+ then: "An exception is thrown"
+ thrown Exception
+ }
def "Test validate User empty firstname"() {
- given: "We have an LdapManager, AccountService andL dapCreateAccount"
- LdapManager ldapManager = Mock(LdapManager)
- AccountService accountService = Mock(AccountService)
- def ldapCreateAccountCmd = new LdapCreateAccountCmd(ldapManager, accountService)
- when: "A user with no firstname attempts to validate"
- ldapCreateAccountCmd.validateUser(new LdapUser("username","email",null,"lastname","principal"))
- then: "An exception is thrown"
- thrown Exception
- }
+ given: "We have an LdapManager, AccountService andL dapCreateAccount"
+ LdapManager ldapManager = Mock(LdapManager)
+ AccountService accountService = Mock(AccountService)
+ def ldapCreateAccountCmd = new LdapCreateAccountCmd(ldapManager, accountService)
+ when: "A user with no firstname attempts to validate"
+ ldapCreateAccountCmd.validateUser(new LdapUser("username", "email", null, "lastname", "principal", false))
+ then: "An exception is thrown"
+ thrown Exception
+ }
- def "Test validate User empty lastname"() {
- given: "We have an LdapManager, AccountService and LdapCreateAccountCmd"
- LdapManager ldapManager = Mock(LdapManager)
- 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"))
- then: "An exception is thown"
- thrown Exception
- }
+ def "Test validate User empty lastname"() {
+ given: "We have an LdapManager, AccountService and LdapCreateAccountCmd"
+ LdapManager ldapManager = Mock(LdapManager)
+ 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, null))
+ then: "An exception is thown"
+ thrown Exception
+ }
def "Test validation of a user"() {
given: "We have an LdapManager, AccountService andL dapCreateAccount"
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 3c9b584d7e5..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"))
- users.add(new LdapUser("bob", "bob@test.com", "Robert", "Young", "cn=bob,ou=engineering,dc=cloudstack,dc=org", "engineering"))
- 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"))
- users.add(new LdapUser("bob", "bob@test.com", "Robert", "Young", "cn=bob,ou=engineering,dc=cloudstack,dc=org", "engineering"))
- 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"))
- users.add(new LdapUser("bob", "bob@test.com", "Robert", "Young", "cn=bob,ou=engineering,dc=cloudstack,dc=org", "engineering"))
- 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"))
- users.add(new LdapUser("bob", "bob@test.com", "Robert", "Young", "cn=bob,ou=engineering,dc=cloudstack,dc=org", "engineering"))
- 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")
- def ldapUser2 = new LdapUser("bob", "bob@test.com", "Robert", "Young", "cn=bob,ou=engineering,dc=cloudstack,dc=org", "engineering");
+ 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"))
- 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"))
- 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"))
- 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 4b32eb1ecd6..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))
- 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)
+ 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)
+ 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 ee317206854..fd047931acb 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
@@ -16,24 +16,17 @@
// under the License.
package groovy.org.apache.cloudstack.ldap
-import org.apache.cloudstack.api.command.LDAPConfigCmd
-import org.apache.cloudstack.api.command.LDAPRemoveCmd
-import org.apache.cloudstack.api.command.LdapAddConfigurationCmd
-import org.apache.cloudstack.api.command.LdapCreateAccountCmd
-import org.apache.cloudstack.api.command.LdapDeleteConfigurationCmd
-import org.apache.cloudstack.api.command.LdapImportUsersCmd
-import org.apache.cloudstack.api.command.LdapListUsersCmd
-import org.apache.cloudstack.api.command.LdapUserSearchCmd
+import com.cloud.exception.InvalidParameterValueException
+import com.cloud.utils.Pair
+import org.apache.cloudstack.api.command.LdapListConfigurationCmd
+import org.apache.cloudstack.api.response.LinkDomainToLdapResponse
+import org.apache.cloudstack.ldap.*
+import org.apache.cloudstack.ldap.dao.LdapConfigurationDaoImpl
+import org.apache.cloudstack.ldap.dao.LdapTrustMapDao
import javax.naming.NamingException
import javax.naming.ldap.InitialLdapContext
-
-import org.apache.cloudstack.api.command.LdapListConfigurationCmd
-import org.apache.cloudstack.ldap.*
-import org.apache.cloudstack.ldap.dao.LdapConfigurationDaoImpl
-
-import com.cloud.exception.InvalidParameterValueException
-import com.cloud.utils.Pair
+import javax.naming.ldap.LdapContext
class LdapManagerImplSpec extends spock.lang.Specification {
def "Test failing of getUser due to bind issue"() {
@@ -44,7 +37,7 @@ class LdapManagerImplSpec extends spock.lang.Specification {
ldapContextFactory.createBindContext() >> { throw new NoLdapUserMatchingQueryException() }
def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager)
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
}
@@ -57,7 +50,7 @@ class LdapManagerImplSpec extends spock.lang.Specification {
ldapContextFactory.createBindContext() >> { throw new NamingException() }
def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager)
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
}
@@ -96,7 +89,7 @@ class LdapManagerImplSpec extends spock.lang.Specification {
def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager)
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"))
+ "engineering", false, null))
then: "The result of the response should match the given ldap user"
result.username == "rmurphy"
result.email == "rmurphy@test.com"
@@ -113,11 +106,11 @@ class LdapManagerImplSpec extends spock.lang.Specification {
def ldapUserManager = Mock(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))
+ 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, ldapUserManager)
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;
}
@@ -128,10 +121,10 @@ class LdapManagerImplSpec extends spock.lang.Specification {
def ldapContextFactory = Mock(LdapContextFactory)
def ldapUserManager = Mock(LdapUserManager)
ldapContextFactory.createBindContext() >> null
- ldapUserManager.getUser(_, _) >> new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null)
- def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager)
+ 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"
@@ -159,10 +152,13 @@ class LdapManagerImplSpec extends spock.lang.Specification {
def ldapContextFactory = Mock(LdapContextFactory)
ldapContextFactory.createUserContext(_, _) >> { throw new NamingException() }
def ldapUserManager = Mock(LdapUserManager)
- def ldapManager = Spy(LdapManagerImpl, constructorArgs: [ldapConfigurationDao, ldapContextFactory, ldapUserManager])
- ldapManager.getUser(_) >> { new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null) }
+ def ldapUserManagerFactory = Mock(LdapUserManagerFactory)
+ ldapUserManagerFactory.getInstance(_) >> ldapUserManager
+ def ldapConfiguration = Mock(LdapConfiguration)
+ def ldapManager = Spy(LdapManagerImpl, constructorArgs: [ldapConfigurationDao, ldapContextFactory, ldapUserManagerFactory, ldapConfiguration])
+ 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
}
@@ -188,7 +184,7 @@ class LdapManagerImplSpec extends spock.lang.Specification {
ldapConfigurationDao.findByHostname(_) >> null
def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager)
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
}
@@ -213,10 +209,13 @@ class LdapManagerImplSpec extends spock.lang.Specification {
def ldapContextFactory = Mock(LdapContextFactory)
ldapContextFactory.createUserContext(_, _) >> null
def ldapUserManager = Mock(LdapUserManager)
- def ldapManager = Spy(LdapManagerImpl, constructorArgs: [ldapConfigurationDao, ldapContextFactory, ldapUserManager])
- ldapManager.getUser(_) >> { new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null) }
+ def ldapUserManagerFactory = Mock(LdapUserManagerFactory)
+ def ldapConfiguration = Mock(LdapConfiguration)
+ ldapUserManagerFactory.getInstance(_) >> ldapUserManager
+ def ldapManager = Spy(LdapManagerImpl, constructorArgs: [ldapConfigurationDao, ldapContextFactory, ldapUserManagerFactory, ldapConfiguration])
+ 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
}
@@ -234,7 +233,7 @@ class LdapManagerImplSpec extends spock.lang.Specification {
ldapConfigurationDao.remove(_) >> null
def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManager)
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
@@ -248,7 +247,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"))
+ 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, ldapUserManager)
@@ -368,12 +367,162 @@ class LdapManagerImplSpec extends spock.lang.Specification {
def ldapUserManager = Mock(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"))
+ 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, ldapUserManager)
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;
}
+
+ def "test linkDomainToLdap invalid ldap group type"() {
+ def ldapManager = new LdapManagerImpl()
+ LdapTrustMapDao ldapTrustMapDao = Mock(LdapTrustMapDao)
+ ldapManager._ldapTrustMapDao = ldapTrustMapDao
+
+ def domainId = 1
+ when:
+ println("using type: " + type)
+ LinkDomainToLdapResponse response = ldapManager.linkDomainToLdap(domainId, type, "CN=test,DC=CCP,DC=Citrix,DC=Com", (short)2)
+ then:
+ thrown(IllegalArgumentException)
+ where:
+ type << ["", null, "TEST", "TEST TEST"]
+ }
+ def "test linkDomainToLdap invalid domain"() {
+ def ldapManager = new LdapManagerImpl()
+ LdapTrustMapDao ldapTrustMapDao = Mock(LdapTrustMapDao)
+ ldapManager._ldapTrustMapDao = ldapTrustMapDao
+
+ when:
+ LinkDomainToLdapResponse response = ldapManager.linkDomainToLdap(null, "GROUP", "CN=test,DC=CCP,DC=Citrix,DC=Com", (short)2)
+ then:
+ thrown(IllegalArgumentException)
+ }
+ def "test linkDomainToLdap invalid ldap name"() {
+ def ldapManager = new LdapManagerImpl()
+ LdapTrustMapDao ldapTrustMapDao = Mock(LdapTrustMapDao)
+ ldapManager._ldapTrustMapDao = ldapTrustMapDao
+
+ def domainId = 1
+ when:
+ println("using name: " + name)
+ LinkDomainToLdapResponse response = ldapManager.linkDomainToLdap(domainId, "GROUP", name, (short)2)
+ then:
+ thrown(IllegalArgumentException)
+ where:
+ name << ["", null]
+ }
+ def "test linkDomainToLdap invalid accountType"(){
+
+ def ldapManager = new LdapManagerImpl()
+ LdapTrustMapDao ldapTrustMapDao = Mock(LdapTrustMapDao)
+ ldapManager._ldapTrustMapDao = ldapTrustMapDao
+
+ def domainId = 1
+ when:
+ println("using accountType: " + accountType)
+ LinkDomainToLdapResponse response = ldapManager.linkDomainToLdap(domainId, "GROUP", "TEST", (short)accountType)
+ then:
+ thrown(IllegalArgumentException)
+ where:
+ accountType << [-1, 1, 3, 4, 5, 6, 20000, -500000]
+ }
+ def "test linkDomainToLdap when all is well"(){
+ def ldapManager = new LdapManagerImpl()
+ LdapTrustMapDao ldapTrustMapDao = Mock(LdapTrustMapDao)
+ ldapManager._ldapTrustMapDao = ldapTrustMapDao
+
+ def domainId=1
+ def type=LdapManager.LinkType.GROUP
+ def name="CN=test,DC=CCP, DC=citrix,DC=com"
+ short accountType=2
+
+ 1 * ldapTrustMapDao.persist(new LdapTrustMapVO(domainId, type, name, accountType)) >> new LdapTrustMapVO(domainId, type, name, accountType)
+
+ when:
+ LinkDomainToLdapResponse response = ldapManager.linkDomainToLdap(domainId, type.toString(), name, accountType)
+ then:
+ response.getDomainId() == domainId
+ response.getType() == type.toString()
+ response.getName() == name
+ response.getAccountType() == accountType
+ }
+
+ def "test getUser(username,type,group) when username disabled in ldap"(){
+ def ldapUserManager = Mock(LdapUserManager)
+ def ldapUserManagerFactory = Mock(LdapUserManagerFactory)
+ ldapUserManagerFactory.getInstance(_) >> ldapUserManager
+ def ldapContextFactory = Mock(LdapContextFactory)
+ ldapContextFactory.createBindContext() >> Mock(LdapContext)
+ def ldapConfiguration = Mock(LdapConfiguration)
+
+ def ldapManager = new LdapManagerImpl()
+ ldapManager._ldapUserManagerFactory = ldapUserManagerFactory
+ ldapManager._ldapContextFactory = ldapContextFactory
+ ldapManager._ldapConfiguration = ldapConfiguration
+
+ def username = "admin"
+ 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, null)
+
+ when:
+ LdapUser user = ldapManager.getUser(username, type, name)
+ then:
+ user.getUsername() == username
+ user.isDisabled() == true
+ }
+
+ def "test getUser(username,type,group) when username doesnt exist in ldap"(){
+ def ldapUserManager = Mock(LdapUserManager)
+ def ldapUserManagerFactory = Mock(LdapUserManagerFactory)
+ ldapUserManagerFactory.getInstance(_) >> ldapUserManager
+ def ldapContextFactory = Mock(LdapContextFactory)
+ ldapContextFactory.createBindContext() >> Mock(LdapContext)
+ def ldapConfiguration = Mock(LdapConfiguration)
+
+ def ldapManager = new LdapManagerImpl()
+ ldapManager._ldapUserManagerFactory = ldapUserManagerFactory
+ ldapManager._ldapContextFactory = ldapContextFactory
+ ldapManager._ldapConfiguration = ldapConfiguration
+
+ def username = "admin"
+ def type = "GROUP"
+ def name = "CN=test,DC=citrix,DC=com"
+
+ ldapUserManager.getUser(username, type, name, _) >> { throw new NamingException("Test naming exception") }
+
+ when:
+ LdapUser user = ldapManager.getUser(username, type, name)
+ then:
+ thrown(NoLdapUserMatchingQueryException)
+ }
+ def "test getUser(username,type,group) when username is an active member of the group in ldap"(){
+ def ldapUserManager = Mock(LdapUserManager)
+ def ldapUserManagerFactory = Mock(LdapUserManagerFactory)
+ ldapUserManagerFactory.getInstance(_) >> ldapUserManager
+ def ldapContextFactory = Mock(LdapContextFactory)
+ ldapContextFactory.createBindContext() >> Mock(LdapContext)
+ def ldapConfiguration = Mock(LdapConfiguration)
+
+ def ldapManager = new LdapManagerImpl()
+ ldapManager._ldapUserManagerFactory = ldapUserManagerFactory
+ ldapManager._ldapContextFactory = ldapContextFactory
+ ldapManager._ldapConfiguration = ldapConfiguration
+
+ def username = "admin"
+ 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, null)
+
+ when:
+ LdapUser user = ldapManager.getUser(username, type, name)
+ then:
+ user.getUsername() == username
+ user.isDisabled() == false
+ }
}
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 1411c29f7b4..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))
+ 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/LdapUserManagerFactorySpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserManagerFactorySpec.groovy
new file mode 100644
index 00000000000..ca423d3e630
--- /dev/null
+++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserManagerFactorySpec.groovy
@@ -0,0 +1,57 @@
+/*
+ * 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 groovy.org.apache.cloudstack.ldap
+
+import org.apache.cloudstack.ldap.ADLdapUserManagerImpl
+import org.apache.cloudstack.ldap.LdapUserManager
+import org.apache.cloudstack.ldap.LdapUserManagerFactory
+import org.apache.cloudstack.ldap.OpenLdapUserManagerImpl
+import org.springframework.beans.factory.config.AutowireCapableBeanFactory
+import org.springframework.context.ApplicationContext
+import spock.lang.Shared
+
+class LdapUserManagerFactorySpec extends spock.lang.Specification {
+
+ @Shared
+ def LdapUserManagerFactory ldapUserManagerFactory;
+
+ def setupSpec() {
+ ldapUserManagerFactory = new LdapUserManagerFactory();
+ ApplicationContext applicationContext = Mock(ApplicationContext);
+ AutowireCapableBeanFactory autowireCapableBeanFactory = Mock(AutowireCapableBeanFactory);
+ applicationContext.getAutowireCapableBeanFactory() >> autowireCapableBeanFactory;
+ ldapUserManagerFactory.setApplicationContext(applicationContext);
+ }
+
+ def "Test getInstance() from factory"() {
+ def result = ldapUserManagerFactory.getInstance(id);
+
+ def expected;
+ if(id == LdapUserManager.Provider.MICROSOFTAD) {
+ expected = ADLdapUserManagerImpl.class;
+ } else {
+ expected = OpenLdapUserManagerImpl.class;
+ }
+
+ expect:
+ assert result.class.is(expected)
+ where:
+ id << [LdapUserManager.Provider.MICROSOFTAD, LdapUserManager.Provider.OPENLDAP, null]
+ }
+}
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 6df947be22a..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, "", "", "", "", "")
+ 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, "", "", "", "", "")
- def userB = new LdapUser(usernameB, "", "", "", "", "")
+ 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, "", "", "", "", "")
- def userB = new LdapUser(usernameB, "", "", "", "", "")
+ 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, "", "", "", "", "")
+ 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, "", "", "", "")
+ 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, "", "", "")
+ 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, "", "")
+ 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, "")
+ 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)
+ 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
new file mode 100644
index 00000000000..46b00a93d6c
--- /dev/null
+++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LinkDomainToLdapCmdSpec.groovy
@@ -0,0 +1,232 @@
+/*
+ * 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 groovy.org.apache.cloudstack.ldap
+
+import com.cloud.exception.InvalidParameterValueException
+import com.cloud.user.Account
+import com.cloud.user.AccountService
+import com.cloud.user.User
+import com.cloud.user.UserAccount
+import org.apache.cloudstack.api.ServerApiException
+import org.apache.cloudstack.api.command.LinkDomainToLdapCmd
+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 spock.lang.Shared
+import spock.lang.Specification
+
+class LinkDomainToLdapCmdSpec extends Specification {
+
+ @Shared
+ private LdapManager _ldapManager;
+
+ @Shared
+ public AccountService _accountService;
+
+ @Shared
+ public LinkDomainToLdapCmd linkDomainToLdapCmd;
+
+ def setup() {
+ _ldapManager = Mock(LdapManager)
+ _accountService = Mock(AccountService)
+
+ linkDomainToLdapCmd = new LinkDomainToLdapCmd()
+ linkDomainToLdapCmd._accountService = _accountService
+ linkDomainToLdapCmd._ldapManager = _ldapManager
+ }
+
+ def "test invalid params"() {
+ _ldapManager.linkDomainToLdap(_,_,_,_) >> {throw new InvalidParameterValueException("invalid param")}
+ when:
+ linkDomainToLdapCmd.execute();
+ then:
+ thrown(ServerApiException)
+ }
+ def "test valid params without admin"(){
+ LinkDomainToLdapResponse response = new LinkDomainToLdapResponse("1", "GROUP", "CN=test,DC=ccp,DC=citrix,DC=com", (short)2)
+ _ldapManager.linkDomainToLdap(_,_,_,_) >> response
+ when:
+ linkDomainToLdapCmd.execute()
+ then:
+ LinkDomainToLdapResponse result = (LinkDomainToLdapResponse)linkDomainToLdapCmd.getResponseObject()
+ result.getObjectName() == "LinkDomainToLdap"
+ result.getResponseName() == linkDomainToLdapCmd.getCommandName()
+ }
+
+ def "test with valid params and with disabled admin"() {
+ def domainId = "1";
+ def type = "GROUP";
+ def name = "CN=test,DC=ccp,DC=Citrix,DC=com"
+ def accountType = 2;
+ def username = "admin"
+
+ 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, null)
+
+ linkDomainToLdapCmd.admin = username
+ linkDomainToLdapCmd.type = type
+ linkDomainToLdapCmd.name = name
+ linkDomainToLdapCmd.domainId = domainId
+
+ when:
+ linkDomainToLdapCmd.execute()
+ then:
+ LinkDomainToLdapResponse result = (LinkDomainToLdapResponse)linkDomainToLdapCmd.getResponseObject()
+ result.getObjectName() == "LinkDomainToLdap"
+ result.getResponseName() == linkDomainToLdapCmd.getCommandName()
+ result.getDomainId() == domainId
+ result.getType() == type
+ result.getName() == name
+ result.getAdminId() == null
+ }
+
+ def "test with valid params and with admin who exist in cloudstack already"() {
+ def domainId = 1L;
+ def type = "GROUP";
+ def name = "CN=test,DC=ccp,DC=Citrix,DC=com"
+ def accountType = 2;
+ def username = "admin"
+
+ 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, null)
+
+ _accountService.getActiveAccountByName(username, domainId) >> Mock(Account)
+
+ linkDomainToLdapCmd.admin = username
+ linkDomainToLdapCmd.type = type
+ linkDomainToLdapCmd.name = name
+ linkDomainToLdapCmd.domainId = domainId
+
+ when:
+ linkDomainToLdapCmd.execute()
+ then:
+ LinkDomainToLdapResponse result = (LinkDomainToLdapResponse)linkDomainToLdapCmd.getResponseObject()
+ result.getObjectName() == "LinkDomainToLdap"
+ result.getResponseName() == linkDomainToLdapCmd.getCommandName()
+ result.getDomainId() == domainId.toString()
+ result.getType() == type
+ result.getName() == name
+ result.getAdminId() == null
+ }
+
+ def "test with valid params and with admin who doesnt exist in cloudstack"() {
+ def domainId = 1L;
+ def type = "GROUP";
+ def name = "CN=test,DC=ccp,DC=Citrix,DC=com"
+ def accountType = 2;
+ def username = "admin"
+ def accountId = 24
+
+ 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)
+
+ _accountService.getActiveAccountByName(username, domainId) >> null
+ UserAccount userAccount = Mock(UserAccount)
+ userAccount.getAccountId() >> 24
+ _accountService.createUserAccount(username, "", "Admin", "Admin", "admin@ccp.citrix.com", null, username, Account.ACCOUNT_TYPE_DOMAIN_ADMIN, domainId,
+ username, null, _, _, User.Source.LDAP) >> userAccount
+
+ linkDomainToLdapCmd.admin = username
+ linkDomainToLdapCmd.type = type
+ linkDomainToLdapCmd.name = name
+ linkDomainToLdapCmd.domainId = domainId
+
+ when:
+ linkDomainToLdapCmd.execute()
+ then:
+ LinkDomainToLdapResponse result = (LinkDomainToLdapResponse)linkDomainToLdapCmd.getResponseObject()
+ result.getObjectName() == "LinkDomainToLdap"
+ result.getResponseName() == linkDomainToLdapCmd.getCommandName()
+ result.getDomainId() == domainId.toString()
+ result.getType() == type
+ result.getName() == name
+ result.getAdminId() == String.valueOf(accountId)
+ }
+
+ def "test when admin doesnt exist in ldap"() {
+ def domainId = 1L;
+ def type = "GROUP";
+ def name = "CN=test,DC=ccp,DC=Citrix,DC=com"
+ def accountType = 2;
+ def username = "admin"
+
+ LinkDomainToLdapResponse response = new LinkDomainToLdapResponse(domainId.toString(), type, name, (short)accountType)
+ _ldapManager.linkDomainToLdap(_,_,_,_) >> response
+ _ldapManager.getUser(username, type, name) >> {throw new NoLdapUserMatchingQueryException("get ldap user failed from mock")}
+
+ linkDomainToLdapCmd.admin = username
+ linkDomainToLdapCmd.type = type
+ linkDomainToLdapCmd.name = name
+ linkDomainToLdapCmd.domainId = domainId
+
+ when:
+ linkDomainToLdapCmd.execute()
+ then:
+ LinkDomainToLdapResponse result = (LinkDomainToLdapResponse)linkDomainToLdapCmd.getResponseObject()
+ result.getObjectName() == "LinkDomainToLdap"
+ result.getResponseName() == linkDomainToLdapCmd.getCommandName()
+ result.getDomainId() == domainId.toString()
+ result.getType() == type
+ result.getName() == name
+ result.getAdminId() == null
+ }
+
+ /**
+ * api should not fail in this case as link domain to ldap is successful
+ */
+ def "test when create user account throws a run time exception"() {
+ def domainId = 1L;
+ def type = "GROUP";
+ def name = "CN=test,DC=ccp,DC=Citrix,DC=com"
+ def accountType = 2;
+ def username = "admin"
+ def accountId = 24
+
+ 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, null)
+
+ _accountService.getActiveAccountByName(username, domainId) >> null
+ UserAccount userAccount = Mock(UserAccount)
+ userAccount.getAccountId() >> 24
+ _accountService.createUserAccount(username, "", "Admin", "Admin", "admin@ccp.citrix.com", null, username, Account.ACCOUNT_TYPE_DOMAIN_ADMIN, domainId,
+ username, null, _, _, User.Source.LDAP) >> { throw new RuntimeException("created failed from mock") }
+
+ linkDomainToLdapCmd.admin = username
+ linkDomainToLdapCmd.type = type
+ linkDomainToLdapCmd.name = name
+ linkDomainToLdapCmd.domainId = domainId
+
+ when:
+ linkDomainToLdapCmd.execute()
+ then:
+ LinkDomainToLdapResponse result = (LinkDomainToLdapResponse)linkDomainToLdapCmd.getResponseObject()
+ result.getObjectName() == "LinkDomainToLdap"
+ result.getResponseName() == linkDomainToLdapCmd.getCommandName()
+ result.getDomainId() == domainId.toString()
+ result.getType() == type
+ result.getName() == name
+ result.getAdminId() == null
+ }
+
+}
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
new file mode 100644
index 00000000000..40daa4110fc
--- /dev/null
+++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/OpenLdapUserManagerSpec.groovy
@@ -0,0 +1,335 @@
+// 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 groovy.org.apache.cloudstack.ldap
+
+import org.apache.cloudstack.ldap.LdapConfiguration
+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.SearchControls
+import javax.naming.directory.SearchResult
+import javax.naming.ldap.InitialLdapContext
+import javax.naming.ldap.LdapContext
+
+class OpenLdapUserManagerSpec extends spock.lang.Specification {
+
+ @Shared
+ private def ldapConfiguration
+
+ @Shared
+ private def username
+
+ @Shared
+ private def email
+
+ @Shared
+ private def firstname
+
+ @Shared
+ private def lastname
+
+ @Shared
+ private def principal
+
+ private def createGroupSearchContextOneUser() {
+
+ def umSearchResult = Mock(SearchResult)
+ umSearchResult.getName() >> principal;
+ umSearchResult.getAttributes() >> principal
+
+ def uniqueMembers = new BasicNamingEnumerationImpl()
+ uniqueMembers.add(umSearchResult);
+ def attributes = Mock(Attributes)
+ def uniqueMemberAttribute = Mock(Attribute)
+ uniqueMemberAttribute.getId() >> "uniquemember"
+ uniqueMemberAttribute.getAll() >> uniqueMembers
+ attributes.get("uniquemember") >> uniqueMemberAttribute
+
+ def groupSearchResult = Mock(SearchResult)
+ groupSearchResult.getName() >> principal;
+ groupSearchResult.getAttributes() >> attributes
+
+ def searchGroupResults = new BasicNamingEnumerationImpl()
+ searchGroupResults.add(groupSearchResult);
+
+ attributes = createUserAttributes(username, email, firstname, lastname)
+ SearchResult userSearchResult = createSearchResult(attributes)
+ def searchUsersResults = new BasicNamingEnumerationImpl()
+ searchUsersResults.add(userSearchResult);
+
+ def context = Mock(LdapContext)
+ context.search(_, _, _) >>> [searchGroupResults, searchUsersResults, searchGroupResults, new BasicNamingEnumerationImpl()];
+
+ return context
+ }
+
+ private def createGroupSearchContextNoUser() {
+
+ def umSearchResult = Mock(SearchResult)
+ umSearchResult.getName() >> principal;
+ umSearchResult.getAttributes() >> principal
+
+ def uniqueMembers = new BasicNamingEnumerationImpl()
+ uniqueMembers.add(umSearchResult);
+ def attributes = Mock(Attributes)
+ def uniqueMemberAttribute = Mock(Attribute)
+ uniqueMemberAttribute.getId() >> "uniquemember"
+ uniqueMemberAttribute.getAll() >> uniqueMembers
+ attributes.get("uniquemember") >> uniqueMemberAttribute
+
+ def groupSearchResult = Mock(SearchResult)
+ groupSearchResult.getName() >> principal;
+ groupSearchResult.getAttributes() >> attributes
+
+ def searchGroupResults = new BasicNamingEnumerationImpl()
+ searchGroupResults.add(groupSearchResult);
+
+ def context = Mock(LdapContext)
+ context.search(_, _, _) >>> [searchGroupResults, new BasicNamingEnumerationImpl()];
+
+ return context
+ }
+
+ private def createContext() {
+ Attributes attributes = createUserAttributes(username, email, firstname, lastname)
+ SearchResult searchResults = createSearchResult(attributes)
+ def searchUsersResults = new BasicNamingEnumerationImpl()
+ searchUsersResults.add(searchResults);
+
+ def context = Mock(LdapContext)
+ context.search(_, _, _) >> searchUsersResults;
+
+ return context
+ }
+
+ private SearchResult createSearchResult(attributes) {
+ def search = Mock(SearchResult)
+
+ search.getName() >> "cn=" + attributes.getAt("uid").get();
+
+ search.getAttributes() >> attributes
+ search.getNameInNamespace() >> principal
+
+ return search
+ }
+
+ private Attributes createUserAttributes(String username, String email, String firstname, String lastname) {
+ def attributes = Mock(Attributes)
+
+ def nameAttribute = Mock(Attribute)
+ nameAttribute.getId() >> "uid"
+ nameAttribute.get() >> username
+ attributes.get("uid") >> nameAttribute
+
+ def mailAttribute = Mock(Attribute)
+ mailAttribute.getId() >> "mail"
+ mailAttribute.get() >> email
+ attributes.get("mail") >> mailAttribute
+
+ def givennameAttribute = Mock(Attribute)
+ givennameAttribute.getId() >> "givenname"
+ givennameAttribute.get() >> firstname
+ attributes.get("givenname") >> givennameAttribute
+
+ def snAttribute = Mock(Attribute)
+ snAttribute.getId() >> "sn"
+ snAttribute.get() >> lastname
+ attributes.get("sn") >> snAttribute
+
+ return attributes
+ }
+
+ def setupSpec() {
+ ldapConfiguration = Mock(LdapConfiguration)
+
+ ldapConfiguration.getScope() >> SearchControls.SUBTREE_SCOPE
+ ldapConfiguration.getReturnAttributes() >> ["uid", "mail", "cn"]
+ ldapConfiguration.getUsernameAttribute() >> "uid"
+ ldapConfiguration.getEmailAttribute() >> "mail"
+ ldapConfiguration.getFirstnameAttribute() >> "givenname"
+ ldapConfiguration.getLastnameAttribute() >> "sn"
+ ldapConfiguration.getBaseDn(_) >> "dc=cloudstack,dc=org"
+ ldapConfiguration.getCommonNameAttribute() >> "cn"
+ ldapConfiguration.getGroupObject() >> "groupOfUniqueNames"
+ ldapConfiguration.getGroupUniqueMemberAttribute(_) >> "uniquemember"
+ ldapConfiguration.getLdapPageSize() >> 1
+ ldapConfiguration.getReadTimeout(_) >> 1000
+
+ username = "rmurphy"
+ email = "rmurphy@test.com"
+ firstname = "Ryan"
+ lastname = "Murphy"
+ principal = "cn=" + username + "," + ldapConfiguration.getBaseDn()
+ }
+
+ def "Test successfully creating an Ldap User from Search result"() {
+ given: "We have attributes, a search and a user manager"
+ def attributes = createUserAttributes(username, email, firstname, lastname)
+ def search = createSearchResult(attributes)
+ def userManager = new OpenLdapUserManagerImpl(ldapConfiguration)
+ def result = userManager.createUser(search,)
+
+ expect: "The crated user the data supplied from LDAP"
+
+ result.username == username
+ result.email == email
+ result.firstname == firstname
+ result.lastname == lastname
+ result.principal == principal
+ }
+
+ def "Test successfully returning a list from get users"() {
+ given: "We have a LdapUserManager"
+
+ def userManager = new OpenLdapUserManagerImpl(ldapConfiguration)
+
+ when: "A request for users is made"
+ def result = userManager.getUsers(username, createContext())
+
+ then: "A list of users is returned"
+ result.size() == 1
+ }
+
+ def "Test successfully returning a list from get users when no username is given"() {
+ given: "We have a LdapUserManager"
+
+ def userManager = new OpenLdapUserManagerImpl(ldapConfiguration)
+
+ when: "Get users is called without a username"
+ def result = userManager.getUsers(createContext())
+
+ then: "All users are returned"
+ result.size() == 1
+ }
+
+ def "Test successfully returning a ldap user from searchUsers"() {
+ given: "We have a LdapUserManager"
+ def userManager = new OpenLdapUserManagerImpl(ldapConfiguration)
+
+ when: "We search for users"
+ def result = userManager.searchUsers(createContext())
+
+ then: "A list of users are returned."
+ result.first().getPrincipal() == principal
+ }
+
+ def "Test successfully returning an Ldap user from a get user request"() {
+ given: "We have a LdapUserMaanger"
+
+ def userManager = new OpenLdapUserManagerImpl(ldapConfiguration)
+
+ when: "A request for a user is made"
+ def result = userManager.getUser(username, createContext())
+
+ then: "The user is returned"
+ result.username == username
+ result.email == email
+ result.firstname == firstname
+ result.lastname == lastname
+ result.principal == principal
+ }
+
+ def "Test successfully throwing an exception when no users are found with getUser"() {
+ given: "We have a seachResult of users and a User Manager"
+
+ def searchUsersResults = new BasicNamingEnumerationImpl()
+
+ def context = Mock(LdapContext)
+ context.search(_, _, _) >> searchUsersResults;
+
+ def userManager = new OpenLdapUserManagerImpl(ldapConfiguration)
+
+ when: "a get user request is made and no user is found"
+ def result = userManager.getUser(username, context)
+
+ then: "An exception is thrown."
+ thrown NamingException
+ }
+
+ def "Test that a newly created Ldap User Manager is not null"() {
+ given: "You have created a new Ldap user manager object"
+ def result = new OpenLdapUserManagerImpl();
+ expect: "The result is not null"
+ result != null
+ }
+
+ def "test successful generateGroupSearchFilter"() {
+ given: "ldap user manager and ldap config"
+ def ldapUserManager = new OpenLdapUserManagerImpl(ldapConfiguration)
+ def groupName = varGroupName == null ? "*" : varGroupName
+ def expectedResult = "(&(objectClass=groupOfUniqueNames)(cn=" + groupName + "))";
+
+ def result = ldapUserManager.generateGroupSearchFilter(varGroupName)
+ expect:
+ result == expectedResult
+ where: "The group name passed is set to "
+ varGroupName << ["", null, "Murphy"]
+ }
+
+ def "test successful getUsersInGroup one user"() {
+ given: "ldap user manager and ldap config"
+ def ldapUserManager = new OpenLdapUserManagerImpl(ldapConfiguration)
+
+ when: "A request for users is made"
+ def result = ldapUserManager.getUsersInGroup("engineering", createGroupSearchContextOneUser(),)
+ then: "one user is returned"
+ result.size() == 1
+ }
+
+ def "test successful getUsersInGroup no user"() {
+ given: "ldap user manager and ldap config"
+ def ldapUserManager = new OpenLdapUserManagerImpl(ldapConfiguration)
+
+ when: "A request for users is made"
+ def result = ldapUserManager.getUsersInGroup("engineering", createGroupSearchContextNoUser(),)
+ then: "no user is returned"
+ result.size() == 0
+ }
+
+ def "test successful getUserForDn"() {
+ given: "ldap user manager and ldap config"
+ def ldapUserManager = new OpenLdapUserManagerImpl(ldapConfiguration)
+
+ when: "A request for users is made"
+ def result = ldapUserManager.getUserForDn("cn=Ryan Murphy,ou=engineering,dc=cloudstack,dc=org", createContext())
+ then: "A list of users is returned"
+ result != 1
+ result.username == username
+ result.email == email
+ result.firstname == firstname
+ result.lastname == lastname
+ result.principal == principal
+
+ }
+
+ def "test searchUsers when ldap basedn in not set"() {
+ given: "ldap configuration where basedn is not set"
+ def ldapconfig = Mock(LdapConfiguration)
+ ldapconfig.getBaseDn() >> null
+ def ldapUserManager = new OpenLdapUserManagerImpl(ldapconfig)
+
+ when: "A request for search users is made"
+ def result = ldapUserManager.searchUsers(new InitialLdapContext())
+
+ then: "An exception with no basedn defined is returned"
+ def e = thrown(IllegalArgumentException)
+ e.message == "ldap basedn is not configured"
+ }
+}
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..d8c0c7af561
--- /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;
+
+class 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
+ */
+ protected 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
+ */
+ protected 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..f901f7778fa
--- /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 extends 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..e8b35a88fd7
--- /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 extends 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..b4a89baf669
--- /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 extends 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..cc6479e2399
--- /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 extends 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/pom.xml b/pom.xml
index aaa9ac5076f..bc37d151467 100644
--- a/pom.xml
+++ b/pom.xml
@@ -82,8 +82,13 @@
1.5.3
1.3.22
2.6
- 2.5
+ 3.6
+ 2.6
+ 1.3.3
+ 4.1
+ 1.6
0.9.8
+ 3.22.0-GA
0.10
build/replace.properties
0.5.1
diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java
index 5d78a3d75e0..3370ebb6fb7 100755
--- a/server/src/com/cloud/configuration/Config.java
+++ b/server/src/com/cloud/configuration/Config.java
@@ -38,6 +38,10 @@ import java.util.HashMap;
import java.util.List;
import java.util.StringTokenizer;
+/**
+ * @deprecated use the more dynamic ConfigKey
+ */
+@Deprecated
public enum Config {
// Alert
@@ -1860,42 +1864,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),
@@ -2019,17 +1987,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;
}
@@ -2050,10 +2007,6 @@ public enum Config {
return _type;
}
- public Class> getComponentClass() {
- return _componentClass;
- }
-
public String getScope() {
return _scope;
}
@@ -2121,8 +2074,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 9de88e0ccb5..c2fdd338f10 100755
--- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
+++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
@@ -124,8 +124,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;
@@ -309,6 +311,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
@Inject
AccountDetailsDao _accountDetailsDao;
@Inject
+ DomainDetailsDao _domainDetailsDao;
+ @Inject
PrimaryDataStoreDao _storagePoolDao;
@Inject
NicSecondaryIpDao _nicSecondaryIpDao;
@@ -507,6 +511,22 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
_accountDetailsDao.update(accountDetailVO.getId(), accountDetailVO);
}
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");
}
@@ -616,7 +636,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
Long clusterId = cmd.getClusterId();
Long storagepoolId = cmd.getStoragepoolId();
Long accountId = cmd.getAccountId();
- CallContext.current().setEventDetails(" Name: " + name + " New Value: " + (((name.toLowerCase()).contains("password")) ? "*****" : (((value == null) ? "" : value))));
+ final Long domainId = cmd.getDomainId();
+ CallContext.current().setEventDetails(" Name: " + name + " New Value: " + (name.toLowerCase().contains("password") ? "*****" : value == null ? "" : value));
// check if config value exists
ConfigurationVO config = _configDao.findByName(name);
String catergory = null;
@@ -661,6 +682,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 8eb30210848..b24e9968d36 100755
--- a/server/src/com/cloud/server/ManagementServerImpl.java
+++ b/server/src/com/cloud/server/ManagementServerImpl.java
@@ -221,6 +221,7 @@ import org.apache.cloudstack.api.command.admin.user.EnableUserCmd;
import org.apache.cloudstack.api.command.admin.user.GetUserCmd;
import org.apache.cloudstack.api.command.admin.user.ListUsersCmd;
import org.apache.cloudstack.api.command.admin.user.LockUserCmd;
+import org.apache.cloudstack.api.command.admin.user.MoveUserCmd;
import org.apache.cloudstack.api.command.admin.user.RegisterCmd;
import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd;
import org.apache.cloudstack.api.command.admin.vlan.CreateVlanIpRangeCmd;
@@ -1642,13 +1643,14 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
Filter searchFilter = new Filter(ConfigurationVO.class, "name", true, cmd.getStartIndex(), cmd.getPageSizeVal());
SearchCriteria sc = _configDao.createSearchCriteria();
- Object name = cmd.getConfigName();
- Object category = cmd.getCategory();
- Object keyword = cmd.getKeyword();
- Long zoneId = cmd.getZoneId();
- Long clusterId = cmd.getClusterId();
- Long storagepoolId = cmd.getStoragepoolId();
- Long accountId = cmd.getAccountId();
+ final Object name = cmd.getConfigName();
+ final Object category = cmd.getCategory();
+ final Object keyword = cmd.getKeyword();
+ final Long zoneId = cmd.getZoneId();
+ final Long clusterId = cmd.getClusterId();
+ final Long storagepoolId = cmd.getStoragepoolId();
+ final Long accountId = cmd.getAccountId();
+ final Long domainId = cmd.getDomainId();
String scope = null;
Long id = null;
int paramCountCheck = 0;
@@ -1668,6 +1670,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;
@@ -1719,7 +1726,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
if (configVo != null) {
ConfigKey> key = _configDepot.get(param.getName());
if (key != null) {
- configVo.setValue(key.valueIn(id).toString());
+ configVo.setValue(key.valueIn(id) == null ? null : key.valueIn(id).toString());
configVOList.add(configVo);
} else {
s_logger.warn("ConfigDepot could not find parameter " + param.getName() + " for scope " + scope);
@@ -2680,6 +2687,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
cmdList.add(GetUserCmd.class);
cmdList.add(ListUsersCmd.class);
cmdList.add(LockUserCmd.class);
+ cmdList.add(MoveUserCmd.class);
cmdList.add(RegisterCmd.class);
cmdList.add(UpdateUserCmd.class);
cmdList.add(CreateVlanIpRangeCmd.class);
diff --git a/server/src/com/cloud/user/AccountManager.java b/server/src/com/cloud/user/AccountManager.java
index 2cae32eadff..331f4188ac1 100755
--- a/server/src/com/cloud/user/AccountManager.java
+++ b/server/src/com/cloud/user/AccountManager.java
@@ -22,6 +22,7 @@ import java.util.Map;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.api.command.admin.account.UpdateAccountCmd;
import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd;
+import org.apache.cloudstack.api.command.admin.user.MoveUserCmd;
import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd;
import com.cloud.api.query.vo.ControlledViewEntity;
@@ -152,10 +153,17 @@ public interface AccountManager extends AccountService {
*/
boolean deleteUser(DeleteUserCmd deleteUserCmd);
+ /**
+ * moves a user to another account within the same domain
+ * @param moveUserCmd
+ * @return true if the user was successfully moved
+ */
+ boolean moveUser(MoveUserCmd moveUserCmd);
+
/**
* Update a user by userId
*
- * @param userId
+ * @param cmd
* @return UserAccount object
*/
UserAccount updateUser(UpdateUserCmd cmd);
@@ -197,4 +205,6 @@ public interface AccountManager extends AccountService {
public static final String MESSAGE_ADD_ACCOUNT_EVENT = "Message.AddAccount.Event";
public static final String MESSAGE_REMOVE_ACCOUNT_EVENT = "Message.RemoveAccount.Event";
+
+ 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 dced8a8936a..a641ae5aca5 100755
--- a/server/src/com/cloud/user/AccountManagerImpl.java
+++ b/server/src/com/cloud/user/AccountManagerImpl.java
@@ -16,6 +16,28 @@
// under the License.
package com.cloud.user;
+import java.net.URLEncoder;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import javax.ejb.Local;
+import javax.inject.Inject;
+import javax.naming.ConfigurationException;
+
import com.cloud.api.ApiDBUtils;
import com.cloud.api.query.vo.ControlledViewEntity;
import com.cloud.configuration.Config;
@@ -128,6 +150,7 @@ import org.apache.cloudstack.affinity.AffinityGroup;
import org.apache.cloudstack.affinity.dao.AffinityGroupDao;
import org.apache.cloudstack.api.command.admin.account.UpdateAccountCmd;
import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd;
+import org.apache.cloudstack.api.command.admin.user.MoveUserCmd;
import org.apache.cloudstack.api.command.admin.user.RegisterCmd;
import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd;
import org.apache.cloudstack.context.CallContext;
@@ -142,27 +165,6 @@ import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
-import javax.crypto.KeyGenerator;
-import javax.crypto.Mac;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
-import javax.ejb.Local;
-import javax.inject.Inject;
-import javax.naming.ConfigurationException;
-import java.net.URLEncoder;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-
@Local(value = {AccountManager.class, AccountService.class})
public class AccountManagerImpl extends ManagerBase implements AccountManager, Manager {
public static final Logger s_logger = Logger.getLogger(AccountManagerImpl.class);
@@ -1662,29 +1664,106 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
@Override
@ActionEvent(eventType = EventTypes.EVENT_USER_DELETE, eventDescription = "deleting User")
public boolean deleteUser(DeleteUserCmd deleteUserCmd) {
- long id = deleteUserCmd.getId();
-
- UserVO user = _userDao.findById(id);
-
- if (user == null) {
- throw new InvalidParameterValueException("The specified user doesn't exist in the system");
- }
+ UserVO user = getValidUserVO(deleteUserCmd.getId());
Account account = _accountDao.findById(user.getAccountId());
+ // don't allow to delete the user from the account of type Project
+ checkAccountAndAccess(user, account);
+ return _userDao.remove(deleteUserCmd.getId());
+ }
+
+ @ActionEvent(eventType = EventTypes.EVENT_USER_MOVE, eventDescription = "moving User to a new account")
+ public boolean moveUser(final MoveUserCmd cmd) {
+ final UserVO user = getValidUserVO(cmd.getId());
+ Account oldAccount = _accountDao.findById(user.getAccountId());
+ checkAccountAndAccess(user, oldAccount);
+ long domainId = oldAccount.getDomainId();
+
+ final 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(final UserVO user, final 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) {
+ UserVO newUser = new UserVO(user);
+ user.setExternalEntity(user.getUuid());
+ user.setUuid(UUID.randomUUID().toString());
+ _userDao.update(user.getId(),user);
+ newUser.setAccountId(newAccountId);
+ boolean success = _userDao.remove(user.getId());
+ UserVO persisted = _userDao.persist(newUser);
+ return success && persisted.getUuid().equals(user.getExternalEntity());
+ }
+ });
+ }
+
+ private long getNewAccountId(long domainId, String accountName, Long accountId) {
+ Account newAccount = null;
+ if (StringUtils.isNotBlank(accountName)) {
+ if(s_logger.isDebugEnabled()) {
+ s_logger.debug("Getting id for account by name '" + accountName + "' in domain " + domainId);
+ }
+ newAccount = _accountDao.findEnabledAccount(accountName, domainId);
+ }
+ 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");
+ }
+
+ 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!");
+ }
+ }
+
+ private void checkAccountAndAccess(UserVO user, Account account) {
// don't allow to delete the user from the account of type Project
if (account.getType() == Account.ACCOUNT_TYPE_PROJECT) {
+ throw new InvalidParameterValueException("Project users cannot be deleted or moved.");
+ }
+
+ checkAccess(CallContext.current().getCallingAccount(), AccessType.OperateEntry, true, account);
+ CallContext.current().putContextParameter(User.class, user.getUuid());
+ }
+
+ private UserVO getValidUserVO(long id) {
+ UserVO user = _userDao.findById(id);
+
+ if (user == null || user.getRemoved() != null) {
throw new InvalidParameterValueException("The specified user doesn't exist in the system");
}
// don't allow to delete default user (system and admin users)
if (user.isDefault()) {
- throw new InvalidParameterValueException("The user is default and can't be removed");
+ throw new InvalidParameterValueException("The user is default and can't be (re)moved");
}
- checkAccess(CallContext.current().getCallingAccount(), AccessType.OperateEntry, true, account);
- CallContext.current().putContextParameter(User.class, user.getUuid());
- return _userDao.remove(id);
+ return user;
}
protected class AccountCleanupTask extends ManagedContextRunnable {
@@ -2124,15 +2203,14 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
s_logger.debug("Attempting to log in user: " + username + " in domain " + domainId);
}
UserAccount userAccount = _userAccountDao.getUserAccount(username, domainId);
- if (userAccount == null) {
- s_logger.warn("Unable to find an user with username " + username + " in domain " + domainId);
- return null;
- }
boolean authenticated = false;
HashSet actionsOnFailedAuthenticaion = new HashSet();
- User.Source userSource = userAccount.getSource();
+ User.Source userSource = userAccount != null ? userAccount.getSource() : User.Source.UNKNOWN;
for (UserAuthenticator authenticator : _userAuthenticators) {
+ if(s_logger.isTraceEnabled()) {
+ s_logger.trace("authenticating '" + username + "' (source: '" + userSource + "') with authenticator " + authenticator.getName());
+ }
if(userSource != User.Source.UNKNOWN) {
if(!authenticator.getName().equalsIgnoreCase(userSource.name())){
continue;
@@ -2156,6 +2234,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
if (domain != null) {
domainName = domain.getName();
}
+ userAccount = _userAccountDao.getUserAccount(username, domainId);
if (!userAccount.getState().equalsIgnoreCase(Account.State.enabled.toString()) ||
!userAccount.getAccountState().equalsIgnoreCase(Account.State.enabled.toString())) {
diff --git a/server/src/org/apache/cloudstack/region/RegionManager.java b/server/src/org/apache/cloudstack/region/RegionManager.java
index 6f254817103..f7d7c10c5b9 100644
--- a/server/src/org/apache/cloudstack/region/RegionManager.java
+++ b/server/src/org/apache/cloudstack/region/RegionManager.java
@@ -21,6 +21,7 @@ import java.util.List;
import org.apache.cloudstack.api.command.admin.account.UpdateAccountCmd;
import org.apache.cloudstack.api.command.admin.domain.UpdateDomainCmd;
import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd;
+import org.apache.cloudstack.api.command.admin.user.MoveUserCmd;
import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd;
import com.cloud.domain.Domain;
@@ -122,10 +123,17 @@ public interface RegionManager {
*/
boolean deleteUser(DeleteUserCmd deleteUserCmd);
+ /**
+ * Deletes user by Id
+ * @param moveUserCmd
+ * @return
+ */
+ boolean moveUser(MoveUserCmd moveUserCmd);
+
/**
* update an existing domain
*
- * @param cmd
+ * @param updateDomainCmd
* - the command containing domainId and new domainName
* @return Domain object if the command succeeded
*/
@@ -142,7 +150,7 @@ public interface RegionManager {
/**
* Update a user by userId
*
- * @param userId
+ * @param updateUserCmd
* @return UserAccount object
*/
UserAccount updateUser(UpdateUserCmd updateUserCmd);
@@ -150,7 +158,7 @@ public interface RegionManager {
/**
* Disables a user by userId
*
- * @param userId
+ * @param id
* - the userId
* @return UserAccount object
*/
diff --git a/server/src/org/apache/cloudstack/region/RegionManagerImpl.java b/server/src/org/apache/cloudstack/region/RegionManagerImpl.java
index 89107141966..920c068beda 100755
--- a/server/src/org/apache/cloudstack/region/RegionManagerImpl.java
+++ b/server/src/org/apache/cloudstack/region/RegionManagerImpl.java
@@ -25,6 +25,7 @@ import javax.ejb.Local;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
+import org.apache.cloudstack.api.command.admin.user.MoveUserCmd;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
@@ -226,6 +227,14 @@ public class RegionManagerImpl extends ManagerBase implements RegionManager, Man
return _accountMgr.deleteUser(cmd);
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean moveUser(MoveUserCmd cmd) {
+ return _accountMgr.moveUser(cmd);
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/server/src/org/apache/cloudstack/region/RegionServiceImpl.java b/server/src/org/apache/cloudstack/region/RegionServiceImpl.java
index 98cf5005f89..019e5517605 100755
--- a/server/src/org/apache/cloudstack/region/RegionServiceImpl.java
+++ b/server/src/org/apache/cloudstack/region/RegionServiceImpl.java
@@ -35,6 +35,7 @@ import org.apache.cloudstack.api.command.admin.domain.UpdateDomainCmd;
import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd;
import org.apache.cloudstack.api.command.admin.user.DisableUserCmd;
import org.apache.cloudstack.api.command.admin.user.EnableUserCmd;
+import org.apache.cloudstack.api.command.admin.user.MoveUserCmd;
import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd;
import org.apache.cloudstack.api.command.user.region.ListRegionsCmd;
@@ -153,6 +154,14 @@ public class RegionServiceImpl extends ManagerBase implements RegionService, Man
return _regionMgr.deleteUser(cmd);
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean moveUser(MoveUserCmd cmd) {
+ return _regionMgr.moveUser(cmd);
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/server/test/com/cloud/user/MockAccountManagerImpl.java b/server/test/com/cloud/user/MockAccountManagerImpl.java
index 30aa49c67ef..b62f08c8725 100644
--- a/server/test/com/cloud/user/MockAccountManagerImpl.java
+++ b/server/test/com/cloud/user/MockAccountManagerImpl.java
@@ -22,6 +22,7 @@ import java.util.Map;
import javax.ejb.Local;
import javax.naming.ConfigurationException;
+import org.apache.cloudstack.api.command.admin.user.MoveUserCmd;
import org.springframework.stereotype.Component;
import org.apache.cloudstack.acl.ControlledEntity;
@@ -119,6 +120,16 @@ public class MockAccountManagerImpl extends ManagerBase implements Manager, Acco
return false;
}
+ @Override
+ public boolean moveUser(MoveUserCmd moveUserCmd) {
+ return false;
+ }
+
+ @Override
+ public boolean moveUser(long id, Long domainId, long accountId) {
+ return false;
+ }
+
@Override
public boolean isAdmin(Long accountId) {
// TODO Auto-generated method stub
diff --git a/server/test/org/apache/cloudstack/networkoffering/ChildTestConfiguration.java b/server/test/org/apache/cloudstack/networkoffering/ChildTestConfiguration.java
index 47601440432..6e1716a1053 100644
--- a/server/test/org/apache/cloudstack/networkoffering/ChildTestConfiguration.java
+++ b/server/test/org/apache/cloudstack/networkoffering/ChildTestConfiguration.java
@@ -63,6 +63,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;
@@ -316,6 +317,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/setup/db/db/schema-452to453-cleanup.sql b/setup/db/db/schema-452to453-cleanup.sql
index 7e3d6227505..df5ec494ced 100644
--- a/setup/db/db/schema-452to453-cleanup.sql
+++ b/setup/db/db/schema-452to453-cleanup.sql
@@ -253,3 +253,4 @@ CREATE VIEW `cloud`.`user_view` AS
`cloud`.`async_job` ON async_job.instance_id = user.id
and async_job.instance_type = 'User'
and async_job.job_status = 0;
+
diff --git a/setup/db/db/schema-452to453.sql b/setup/db/db/schema-452to453.sql
index 9b580e62860..d66e30adbca 100644
--- a/setup/db/db/schema-452to453.sql
+++ b/setup/db/db/schema-452to453.sql
@@ -528,3 +528,27 @@ CREATE TABLE IF NOT EXISTS `cloud`.`crl` (
KEY (`serial`),
UNIQUE KEY (`serial`, `cn`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+-- 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) 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;
+
+CREATE TABLE `cloud`.`ldap_trust_map` (
+ `id` int unsigned NOT NULL AUTO_INCREMENT,
+ `domain_id` bigint unsigned NOT NULL,
+ `type` varchar(10) NOT NULL,
+ `name` varchar(255) NOT NULL,
+ `account_type` int(1) unsigned NOT NULL,
+ account_id BIGINT(20) DEFAULT 0,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_ldap_trust_map__bind_location` (`domain_id`, `account_id`),
+ CONSTRAINT `fk_ldap_trust_map__domain_id` FOREIGN KEY (`domain_id`) REFERENCES `domain` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+ALTER TABLE cloud.ldap_configuration ADD COLUMN domain_id BIGINT(20) DEFAULT NULL;
diff --git a/test/integration/smoke/test_accounts.py b/test/integration/smoke/test_accounts.py
new file mode 100644
index 00000000000..dffb00aaef4
--- /dev/null
+++ b/test/integration/smoke/test_accounts.py
@@ -0,0 +1,2225 @@
+# 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.
+""" P1 tests for Account
+"""
+# Import Local Modules
+from marvin.cloudstackTestCase import cloudstackTestCase
+from marvin.lib.utils import (random_gen,
+ cleanup_resources)
+from marvin.cloudstackAPI import *
+from marvin.lib.base import (Domain,
+ Account,
+ ServiceOffering,
+ VirtualMachine,
+ Network,
+ User,
+ NATRule,
+ Template,
+ PublicIPAddress)
+from marvin.lib.common import (get_domain,
+ get_zone,
+ get_test_template,
+ list_accounts,
+ list_virtual_machines,
+ list_service_offering,
+ list_templates,
+ list_users,
+ get_builtin_template_info,
+ wait_for_cleanup)
+from nose.plugins.attrib import attr
+from marvin.cloudstackException import CloudstackAPIException
+import time
+
+from pyVmomi.VmomiSupport import GetVersionFromVersionUri
+
+
+class Services:
+
+ """Test Account Services
+ """
+
+ def __init__(self):
+ self.services = {
+ "domain": {
+ "name": "Domain",
+ },
+ "account": {
+ "email": "test@test.com",
+ "firstname": "Test",
+ "lastname": "User",
+ "username": "test",
+ # Random characters are appended for unique
+ # username
+ "password": "fr3sca",
+ },
+ "user": {
+ "email": "user@test.com",
+ "firstname": "User",
+ "lastname": "User",
+ "username": "User",
+ # Random characters are appended for unique
+ # username
+ "password": "fr3sca",
+ },
+ "service_offering": {
+ "name": "Tiny Instance",
+ "displaytext": "Tiny Instance",
+ "cpunumber": 1,
+ "cpuspeed": 100,
+ # in MHz
+ "memory": 128,
+ # In MBs
+ },
+ "virtual_machine": {
+ "displayname": "Test VM",
+ "username": "root",
+ "password": "password",
+ "ssh_port": 22,
+ "hypervisor": 'XenServer',
+ # Hypervisor type should be same as
+ # hypervisor type of cluster
+ "privateport": 22,
+ "publicport": 22,
+ "protocol": 'TCP',
+ },
+ "template": {
+ "displaytext": "Public Template",
+ "name": "Public template",
+ "ostype": 'CentOS 5.6 (64-bit)',
+ "url": "",
+ "hypervisor": '',
+ "format": '',
+ "isfeatured": True,
+ "ispublic": True,
+ "isextractable": True,
+ "templatefilter": "self"
+ },
+ "natrule": {
+ "publicport": 22,
+ "privateport": 22,
+ "protocol": 'TCP',
+ },
+ "ostype": 'CentOS 5.6 (64-bit)',
+ "sleep": 60,
+ "timeout": 10,
+ }
+
+
+class TestAccounts(cloudstackTestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.testClient = super(TestAccounts, cls).getClsTestClient()
+ cls.api_client = cls.testClient.getApiClient()
+
+ cls.services = Services().services
+ cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests())
+ cls.hypervisor = cls.testClient.getHypervisorInfo()
+ cls.services['mode'] = cls.zone.networktype
+ cls.template = get_test_template(
+ cls.api_client,
+ cls.zone.id,
+ cls.hypervisor
+ )
+ cls.services["virtual_machine"]["zoneid"] = cls.zone.id
+ cls.services["virtual_machine"]["template"] = cls.template.id
+
+ cls.service_offering = ServiceOffering.create(
+ cls.api_client,
+ cls.services["service_offering"]
+ )
+ cls._cleanup = [cls.service_offering]
+ return
+
+ @classmethod
+ def tearDownClass(cls):
+ try:
+ # Cleanup resources used
+ cleanup_resources(cls.api_client, cls._cleanup)
+ except Exception as e:
+ raise Exception("Warning: Exception during cleanup : %s" % e)
+ return
+
+ def setUp(self):
+ self.apiclient = self.testClient.getApiClient()
+ self.dbclient = self.testClient.getDbConnection()
+ self.cleanup = []
+ return
+
+ def tearDown(self):
+ try:
+ # Clean up, terminate the created accounts, domains etc
+ cleanup_resources(self.apiclient, self.cleanup)
+ except Exception as e:
+ raise Exception("Warning: Exception during cleanup : %s" % e)
+ return
+
+ @attr(
+ tags=[
+ "advanced",
+ "basic",
+ "eip",
+ "advancedns",
+ "sg"],
+ required_hardware="false")
+ def test_01_create_account(self):
+ """Test Create Account and user for that account
+ """
+
+ # Validate the following
+ # 1. Create an Account. Verify the account is created.
+ # 2. Create User associated with that account. Verify the created user
+
+ # Create an account
+ account = Account.create(
+ self.apiclient,
+ self.services["account"]
+ )
+ self.debug("Created account: %s" % account.name)
+ self.cleanup.append(account)
+ list_accounts_response = list_accounts(
+ self.apiclient,
+ id=account.id
+ )
+ self.assertEqual(
+ isinstance(list_accounts_response, list),
+ True,
+ "Check list accounts for valid data"
+ )
+ self.assertNotEqual(
+ len(list_accounts_response),
+ 0,
+ "Check List Account response"
+ )
+
+ account_response = list_accounts_response[0]
+ self.assertEqual(
+ account.accounttype,
+ account_response.accounttype,
+ "Check Account Type of Created account"
+ )
+ self.assertEqual(
+ account.name,
+ account_response.name,
+ "Check Account Name of Created account"
+ )
+ # Create an User associated with account
+ user = User.create(
+ self.apiclient,
+ self.services["user"],
+ account=account.name,
+ domainid=account.domainid
+ )
+ self.debug("Created user: %s" % user.id)
+ list_users_response = list_users(
+ self.apiclient,
+ id=user.id
+ )
+ self.assertEqual(
+ isinstance(list_users_response, list),
+ True,
+ "Check list users for valid data"
+ )
+
+ self.assertNotEqual(
+ len(list_users_response),
+ 0,
+ "Check List User response"
+ )
+
+ user_response = list_users_response[0]
+ self.assertEqual(
+ user.username,
+ user_response.username,
+ "Check username of Created user"
+ )
+ self.assertEqual(
+ user.state,
+ user_response.state,
+ "Check state of created user"
+ )
+ self.assertEqual(
+ "native",
+ user_response.usersource,
+ "Check user source of created user"
+ )
+ return
+
+
+class TestRemoveUserFromAccount(cloudstackTestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.testClient = super(
+ TestRemoveUserFromAccount,
+ cls).getClsTestClient()
+ cls.api_client = cls.testClient.getApiClient()
+
+ cls.services = Services().services
+ cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests())
+ cls.hypervisor = cls.testClient.getHypervisorInfo()
+ cls.services['mode'] = cls.zone.networktype
+ cls.template = get_test_template(
+ cls.api_client,
+ cls.zone.id,
+ cls.hypervisor
+ )
+ cls.services["virtual_machine"]["zoneid"] = cls.zone.id
+ cls.services["virtual_machine"]["template"] = cls.template.id
+
+ cls.service_offering = ServiceOffering.create(
+ cls.api_client,
+ cls.services["service_offering"]
+ )
+ # Create an account
+ cls.account = Account.create(
+ cls.api_client,
+ cls.services["account"]
+ )
+
+ cls._cleanup = [cls.account,
+ cls.service_offering,
+ ]
+ return
+
+ @classmethod
+ def tearDownClass(cls):
+ try:
+ # Cleanup resources used
+ cleanup_resources(cls.api_client, cls._cleanup)
+ except Exception as e:
+ raise Exception("Warning: Exception during cleanup : %s" % e)
+ return
+
+ def setUp(self):
+ self.apiclient = self.testClient.getApiClient()
+ self.dbclient = self.testClient.getDbConnection()
+ self.cleanup = []
+ return
+
+ def tearDown(self):
+ try:
+ # Clean up, terminate the created instance, users etc
+ cleanup_resources(self.apiclient, self.cleanup)
+ except Exception as e:
+ raise Exception("Warning: Exception during cleanup : %s" % e)
+ return
+
+ @attr(
+ tags=[
+ "advanced",
+ "basic",
+ "eip",
+ "advancedns",
+ "sg"],
+ required_hardware="false")
+ def test_01_user_remove_VM_running(self):
+ """Test Remove one user from the account
+ """
+
+ # Validate the following
+ # 1. Create an account with 2 users.
+ # 2. Start 2 VMs; one for each user of the account
+ # 3. Remove one user from the account. Verify that account
+ # still exists.
+ # 4. Verify that VM started by the removed user are still running
+
+ # Create an User associated with account and VMs
+ user_1 = User.create(
+ self.apiclient,
+ self.services["user"],
+ account=self.account.name,
+ domainid=self.account.domainid
+ )
+ self.debug("Created user: %s" % user_1.id)
+
+ user_2 = User.create(
+ self.apiclient,
+ self.services["user"],
+ account=self.account.name,
+ domainid=self.account.domainid
+ )
+ self.debug("Created user: %s" % user_2.id)
+ self.cleanup.append(user_2)
+
+ vm_1 = VirtualMachine.create(
+ self.apiclient,
+ self.services["virtual_machine"],
+ accountid=self.account.name,
+ domainid=self.account.domainid,
+ serviceofferingid=self.service_offering.id
+ )
+ self.debug("Deployed VM in account: %s, ID: %s" % (
+ self.account.name,
+ vm_1.id
+ ))
+ self.cleanup.append(vm_1)
+
+ vm_2 = VirtualMachine.create(
+ self.apiclient,
+ self.services["virtual_machine"],
+ accountid=self.account.name,
+ domainid=self.account.domainid,
+ serviceofferingid=self.service_offering.id
+ )
+ self.debug("Deployed VM in account: %s, ID: %s" % (
+ self.account.name,
+ vm_2.id
+ ))
+ self.cleanup.append(vm_2)
+
+ # Remove one of the user
+ self.debug("Deleting user: %s" % user_1.id)
+ user_1.delete(self.apiclient)
+
+ # Account should exist after deleting user
+ accounts_response = list_accounts(
+ self.apiclient,
+ id=self.account.id
+ )
+ self.assertEqual(
+ isinstance(accounts_response, list),
+ True,
+ "Check for valid list accounts response"
+ )
+
+ self.assertNotEqual(
+ len(accounts_response),
+ 0,
+ "Check List Account response"
+ )
+ vm_response = list_virtual_machines(
+ self.apiclient,
+ account=self.account.name,
+ domainid=self.account.domainid
+ )
+ self.assertEqual(
+ isinstance(vm_response, list),
+ True,
+ "Check for valid list VM response"
+ )
+
+ self.assertNotEqual(
+ len(vm_response),
+ 0,
+ "Check List VM response"
+ )
+
+ # VMs associated with that account should be running
+ for vm in vm_response:
+ self.assertEqual(
+ vm.state,
+ 'Running',
+ "Check state of VMs associated with account"
+ )
+ return
+
+
+class TestNonRootAdminsPrivileges(cloudstackTestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.testClient = super(
+ TestNonRootAdminsPrivileges,
+ cls).getClsTestClient()
+ cls.api_client = cls.testClient.getApiClient()
+
+ cls.services = Services().services
+ cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests())
+ cls.services['mode'] = cls.zone.networktype
+ # Create an account, domain etc
+ cls.domain = Domain.create(
+ cls.api_client,
+ cls.services["domain"],
+ )
+ cls.account = Account.create(
+ cls.api_client,
+ cls.services["account"],
+ admin=True,
+ domainid=cls.domain.id
+ )
+ cls._cleanup = [
+ cls.account,
+ cls.domain
+ ]
+ return
+
+ @classmethod
+ def tearDownClass(cls):
+ try:
+ # Cleanup resources used
+ cleanup_resources(cls.api_client, cls._cleanup)
+ except Exception as e:
+ raise Exception("Warning: Exception during cleanup : %s" % e)
+ return
+
+ def setUp(self):
+ self.apiclient = self.testClient.getApiClient()
+ self.dbclient = self.testClient.getDbConnection()
+ self.cleanup = []
+ return
+
+ def tearDown(self):
+ try:
+ # Clean up, terminate the created accounts
+ cleanup_resources(self.apiclient, self.cleanup)
+ except Exception as e:
+ raise Exception("Warning: Exception during cleanup : %s" % e)
+ return
+
+ @attr(
+ tags=[
+ "advanced",
+ "basic",
+ "eip",
+ "advancedns",
+ "sg"],
+ required_hardware="false")
+ def test_01_non_root_admin_Privileges(self):
+ """Test to verify Non Root admin previleges"""
+
+ # Validate the following
+ # 1. Create few accounts/users in ROOT domain
+ # 2. Verify listAccounts API gives only accounts associated with new
+ # domain.
+
+ # Create accounts for ROOT domain
+ account_1 = Account.create(
+ self.apiclient,
+ self.services["account"]
+ )
+ self.debug("Created account: %s" % account_1.name)
+ self.cleanup.append(account_1)
+ account_2 = Account.create(
+ self.apiclient,
+ self.services["account"]
+ )
+ self.debug("Created account: %s" % account_2.name)
+ self.cleanup.append(account_2)
+
+ accounts_response = list_accounts(
+ self.apiclient,
+ domainid=self.domain.id,
+ listall=True
+ )
+
+ self.assertEqual(
+ isinstance(accounts_response, list),
+ True,
+ "Check list accounts response for valid data"
+ )
+
+ self.assertEqual(
+ len(accounts_response),
+ 1,
+ "Check List accounts response"
+ )
+ # Verify only account associated with domain is listed
+ for account in accounts_response:
+ self.assertEqual(
+ account.domainid,
+ self.domain.id,
+ "Check domain ID of account"
+ )
+ return
+
+
+class TestServiceOfferingSiblings(cloudstackTestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.api_client = super(
+ TestServiceOfferingSiblings,
+ cls
+ ).getClsTestClient().getApiClient()
+ cls.services = Services().services
+
+ # Create Domains, accounts etc
+ cls.domain_1 = Domain.create(
+ cls.api_client,
+ cls.services["domain"]
+ )
+ cls.domain_2 = Domain.create(
+ cls.api_client,
+ cls.services["domain"]
+ )
+ cls.service_offering = ServiceOffering.create(
+ cls.api_client,
+ cls.services["service_offering"],
+ domainid=cls.domain_1.id
+ )
+ # Create account for doamin_1
+ cls.account_1 = Account.create(
+ cls.api_client,
+ cls.services["account"],
+ admin=True,
+ domainid=cls.domain_1.id
+ )
+
+ # Create an account for domain_2
+ cls.account_2 = Account.create(
+ cls.api_client,
+ cls.services["account"],
+ admin=True,
+ domainid=cls.domain_2.id
+ )
+
+ cls._cleanup = [
+ cls.account_1,
+ cls.account_2,
+ cls.service_offering,
+ cls.domain_1,
+ cls.domain_2,
+ ]
+ return
+
+ @classmethod
+ def tearDownClass(cls):
+ try:
+ # Cleanup resources used
+ cleanup_resources(cls.api_client, cls._cleanup)
+ except Exception as e:
+ raise Exception("Warning: Exception during cleanup : %s" % e)
+ return
+
+ def setUp(self):
+ self.apiclient = self.testClient.getApiClient()
+ self.dbclient = self.testClient.getDbConnection()
+ self.cleanup = []
+ return
+
+ def tearDown(self):
+ try:
+ # Clean up, terminate the created domains, accounts
+ cleanup_resources(self.apiclient, self.cleanup)
+ except Exception as e:
+ raise Exception("Warning: Exception during cleanup : %s" % e)
+ return
+
+ @attr(
+ tags=[
+ "advanced",
+ "basic",
+ "eip",
+ "advancedns",
+ "sg"],
+ required_hardware="false")
+ def test_01_service_offering_siblings(self):
+ """Test to verify service offerings at same level in hierarchy"""
+
+ # Validate the following
+ # 1. Verify service offering is visible for domain_1
+ # 2. Verify service offering is not visible for domain_2
+
+ service_offerings = list_service_offering(
+ self.apiclient,
+ domainid=self.domain_1.id
+ )
+ self.assertEqual(
+ isinstance(service_offerings, list),
+ True,
+ "Check if valid list service offerings response"
+ )
+
+ self.assertNotEqual(
+ len(service_offerings),
+ 0,
+ "Check List Service Offerings response"
+ )
+
+ for service_offering in service_offerings:
+ self.debug("Validating service offering: %s" % service_offering.id)
+ self.assertEqual(
+ service_offering.id,
+ self.service_offering.id,
+ "Check Service offering ID for domain" +
+ str(self.domain_1.name)
+ )
+ # Verify private service offering is not visible to other domain
+ service_offerings = list_service_offering(
+ self.apiclient,
+ domainid=self.domain_2.id
+ )
+ self.assertEqual(
+ service_offerings,
+ None,
+ "Check List Service Offerings response for other domain"
+ )
+ return
+
+
+class TestServiceOfferingHierarchy(cloudstackTestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.api_client = super(
+ TestServiceOfferingHierarchy,
+ cls
+ ).getClsTestClient().getApiClient()
+ cls.services = Services().services
+
+ # Create domain, service offerings etc
+ cls.domain_1 = Domain.create(
+ cls.api_client,
+ cls.services["domain"]
+ )
+ cls.domain_2 = Domain.create(
+ cls.api_client,
+ cls.services["domain"],
+ parentdomainid=cls.domain_1.id
+ )
+ cls.service_offering = ServiceOffering.create(
+ cls.api_client,
+ cls.services["service_offering"],
+ domainid=cls.domain_1.id
+ )
+ # Create account for doamin_1
+ cls.account_1 = Account.create(
+ cls.api_client,
+ cls.services["account"],
+ admin=True,
+ domainid=cls.domain_1.id
+ )
+
+ # Create an account for domain_2
+ cls.account_2 = Account.create(
+ cls.api_client,
+ cls.services["account"],
+ admin=True,
+ domainid=cls.domain_2.id
+ )
+
+ cls._cleanup = [
+ cls.account_2,
+ cls.domain_2,
+ cls.service_offering,
+ cls.account_1,
+ cls.domain_1,
+ ]
+
+ return
+
+ @classmethod
+ def tearDownClass(cls):
+ try:
+ # Cleanup resources used
+ cleanup_resources(cls.api_client, cls._cleanup)
+ except Exception as e:
+ raise Exception("Warning: Exception during cleanup : %s" % e)
+ return
+
+ def setUp(self):
+ self.apiclient = self.testClient.getApiClient()
+ self.dbclient = self.testClient.getDbConnection()
+ self.cleanup = []
+ return
+
+ def tearDown(self):
+ try:
+ # Clean up, terminate the created instance, volumes and snapshots
+ cleanup_resources(self.apiclient, self.cleanup)
+ except Exception as e:
+ raise Exception("Warning: Exception during cleanup : %s" % e)
+ return
+
+ @attr(
+ tags=[
+ "advanced",
+ "basic",
+ "eip",
+ "advancedns",
+ "sg"],
+ required_hardware="false")
+ def test_01_service_offering_hierarchy(self):
+ """Test to verify service offerings at same level in hierarchy"""
+
+ # Validate the following
+ # 1. Verify service offering is visible for domain_1
+ # 2. Verify service offering is also visible for domain_2
+
+ service_offerings = list_service_offering(
+ self.apiclient,
+ domainid=self.domain_1.id
+ )
+ self.assertEqual(
+ isinstance(service_offerings, list),
+ True,
+ "Check List Service Offerings for a valid response"
+ )
+ self.assertNotEqual(
+ len(service_offerings),
+ 0,
+ "Check List Service Offerings response"
+ )
+
+ for service_offering in service_offerings:
+ self.assertEqual(
+ service_offering.id,
+ self.service_offering.id,
+ "Check Service offering ID for domain" +
+ str(self.domain_1.name)
+ )
+
+ # Verify private service offering is not visible to other domain
+ service_offerings = list_service_offering(
+ self.apiclient,
+ domainid=self.domain_2.id
+ )
+ self.assertEqual(
+ service_offerings,
+ None,
+ "Check List Service Offerings for a valid response"
+ )
+ return
+
+
+class TestTemplateHierarchy(cloudstackTestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.testClient = super(TestTemplateHierarchy, cls).getClsTestClient()
+ cls.api_client = cls.testClient.getApiClient()
+ cls.hypervisor = cls.testClient.getHypervisorInfo()
+
+ cls.services = Services().services
+ cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests())
+ cls.services['mode'] = cls.zone.networktype
+
+ # Create domains, accounts and template
+ cls.domain_1 = Domain.create(
+ cls.api_client,
+ cls.services["domain"]
+ )
+ cls.domain_2 = Domain.create(
+ cls.api_client,
+ cls.services["domain"],
+ parentdomainid=cls.domain_1.id
+ )
+
+ # Create account for doamin_1
+ cls.account_1 = Account.create(
+ cls.api_client,
+ cls.services["account"],
+ admin=True,
+ domainid=cls.domain_1.id
+ )
+
+ # Create an account for domain_2
+ cls.account_2 = Account.create(
+ cls.api_client,
+ cls.services["account"],
+ admin=True,
+ domainid=cls.domain_2.id
+ )
+
+ cls._cleanup = [
+ cls.account_2,
+ cls.domain_2,
+ cls.account_1,
+ cls.domain_1,
+ ]
+
+ builtin_info = get_builtin_template_info(cls.api_client, cls.zone.id)
+ cls.services["template"]["url"] = builtin_info[0]
+ cls.services["template"]["hypervisor"] = builtin_info[1]
+ cls.services["template"]["format"] = builtin_info[2]
+
+ # Register new template
+ cls.template = Template.register(
+ cls.api_client,
+ cls.services["template"],
+ zoneid=cls.zone.id,
+ account=cls.account_1.name,
+ domainid=cls.domain_1.id,
+ hypervisor=cls.hypervisor
+ )
+
+ # Wait for template to download
+ cls.template.download(cls.api_client)
+
+ # Wait for template status to be changed across
+ time.sleep(60)
+ return
+
+ @classmethod
+ def tearDownClass(cls):
+ try:
+ # Cleanup resources used
+ cleanup_resources(cls.api_client, cls._cleanup)
+ except Exception as e:
+ raise Exception("Warning: Exception during cleanup : %s" % e)
+ return
+
+ def setUp(self):
+ self.apiclient = self.testClient.getApiClient()
+ self.dbclient = self.testClient.getDbConnection()
+ self.cleanup = []
+ return
+
+ def tearDown(self):
+ try:
+ # Clean up, terminate the created instance, volumes and snapshots
+ cleanup_resources(self.apiclient, self.cleanup)
+ except Exception as e:
+ raise Exception("Warning: Exception during cleanup : %s" % e)
+ return
+
+ @attr(tags=["advanced", "basic", "eip", "advancedns", "sg"], required_hardware="true")
+ def test_01_template_hierarchy(self):
+ """Test to verify template at same level in hierarchy"""
+
+ # Validate the following
+ # 1. Verify template is visible for domain_1
+ # 2. Verify template is also visible for domain_2
+
+ # Sleep to ensure that template state is reflected across
+
+ templates = list_templates(
+ self.apiclient,
+ templatefilter='self',
+ account=self.account_1.name,
+ domainid=self.domain_1.id
+ )
+ self.assertEqual(
+ isinstance(templates, list),
+ True,
+ "Template response %s is not a list" % templates
+ )
+ self.assertNotEqual(
+ len(templates),
+ 0,
+ "No templates found"
+ )
+
+ for template in templates:
+ self.assertEqual(
+ template.id,
+ self.template.id,
+ "Check Template ID for domain" + str(self.domain_1.name)
+ )
+
+ # Verify private service offering is not visible to other domain
+ templates = list_templates(
+ self.apiclient,
+ id=self.template.id,
+ templatefilter='all',
+ account=self.account_2.name,
+ domainid=self.domain_2.id
+ )
+ self.assertEqual(
+ isinstance(templates, list),
+ True,
+ "Template response %s is not a list" % templates
+ )
+ self.assertNotEqual(
+ len(templates),
+ 0,
+ "No templates found"
+ )
+
+ for template in templates:
+ self.assertEqual(
+ template.id,
+ self.template.id,
+ "Check Template ID for domain" + str(self.domain_2.name)
+ )
+ return
+
+
+class TestAddVmToSubDomain(cloudstackTestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.testClient = super(TestAddVmToSubDomain, cls).getClsTestClient()
+ cls.api_client = cls.testClient.getApiClient()
+
+ cls.services = Services().services
+ cls.domain = get_domain(cls.api_client)
+ cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests())
+ cls.hypervisor = cls.testClient.getHypervisorInfo()
+ cls.services['mode'] = cls.zone.networktype
+ cls.sub_domain = Domain.create(
+ cls.api_client,
+ cls.services["domain"],
+ parentdomainid=cls.domain.id
+ )
+
+ # Create account for doamin_1
+ cls.account_1 = Account.create(
+ cls.api_client,
+ cls.services["account"],
+ admin=True,
+ domainid=cls.domain.id
+ )
+
+ # Create an account for domain_2
+ cls.account_2 = Account.create(
+ cls.api_client,
+ cls.services["account"],
+ admin=True,
+ domainid=cls.sub_domain.id
+ )
+
+ cls.service_offering = ServiceOffering.create(
+ cls.api_client,
+ cls.services["service_offering"],
+ domainid=cls.domain.id
+ )
+
+ cls._cleanup = [
+ cls.account_2,
+ cls.account_1,
+ cls.sub_domain,
+ cls.service_offering
+ ]
+ cls.template = get_test_template(
+ cls.api_client,
+ cls.zone.id,
+ cls.hypervisor
+ )
+ cls.services["virtual_machine"]["zoneid"] = cls.zone.id
+ cls.vm_1 = VirtualMachine.create(
+ cls.api_client,
+ cls.services["virtual_machine"],
+ templateid=cls.template.id,
+ accountid=cls.account_1.name,
+ domainid=cls.account_1.domainid,
+ serviceofferingid=cls.service_offering.id
+ )
+
+ cls.vm_2 = VirtualMachine.create(
+ cls.api_client,
+ cls.services["virtual_machine"],
+ templateid=cls.template.id,
+ accountid=cls.account_2.name,
+ domainid=cls.account_2.domainid,
+ serviceofferingid=cls.service_offering.id
+ )
+ return
+
+ @classmethod
+ def tearDownClass(cls):
+ try:
+ # Clean up, terminate the created resources
+ cleanup_resources(cls.api_client, cls._cleanup)
+ except Exception as e:
+ raise Exception("Warning: Exception during cleanup : %s" % e)
+ return
+
+ def setUp(self):
+ self.apiclient = self.testClient.getApiClient()
+ self.dbclient = self.testClient.getDbConnection()
+ self.cleanup = []
+ return
+
+ def tearDown(self):
+ try:
+ # Clean up, terminate the created resources
+ cleanup_resources(self.apiclient, self.cleanup)
+ except Exception as e:
+ raise Exception("Warning: Exception during cleanup : %s" % e)
+ return
+
+ @attr(
+ tags=[
+ "advanced",
+ "basic",
+ "eip",
+ "advancedns",
+ "sg"],
+ required_hardware="false")
+ def test_01_add_vm_to_subdomain(self):
+ """ Test Sub domain allowed to launch VM when a Domain
+ level zone is created"""
+
+ # Validate the following
+ # 1. Verify VM created by Account_1 is in Running state
+ # 2. Verify VM created by Account_2 is in Running state
+
+ vm_response = list_virtual_machines(
+ self.apiclient,
+ id=self.vm_1.id
+ )
+ self.assertEqual(
+ isinstance(vm_response, list),
+ True,
+ "Check List VM for a valid response"
+ )
+ self.assertNotEqual(
+ len(vm_response),
+ 0,
+ "Check List Template response"
+ )
+
+ for vm in vm_response:
+ self.debug("VM ID: %s and state: %s" % (vm.id, vm.state))
+ self.assertEqual(
+ vm.state,
+ 'Running',
+ "Check State of Virtual machine"
+ )
+
+ vm_response = list_virtual_machines(
+ self.apiclient,
+ id=self.vm_2.id
+ )
+ self.assertNotEqual(
+ len(vm_response),
+ 0,
+ "Check List Template response"
+ )
+
+ for vm in vm_response:
+ self.debug("VM ID: %s and state: %s" % (vm.id, vm.state))
+ self.assertEqual(
+ vm.state,
+ 'Running',
+ "Check State of Virtual machine"
+ )
+ return
+
+
+class TestUserDetails(cloudstackTestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.testClient = super(TestUserDetails, cls).getClsTestClient()
+ cls.api_client = cls.testClient.getApiClient()
+
+ cls.services = Services().services
+ cls.domain = get_domain(cls.api_client)
+ cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests())
+ cls.services['mode'] = cls.zone.networktype
+ cls._cleanup = []
+ return
+
+ @classmethod
+ def tearDownClass(cls):
+ try:
+ # Cleanup resources used
+ cleanup_resources(cls.api_client, cls._cleanup)
+ except Exception as e:
+ raise Exception("Warning: Exception during cleanup : %s" % e)
+ return
+
+ def setUp(self):
+ self.apiclient = self.testClient.getApiClient()
+ self.dbclient = self.testClient.getDbConnection()
+ self.cleanup = []
+ return
+
+ def tearDown(self):
+ try:
+ # Clean up, terminate the created network offerings
+ cleanup_resources(self.apiclient, self.cleanup)
+ except Exception as e:
+ raise Exception("Warning: Exception during cleanup : %s" % e)
+ return
+
+ @attr(tags=[
+ "role",
+ "accounts",
+ "simulator",
+ "advanced",
+ "advancedns",
+ "basic",
+ "eip",
+ "sg"
+ ])
+ def test_updateUserDetails(self):
+ """Test user update API
+ """
+
+ # Steps for test scenario
+ # 1. create a user account
+ # 2. update the user details (firstname, lastname, user) with
+ # updateUser API
+ # 3. listUsers in the account
+ # 4. delete the account
+ # Validate the following
+ # 1. listAccounts should show account created successfully
+ # 2. updateUser API should return valid response
+ # 3. user should be updated with new details
+
+ self.debug("Creating an user account..")
+ self.account = Account.create(
+ self.apiclient,
+ self.services["account"],
+ domainid=self.domain.id
+ )
+ self.cleanup.append(self.account)
+
+ # Fetching the user details of account
+ self.debug(
+ "Fetching user details for account: %s" %
+ self.account.name)
+ users = User.list(
+ self.apiclient,
+ account=self.account.name,
+ domainid=self.account.domainid
+ )
+ self.assertEqual(
+ isinstance(users, list),
+ True,
+ "List users should return a valid list for account"
+ )
+ user_1 = users[0]
+ self.debug("Updating the details of user: %s" % user_1.name)
+ firstname = random_gen()
+ lastname = random_gen()
+
+ self.debug("New firstname: %s, lastname: %s" % (firstname, lastname))
+ User.update(
+ self.apiclient,
+ user_1.id,
+ firstname=firstname,
+ lastname=lastname
+ )
+
+ # Fetching the user details of account
+ self.debug(
+ "Fetching user details for user: %s" % user_1.name)
+ users = User.list(
+ self.apiclient,
+ id=user_1.id,
+ listall=True
+ )
+
+ self.assertEqual(
+ isinstance(users, list),
+ True,
+ "List users should return a valid list for account"
+ )
+ user_1 = users[0]
+ self.assertEqual(
+ user_1.firstname,
+ firstname,
+ "User's first name should be updated with new one"
+ )
+ self.assertEqual(
+ user_1.lastname,
+ lastname,
+ "User's last name should be updated with new one"
+ )
+ return
+
+ @attr(tags=[
+ "role",
+ "accounts",
+ "simulator",
+ "advanced",
+ "advancedns",
+ "basic",
+ "eip",
+ "sg"
+ ])
+ def test_updateAdminDetails(self):
+ """Test update admin details
+ """
+
+ # Steps for test scenario
+ # 1. create a admin account
+ # 2. update the user details (firstname, lastname, user) with
+ # updateUser API
+ # 3. listUsers in the account
+ # 4. delete the account
+ # Validate the following
+ # 1. listAccounts should show account created successfully
+ # 2. updateUser API should return valid response
+ # 3. user should be updated with new details
+
+ self.debug("Creating a ROOT admin account")
+ self.account = Account.create(
+ self.apiclient,
+ self.services["account"],
+ admin=True,
+ )
+ self.cleanup.append(self.account)
+
+ # Fetching the user details of account
+ self.debug(
+ "Fetching user details for account: %s" %
+ self.account.name)
+ users = User.list(
+ self.apiclient,
+ account=self.account.name,
+ domainid=self.account.domainid
+ )
+ self.assertEqual(
+ isinstance(users, list),
+ True,
+ "List users should return a valid list for account"
+ )
+ user_1 = users[0]
+ self.debug("Updating the details of user: %s" % user_1.name)
+ firstname = random_gen()
+ lastname = random_gen()
+
+ self.debug("New firstname: %s, lastname: %s" % (firstname, lastname))
+ User.update(
+ self.apiclient,
+ user_1.id,
+ firstname=firstname,
+ lastname=lastname
+ )
+
+ # Fetching the user details of account
+ self.debug(
+ "Fetching user details for user: %s" % user_1.name)
+ users = User.list(
+ self.apiclient,
+ id=user_1.id,
+ listall=True
+ )
+
+ self.assertEqual(
+ isinstance(users, list),
+ True,
+ "List users should return a valid list for account"
+ )
+ user_1 = users[0]
+ self.assertEqual(
+ user_1.firstname,
+ firstname,
+ "User's first name should be updated with new one"
+ )
+ self.assertEqual(
+ user_1.lastname,
+ lastname,
+ "User's last name should be updated with new one"
+ )
+ return
+
+ @attr(tags=[
+ "role",
+ "accounts",
+ "simulator",
+ "advanced",
+ "advancedns",
+ "basic",
+ "eip",
+ "sg"
+ ])
+ def test_updateDomainAdminDetails(self):
+ """Test update domain admin details
+ """
+
+ # Steps for test scenario
+ # 2. update the user details (firstname, lastname, user) with
+ # updateUser API
+ # 3. listUsers in the account
+ # 4. delete the account
+ # Validate the following
+ # 1. listAccounts should show account created successfully
+ # 2. updateUser API should return valid response
+ # 3. user should be updated with new details
+
+ self.debug("Creating a domain admin account")
+ self.account = Account.create(
+ self.apiclient,
+ self.services["account"],
+ admin=True,
+ domainid=self.domain.id
+ )
+ self.cleanup.append(self.account)
+
+ # Fetching the user details of account
+ self.debug(
+ "Fetching user details for account: %s" %
+ self.account.name)
+ users = User.list(
+ self.apiclient,
+ account=self.account.name,
+ domainid=self.account.domainid
+ )
+ self.assertEqual(
+ isinstance(users, list),
+ True,
+ "List users should return a valid list for account"
+ )
+ user_1 = users[0]
+ self.debug("Updating the details of user: %s" % user_1.name)
+ firstname = random_gen()
+ lastname = random_gen()
+
+ self.debug("New firstname: %s, lastname: %s" % (firstname, lastname))
+ User.update(
+ self.apiclient,
+ user_1.id,
+ firstname=firstname,
+ lastname=lastname
+ )
+
+ # Fetching the user details of account
+ self.debug(
+ "Fetching user details for user: %s" % user_1.name)
+ users = User.list(
+ self.apiclient,
+ id=user_1.id,
+ listall=True
+ )
+
+ self.assertEqual(
+ isinstance(users, list),
+ True,
+ "List users should return a valid list for account"
+ )
+ user_1 = users[0]
+ self.assertEqual(
+ user_1.firstname,
+ firstname,
+ "User's first name should be updated with new one"
+ )
+ self.assertEqual(
+ user_1.lastname,
+ lastname,
+ "User's last name should be updated with new one"
+ )
+ return
+
+
+class TestUserLogin(cloudstackTestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.testClient = super(TestUserLogin, cls).getClsTestClient()
+ cls.api_client = cls.testClient.getApiClient()
+
+ cls.services = Services().services
+ cls.domain = get_domain(cls.api_client)
+ cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests())
+ cls.services['mode'] = cls.zone.networktype
+ cls._cleanup = []
+ return
+
+ @classmethod
+ def tearDownClass(cls):
+ try:
+ # Cleanup resources used
+ cleanup_resources(cls.api_client, cls._cleanup)
+ except Exception as e:
+ raise Exception("Warning: Exception during cleanup : %s" % e)
+ return
+
+ def setUp(self):
+ self.apiclient = self.testClient.getApiClient()
+ self.dbclient = self.testClient.getDbConnection()
+ self.cleanup = []
+ return
+
+ def tearDown(self):
+ try:
+ # Clean up, terminate the created network offerings
+ cleanup_resources(self.apiclient, self.cleanup)
+ except Exception as e:
+ raise Exception("Warning: Exception during cleanup : %s" % e)
+ return
+
+ @attr(tags=["login", "accounts", "simulator", "advanced",
+ "advancedns", "basic", "eip", "sg"])
+ def test_LoginApiUuidResponse(self):
+ """Test if Login API does not return UUID's
+ """
+
+ # Steps for test scenario
+ # 1. create a user account
+ # 2. login to the user account with given credentials (loginCmd)
+ # 3. delete the user account
+ # Validate the following
+ # 1. listAccounts should return account created
+ # 2. loginResponse should have UUID only is response. Assert by
+ # checking database id is not same as response id
+ # Login also succeeds with non NULL sessionId in response
+
+ self.debug("Creating an user account..")
+ self.account = Account.create(
+ self.apiclient,
+ self.services["account"],
+ domainid=self.domain.id
+ )
+ self.cleanup.append(self.account)
+
+ self.debug("Logging into the cloudstack with login API")
+ respose = User.login(
+ self.apiclient,
+ username=self.account.name,
+ password=self.services["account"]["password"]
+ )
+
+ self.debug("Login API response: %s" % respose)
+
+ self.assertNotEqual(
+ respose.sessionkey,
+ None,
+ "Login to the CloudStack should be successful" +
+ "response shall have non Null key"
+ )
+ return
+
+ @attr(tags=["login", "accounts", "simulator", "advanced",
+ "advancedns", "basic", "eip", "sg"])
+ def test_LoginApiDomain(self):
+ """Test login API with domain
+ """
+
+ # Steps for test scenario
+ # 1. create a domain
+ # 2. create user in the domain
+ # 3. login to the user account above using UUID domain/user
+ # 4. delete the user account
+ # Validate the following
+ # 1. listDomains returns created domain
+ # 2. listAccounts returns created user
+ # 3. loginResponse should have UUID only in responses
+ # Login also succeeds with non NULL sessionId in response
+
+ self.debug("Creating a domain for login with API domain test")
+ domain = Domain.create(
+ self.apiclient,
+ self.services["domain"],
+ parentdomainid=self.domain.id
+ )
+ self.debug("Domain: %s is created succesfully." % domain.name)
+ self.debug(
+ "Checking if the created domain is listed in list domains API")
+ domains = Domain.list(self.apiclient, id=domain.id, listall=True)
+
+ self.assertEqual(
+ isinstance(domains, list),
+ True,
+ "List domains shall return a valid response"
+ )
+ self.debug("Creating an user account in domain: %s" % domain.name)
+ self.account = Account.create(
+ self.apiclient,
+ self.services["account"],
+ domainid=domain.id
+ )
+ self.cleanup.append(self.account)
+
+ accounts = Account.list(
+ self.apiclient,
+ name=self.account.name,
+ domainid=self.account.domainid,
+ listall=True
+ )
+
+ self.assertEqual(
+ isinstance(accounts, list),
+ True,
+ "List accounts should return a valid response"
+ )
+
+ self.debug("Logging into the cloudstack with login API")
+ respose = User.login(
+ self.apiclient,
+ username=self.account.name,
+ password=self.services["account"]["password"],
+ domainid=domain.id)
+ self.debug("Login API response: %s" % respose)
+
+ self.assertNotEqual(
+ respose.sessionkey,
+ None,
+ "Login to the CloudStack should be successful" +
+ "response shall have non Null key"
+ )
+ return
+
+
+class TestUserAPIKeys(cloudstackTestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.testClient = super(TestUserAPIKeys, cls).getClsTestClient()
+ cls.api_client = cls.testClient.getApiClient()
+
+ cls.services = Services().services
+ cls.domain = get_domain(cls.api_client)
+ cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests())
+ cls.services['mode'] = cls.zone.networktype
+ # Create an account, domain etc
+ cls.domain = Domain.create(
+ cls.api_client,
+ cls.services["domain"],
+ )
+ cls.account = Account.create(
+ cls.api_client,
+ cls.services["account"],
+ admin=False,
+ domainid=cls.domain.id
+ )
+ cls.domain_2 = Domain.create(
+ cls.api_client,
+ cls.services["domain"],
+ )
+ cls.account_2 = Account.create(
+ cls.api_client,
+ cls.services["account"],
+ admin=False,
+ domainid=cls.domain_2.id
+ )
+ cls._cleanup = [
+ cls.account,
+ cls.domain,
+ cls.account_2,
+ cls.domain_2
+ ]
+ return
+
+ @classmethod
+ def tearDownClass(cls):
+ try:
+ # Cleanup resources used
+ cleanup_resources(cls.api_client, cls._cleanup)
+ except Exception as e:
+ raise Exception("Warning: Exception during cleanup : %s" % e)
+ return
+
+ def setUp(self):
+ self.apiclient = self.testClient.getApiClient()
+ self.dbclient = self.testClient.getDbConnection()
+ self.cleanup = []
+ return
+
+ def tearDown(self):
+ try:
+ # Clean up, terminate the created network offerings
+ cleanup_resources(self.apiclient, self.cleanup)
+ except Exception as e:
+ raise Exception("Warning: Exception during cleanup : %s" % e)
+ return
+
+ @attr(tags=[
+ "role",
+ "accounts",
+ "simulator",
+ "advanced",
+ "advancedns",
+ "basic",
+ "eip",
+ "sg"
+ ])
+ def test_user_key_renew_same_account(self):
+ # Create an User associated with the account
+ user_1 = User.create(
+ self.apiclient,
+ self.services["user"],
+ account=self.account.name,
+ domainid=self.domain.id
+ )
+ self.cleanup.append(user_1)
+ account_response = list_accounts(
+ self.apiclient,
+ id=self.account.id
+ )[0]
+ self.assertEqual(
+ hasattr(account_response, 'user'),
+ True,
+ "Users are included in account response")
+
+ account_users = account_response.user
+ self.assertEqual(
+ isinstance(account_users, list),
+ True,
+ "Check account for valid data"
+ )
+ self.assertNotEqual(
+ len(account_users),
+ 0,
+ "Check number of User in Account")
+ [user] = [u for u in account_users if u.username == user_1.username]
+ self.assertEqual(
+ user.apikey,
+ None,
+ "Check that the user don't have an API key yet")
+
+ self.debug("Register API keys for user")
+ userkeys = User.registerUserKeys(self.apiclient, user_1.id)
+ users = list_accounts(
+ self.apiclient,
+ id=self.account.id
+ )[0].user
+ [user] = [u for u in users if u.id == user_1.id]
+ self.assertEqual(
+ user.apikey,
+ userkeys.apikey,
+ "Check User api key")
+ user.secretkey = self.get_secret_key(user.id)
+ self.assertEqual(
+ user.secretkey,
+ userkeys.secretkey,
+ "Check User having secret key")
+
+ self.debug("Get test client with user keys")
+ cs_api = self.testClient.getUserApiClient(
+ UserName=self.account.name,
+ DomainName=self.account.domain)
+ self.debug("Renew API keys for user using current keys")
+ new_keys = User.registerUserKeys(cs_api, user_1.id)
+ self.assertNotEqual(
+ userkeys.apikey,
+ new_keys.apikey,
+ "Check API key is different")
+ new_keys.secretkey = self.get_secret_key(user_1.id)
+ self.assertNotEqual(
+ userkeys.secretkey,
+ new_keys.secretkey,
+ "Check secret key is different")
+
+ def get_secret_key(self, id):
+ cmd = getUserKeys.getUserKeysCmd()
+ cmd.id = id
+ keypair = self.apiclient.getUserKeys(cmd)
+ return keypair.secretkey
+
+ @attr(tags=[
+ "role",
+ "accounts",
+ "simulator",
+ "advanced",
+ "advancedns",
+ "basic",
+ "eip",
+ "sg"
+ ])
+ def test_user_cannot_renew_other_keys(self):
+ cs_api = self.testClient.getUserApiClient(
+ UserName=self.account.name,
+ DomainName=self.account.domain)
+ self.debug("Try to change API key of an account in another domain")
+ users = list_accounts(
+ self.apiclient,
+ id=self.account_2.id
+ )[0].user
+ with self.assertRaises(CloudstackAPIException) as e:
+ User.registerUserKeys(cs_api, users[0].id)
+
+
+class TestDomainForceRemove(cloudstackTestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.testClient = super(TestDomainForceRemove, cls).getClsTestClient()
+ cls.api_client = cls.testClient.getApiClient()
+
+ cls.services = Services().services
+ cls.domain = get_domain(cls.api_client)
+ cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests())
+ cls.hypervisor = cls.testClient.getHypervisorInfo()
+ cls.services['mode'] = cls.zone.networktype
+
+ cls.template = get_test_template(
+ cls.api_client,
+ cls.zone.id,
+ cls.hypervisor
+ )
+
+ cls.services["virtual_machine"]["zoneid"] = cls.zone.id
+ cls._cleanup = []
+ return
+
+ @classmethod
+ def tearDownClass(cls):
+ try:
+ # Clean up, terminate the created resources
+ cleanup_resources(cls.api_client, cls._cleanup)
+ except Exception as e:
+
+ raise Exception("Warning: Exception during cleanup : %s" % e)
+ return
+
+ def setUp(self):
+ self.apiclient = self.testClient.getApiClient()
+ self.dbclient = self.testClient.getDbConnection()
+ self.cleanup = []
+ return
+
+ def tearDown(self):
+ try:
+ # Clean up, terminate the created resources
+ cleanup_resources(self.apiclient, self.cleanup)
+ except Exception as e:
+ raise Exception("Warning: Exception during cleanup : %s" % e)
+ return
+
+ @attr(
+ tags=[
+ "domains",
+ "advanced",
+ "advancedns",
+ "simulator",
+ "dvs"],
+ required_hardware="false")
+ def test_forceDeleteDomain(self):
+ """ Test delete domain with force option"""
+
+ # Steps for validations
+ # 1. create a domain DOM
+ # 2. create 2 users under this domain
+ # 3. deploy 1 VM into each of these user accounts
+ # 4. create PF / FW rules for port 22 on these VMs for their
+ # respective accounts
+ # 5. delete the domain with force=true option
+ # Validate the following
+ # 1. listDomains should list the created domain
+ # 2. listAccounts should list the created accounts
+ # 3. listvirtualmachines should show the Running VMs
+ # 4. PF and FW rules should be shown in listFirewallRules
+ # 5. domain should delete successfully and above three list calls
+ # should show all the resources now deleted. listRouters should
+ # not return any routers in the deleted accounts/domains
+
+ self.debug("Creating a domain for login with API domain test")
+ domain = Domain.create(
+ self.apiclient,
+ self.services["domain"],
+ parentdomainid=self.domain.id
+ )
+ self.debug("Domain is created succesfully.")
+ self.debug(
+ "Checking if the created domain is listed in list domains API")
+ domains = Domain.list(self.apiclient, id=domain.id, listall=True)
+
+ self.assertEqual(
+ isinstance(domains, list),
+ True,
+ "List domains shall return a valid response"
+ )
+ self.debug("Creating 2 user accounts in domain: %s" % domain.name)
+ self.account_1 = Account.create(
+ self.apiclient,
+ self.services["account"],
+ domainid=domain.id
+ )
+
+ self.account_2 = Account.create(
+ self.apiclient,
+ self.services["account"],
+ domainid=domain.id
+ )
+
+ try:
+ self.debug("Creating a tiny service offering for VM deployment")
+ self.service_offering = ServiceOffering.create(
+ self.apiclient,
+ self.services["service_offering"],
+ domainid=self.domain.id
+ )
+
+ self.debug("Deploying virtual machine in account 1: %s" %
+ self.account_1.name)
+ vm_1 = VirtualMachine.create(
+ self.apiclient,
+ self.services["virtual_machine"],
+ templateid=self.template.id,
+ accountid=self.account_1.name,
+ domainid=self.account_1.domainid,
+ serviceofferingid=self.service_offering.id
+ )
+
+ self.debug("Deploying virtual machine in account 2: %s" %
+ self.account_2.name)
+ VirtualMachine.create(
+ self.apiclient,
+ self.services["virtual_machine"],
+ templateid=self.template.id,
+ accountid=self.account_2.name,
+ domainid=self.account_2.domainid,
+ serviceofferingid=self.service_offering.id
+ )
+
+ networks = Network.list(
+ self.apiclient,
+ account=self.account_1.name,
+ domainid=self.account_1.domainid,
+ listall=True
+ )
+ self.assertEqual(
+ isinstance(networks, list),
+ True,
+ "List networks should return a valid response"
+ )
+ network_1 = networks[0]
+ self.debug("Default network in account 1: %s is %s" % (
+ self.account_1.name,
+ network_1.name))
+ src_nat_list = PublicIPAddress.list(
+ self.apiclient,
+ associatednetworkid=network_1.id,
+ account=self.account_1.name,
+ domainid=self.account_1.domainid,
+ listall=True,
+ issourcenat=True,
+ )
+ self.assertEqual(
+ isinstance(src_nat_list, list),
+ True,
+ "List Public IP should return a valid source NAT"
+ )
+ self.assertNotEqual(
+ len(src_nat_list),
+ 0,
+ "Length of response from listPublicIp should not be 0"
+ )
+
+ src_nat = src_nat_list[0]
+
+ self.debug(
+ "Trying to create a port forwarding rule in source NAT: %s" %
+ src_nat.ipaddress)
+ # Create NAT rule
+ nat_rule = NATRule.create(
+ self.apiclient,
+ vm_1,
+ self.services["natrule"],
+ ipaddressid=src_nat.id
+ )
+ self.debug("Created PF rule on source NAT: %s" % src_nat.ipaddress)
+
+ nat_rules = NATRule.list(self.apiclient, id=nat_rule.id)
+
+ self.assertEqual(
+ isinstance(nat_rules, list),
+ True,
+ "List NAT should return a valid port forwarding rules"
+ )
+
+ self.assertNotEqual(
+ len(nat_rules),
+ 0,
+ "Length of response from listLbRules should not be 0"
+ )
+ except Exception as e:
+ self.clenaup.append(self.account_1)
+ self.cleanup.append(self.account_2)
+ self.fail(e)
+
+ self.debug("Deleting domain with force option")
+ try:
+ domain.delete(self.apiclient, cleanup=True)
+ except Exception as e:
+ self.debug("Waiting for account.cleanup.interval" +
+ " to cleanup any remaining resouces")
+ # Sleep 3*account.gc to ensure that all resources are deleted
+ wait_for_cleanup(self.apiclient, ["account.cleanup.interval"] * 3)
+ with self.assertRaises(CloudstackAPIException):
+ Domain.list(
+ self.apiclient,
+ id=domain.id,
+ listall=True
+ )
+
+ self.debug("Checking if the resources in domain are deleted")
+ with self.assertRaises(CloudstackAPIException):
+ Account.list(
+ self.apiclient,
+ name=self.account_1.name,
+ domainid=self.account_1.domainid,
+ listall=True
+ )
+ return
+
+ @attr(
+ tags=[
+ "domains",
+ "advanced",
+ "advancedns",
+ "simulator"],
+ required_hardware="false")
+ def test_DeleteDomain(self):
+ """ Test delete domain without force option"""
+
+ # Steps for validations
+ # 1. create a domain DOM
+ # 2. create 2 users under this domain
+ # 3. deploy 1 VM into each of these user accounts
+ # 4. create PF / FW rules for port 22 on these VMs for their
+ # respective accounts
+ # 5. delete the domain with force=false option
+ # Validate the following
+ # 1. listDomains should list the created domain
+ # 2. listAccounts should list the created accounts
+ # 3. listvirtualmachines should show the Running VMs
+ # 4. PF and FW rules should be shown in listFirewallRules
+ # 5. domain deletion should fail saying there are resources under use
+
+ self.debug("Creating a domain for login with API domain test")
+ domain = Domain.create(
+ self.apiclient,
+ self.services["domain"],
+ parentdomainid=self.domain.id
+ )
+ self.debug("Domain: %s is created successfully." % domain.name)
+ self.debug(
+ "Checking if the created domain is listed in list domains API")
+ domains = Domain.list(self.apiclient, id=domain.id, listall=True)
+
+ self.assertEqual(
+ isinstance(domains, list),
+ True,
+ "List domains shall return a valid response"
+ )
+ self.debug("Creating 2 user accounts in domain: %s" % domain.name)
+ self.account_1 = Account.create(
+ self.apiclient,
+ self.services["account"],
+ domainid=domain.id
+ )
+ self.cleanup.append(self.account_1)
+
+ self.account_2 = Account.create(
+ self.apiclient,
+ self.services["account"],
+ domainid=domain.id
+ )
+ self.cleanup.append(self.account_2)
+
+ self.debug("Creating a tiny service offering for VM deployment")
+ self.service_offering = ServiceOffering.create(
+ self.apiclient,
+ self.services["service_offering"],
+ domainid=self.domain.id
+ )
+ self.cleanup.append(self.service_offering)
+
+ self.debug("Deploying virtual machine in account 1: %s" %
+ self.account_1.name)
+ vm_1 = VirtualMachine.create(
+ self.apiclient,
+ self.services["virtual_machine"],
+ templateid=self.template.id,
+ accountid=self.account_1.name,
+ domainid=self.account_1.domainid,
+ serviceofferingid=self.service_offering.id
+ )
+
+ self.debug("Deploying virtual machine in account 2: %s" %
+ self.account_2.name)
+ VirtualMachine.create(
+ self.apiclient,
+ self.services["virtual_machine"],
+ templateid=self.template.id,
+ accountid=self.account_2.name,
+ domainid=self.account_2.domainid,
+ serviceofferingid=self.service_offering.id
+ )
+
+ networks = Network.list(
+ self.apiclient,
+ account=self.account_1.name,
+ domainid=self.account_1.domainid,
+ listall=True
+ )
+ self.assertEqual(
+ isinstance(networks, list),
+ True,
+ "List networks should return a valid response"
+ )
+ network_1 = networks[0]
+ self.debug("Default network in account 1: %s is %s" % (
+ self.account_1.name,
+ network_1.name))
+ src_nat_list = PublicIPAddress.list(
+ self.apiclient,
+ associatednetworkid=network_1.id,
+ account=self.account_1.name,
+ domainid=self.account_1.domainid,
+ listall=True,
+ issourcenat=True,
+ )
+ self.assertEqual(
+ isinstance(src_nat_list, list),
+ True,
+ "List Public IP should return a valid source NAT"
+ )
+ self.assertNotEqual(
+ len(src_nat_list),
+ 0,
+ "Length of response from listPublicIp should not be 0"
+ )
+
+ src_nat = src_nat_list[0]
+
+ self.debug(
+ "Trying to create a port forwarding rule in source NAT: %s" %
+ src_nat.ipaddress)
+ # Create NAT rule
+ nat_rule = NATRule.create(
+ self.apiclient,
+ vm_1,
+ self.services["natrule"],
+ ipaddressid=src_nat.id
+ )
+ self.debug("Created PF rule on source NAT: %s" % src_nat.ipaddress)
+
+ nat_rules = NATRule.list(self.apiclient, id=nat_rule.id)
+
+ self.assertEqual(
+ isinstance(nat_rules, list),
+ True,
+ "List NAT should return a valid port forwarding rules"
+ )
+
+ self.assertNotEqual(
+ len(nat_rules),
+ 0,
+ "Length of response from listLbRules should not be 0"
+ )
+
+ self.debug("Deleting domain without force option")
+ with self.assertRaises(Exception):
+ domain.delete(self.apiclient, cleanup=False)
+ return
+
+class TestMoveUser(cloudstackTestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.testClient = super(TestMoveUser, cls).getClsTestClient()
+ cls.api_client = cls.testClient.getApiClient()
+ cls.testdata = cls.testClient.getParsedTestDataConfig()
+
+ cls.domain = get_domain(cls.api_client)
+ cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests())
+ cls.testdata['mode'] = cls.zone.networktype
+
+ cls.template = get_test_template(
+ cls.api_client,
+ cls.zone.id,
+ cls.testdata["ostype"]
+ )
+
+ cls.testdata["virtual_machine"]["zoneid"] = cls.zone.id
+ cls._cleanup = []
+ return
+
+ @classmethod
+ def tearDownClass(cls):
+ try:
+ # Clean up, terminate the created resources
+ cleanup_resources(cls.api_client, cls._cleanup)
+ except Exception as e:
+
+ raise Exception("Warning: Exception during cleanup : %s" % e)
+ return
+
+ def setUp(self):
+ self.apiclient = self.testClient.getApiClient()
+ self.dbclient = self.testClient.getDbConnection()
+ self.cleanup = []
+ self.testdata = self.testClient.getParsedTestDataConfig()
+ self.account1 = Account.create(
+ self.apiclient,
+ self.testdata["acl"]["accountD1"],
+ domainid=self.domain.id
+ )
+ self.cleanup.append(self.account1)
+
+ self.account2 = Account.create(
+ self.apiclient,
+ self.testdata["acl"]["accountD1A"],
+ domainid=self.domain.id
+ )
+ self.cleanup.append(self.account2)
+
+ self.user = User.create(
+ self.apiclient,
+ self.testdata["user"],
+ account=self.account1.name,
+ domainid=self.account1.domainid
+ )
+
+ return
+
+ def tearDown(self):
+ try:
+ # Clean up, terminate the created resources
+ cleanup_resources(self.apiclient, self.cleanup)
+ except Exception as e:
+ raise Exception("Warning: Exception during cleanup : %s" % e)
+ return
+
+ @attr(tags=["domains", "advanced", "advancedns", "simulator","dvs"], required_hardware="false")
+ def test_move_user_to_accountID(self):
+
+ self.user.move(self.api_client, dest_accountid=self.account2.id)
+
+ self.assertEqual(
+ self.account2.name,
+ self.user.list(self.apiclient, id=self.user.id)[0].account,
+ "Check user source of created user"
+ )
+ return
+
+ @attr(tags=["domains", "advanced", "advancedns", "simulator","dvs"], required_hardware="false")
+ def test_move_user_to_account_name(self):
+
+ self.user.move(self.api_client, dest_account=self.account2.name)
+
+ self.assertEqual(
+ self.account2.name,
+ self.user.list(self.apiclient, id=self.user.id)[0].account,
+ "Check user source of created user"
+ )
+ return
+
+ @attr(tags=["domains", "advanced", "advancedns", "simulator","dvs"], required_hardware="false")
+ def test_move_user_to_different_domain(self):
+ domain2 = Domain.create(self.api_client,
+ self.testdata["domain"],
+ parentdomainid=self.domain.id
+ )
+ self.cleanup.append(domain2)
+
+ account_different_domain = Account.create(
+ self.apiclient,
+ self.testdata["acl"]["accountD1B"],
+ domainid=domain2.id
+ )
+ self.cleanup.append(account_different_domain)
+ try:
+ self.user.move(self.api_client, dest_account=account_different_domain.name)
+ except Exception:
+ pass
+ else:
+ self.fail("It should not be allowed to move users across accounts in different domains, failing")
+
+ account_different_domain.delete(self.api_client)
+ return
+
+ @attr(tags=["domains", "advanced", "advancedns", "simulator","dvs"], required_hardware="false")
+ def test_move_user_incorrect_account_id(self):
+
+ try:
+ self.user.move(self.api_client, dest_accountid='incorrect-account-id')
+ except Exception:
+ pass
+ else:
+ self.fail("moving to non-existing account should not be possible, failing")
+ return
+
+ @attr(tags=["domains", "advanced", "advancedns", "simulator","dvs"], required_hardware="false")
+ def test_move_user_incorrect_account_name(self):
+
+ try:
+ self.user.move(self.api_client, dest_account='incorrect-account-name')
+ except Exception:
+ pass
+ else:
+ self.fail("moving to non-existing account should not be possible, failing")
+ return
diff --git a/tools/marvin/marvin/config/test_data.py b/tools/marvin/marvin/config/test_data.py
index 75609dd1e47..e12bad7fa69 100644
--- a/tools/marvin/marvin/config/test_data.py
+++ b/tools/marvin/marvin/config/test_data.py
@@ -70,6 +70,15 @@ test_data = {
"url": 'http://10.147.60.15/42xescauto spaces/42xesc Clusters',
"clustername": 'VMWare Cluster with Space in DC name',
},
+ "user": {
+ "email": "user@test.com",
+ "firstname": "User",
+ "lastname": "User",
+ "username": "User",
+ # Random characters are appended for unique
+ # username
+ "password": "fr3sca",
+ },
"small": {
"displayname": "testserver",
"username": "root",
diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py
index 63f07232a43..a9c84be6a9e 100755
--- a/tools/marvin/marvin/lib/base.py
+++ b/tools/marvin/marvin/lib/base.py
@@ -268,6 +268,19 @@ class User:
cmd.id = self.id
apiclient.deleteUser(cmd)
+ def move(self, api_client, dest_accountid = None, dest_account = None, domain= None):
+
+ if all([dest_account, dest_accountid]) is None:
+ raise Exception("Please add either destination account or destination account ID.")
+
+ cmd = moveUser.moveUserCmd()
+ cmd.id = self.id
+ cmd.accountid = dest_accountid
+ cmd.account = dest_account
+ cmd.domain = domain
+
+ return api_client.moveUser(cmd)
+
@classmethod
def list(cls, apiclient, **kwargs):
"""Lists users and provides detailed account information for
diff --git a/ui/scripts/domains.js b/ui/scripts/domains.js
index e46f104364d..7872dbd32b0 100644
--- a/ui/scripts/domains.js
+++ b/ui/scripts/domains.js
@@ -341,6 +341,15 @@
}
}
},
+
+ tabFilter: function(args) {
+ var hiddenTabs = [];
+ if(!isAdmin()) {
+ hiddenTabs.push('settings');
+ }
+ return hiddenTabs;
+ },
+
tabs: {
details: {
title: 'label.details',
@@ -522,36 +531,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,
@@ -606,7 +585,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',