diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java
index e86e2d4a995..2728bcc6fef 100644
--- a/api/src/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/org/apache/cloudstack/api/ApiConstants.java
@@ -628,6 +628,8 @@ public class ApiConstants {
public static final String OVM3_CLUSTER = "ovm3cluster";
public static final String OVM3_VIP = "ovm3vip";
+ public static final String ADMIN = "admin";
+
public enum HostDetails {
all, capacity, events, stats, min;
}
diff --git a/client/WEB-INF/classes/resources/messages.properties b/client/WEB-INF/classes/resources/messages.properties
index 492b46ee8b4..49e8b50386e 100644
--- a/client/WEB-INF/classes/resources/messages.properties
+++ b/client/WEB-INF/classes/resources/messages.properties
@@ -2133,6 +2133,10 @@ label.every=Every
label.day=Day
label.of.month=of month
label.add.private.gateway=Add Private Gateway
+label.link.domain.to.ldap=Link Domain to LDAP
+message.link.domain.to.ldap=Enable autosync for this domain in LDAP
+label.ldap.link.type=Type
+label.account.type=Account Type
message.desc.created.ssh.key.pair=Created a SSH Key Pair.
message.please.confirm.remove.ssh.key.pair=Please confirm that you want to remove this SSH Key Pair
message.password.has.been.reset.to=Password has been reset to
diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in
index aec751658b3..473c6354a8f 100644
--- a/client/tomcatconf/commands.properties.in
+++ b/client/tomcatconf/commands.properties.in
@@ -771,6 +771,7 @@ deleteLdapConfiguration=3
listLdapUsers=3
ldapCreateAccount=3
importLdapUsers=3
+linkDomainToLdap=3
#### juniper-contrail commands
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 8ae4009367f..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
@@ -35,5 +35,6 @@
+
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..0ffa8408ce3
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LinkDomainToLdapCmd.java
@@ -0,0 +1,115 @@
+/*
+ * 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.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.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;
+
+ @Override
+ public void execute() throws ServerApiException {
+ try {
+ LinkDomainToLdapResponse response = _ldapManager.linkDomainToLdap(domainId, type, name, accountType);
+ if(admin!=null) {
+ LdapUser ldapUser = null;
+ try {
+ ldapUser = _ldapManager.getUser(admin, type, name);
+ } 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, domainId, admin, 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/LinkDomainToLdapResponse.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LinkDomainToLdapResponse.java
new file mode 100644
index 00000000000..b0032b04b4d
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LinkDomainToLdapResponse.java
@@ -0,0 +1,78 @@
+/*
+ * 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 long domainId;
+
+ @SerializedName(ApiConstants.NAME)
+ @Param(description = "name of the group or OU in LDAP which is linked to the domain")
+ private String name;
+
+ @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(long domainId, String type, String name, short accountType) {
+ this.domainId = domainId;
+ this.name = name;
+ this.type = type;
+ this.accountType = accountType;
+ }
+
+ public long getDomainId() {
+ return domainId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ 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
index 50f1fa00092..5570084c5d4 100644
--- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/ADLdapUserManagerImpl.java
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/ADLdapUserManagerImpl.java
@@ -32,7 +32,8 @@ 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_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) throws NamingException {
@@ -66,7 +67,7 @@ public class ADLdapUserManagerImpl extends OpenLdapUserManagerImpl implements Ld
final StringBuilder memberOfFilter = new StringBuilder();
String groupCnName = _ldapConfiguration.getCommonNameAttribute() + "=" +groupName + "," + _ldapConfiguration.getBaseDn();
- memberOfFilter.append("(" + MICROSOFT_AD_NESTED_MEMBERS_FILTER + ":=");
+ memberOfFilter.append("(").append(getMemberOfAttribute()).append("=");
memberOfFilter.append(groupCnName);
memberOfFilter.append(")");
@@ -79,4 +80,25 @@ public class ADLdapUserManagerImpl extends OpenLdapUserManagerImpl implements Ld
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.valueOf(userAccountControl);
+ // second bit represents disabled user flag in AD
+ if ((control & 2) > 0) {
+ isDisabledUser = true;
+ }
+ }
+ return isDisabledUser;
+ }
+
+ protected String getMemberOfAttribute() {
+ if(_ldapConfiguration.isNestedGroupsEnabled()) {
+ 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..24991c35587 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
@@ -17,6 +17,9 @@
package org.apache.cloudstack.ldap;
import com.cloud.server.auth.DefaultUserAuthenticator;
+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;
@@ -25,6 +28,7 @@ import org.apache.log4j.Logger;
import javax.inject.Inject;
import java.util.Map;
+import java.util.UUID;
public class LdapAuthenticator extends DefaultUserAuthenticator {
private static final Logger s_logger = Logger.getLogger(LdapAuthenticator.class.getName());
@@ -33,6 +37,8 @@ public class LdapAuthenticator extends DefaultUserAuthenticator {
private LdapManager _ldapManager;
@Inject
private UserAccountDao _userAccountDao;
+ @Inject
+ private AccountManager _accountManager;
public LdapAuthenticator() {
super();
@@ -52,21 +58,71 @@ public class LdapAuthenticator extends DefaultUserAuthenticator {
return new Pair(false, null);
}
- final UserAccount user = _userAccountDao.getUserAccount(username, domainId);
+ boolean result = false;
+ ActionOnFailedAuthentication action = null;
- 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) {
+ if (_ldapManager.isLdapEnabled()) {
+ final UserAccount user = _userAccountDao.getUserAccount(username, domainId);
+ LdapTrustMapVO ldapTrustMapVO = _ldapManager.getDomainLinkedToLdap(domainId);
+ if(ldapTrustMapVO != null) {
+ try {
+ LdapUser ldapUser = _ldapManager.getUser(username, ldapTrustMapVO.getType().toString(), ldapTrustMapVO.getName());
+ if(!ldapUser.isDisabled()) {
+ result = _ldapManager.canAuthenticate(ldapUser.getPrincipal(), password);
+ if(result) {
+ if(user == null) {
+ // import user to cloudstack
+ createCloudStackUserAccount(ldapUser, domainId, ldapTrustMapVO.getAccountType());
+ } else {
+ enableUserInCloudStack(user);
+ }
+ }
+ } else {
+ //disable user in cloudstack
+ disableUserInCloudStack(user);
+ }
+ } catch (NoLdapUserMatchingQueryException e) {
+ s_logger.debug(e.getMessage());
+ }
+
+ } else {
+ //domain is not linked to ldap follow normal authentication
+ if(user != null ) {
+ try {
+ LdapUser ldapUser = _ldapManager.getUser(username);
+ if(!ldapUser.isDisabled()) {
+ result = _ldapManager.canAuthenticate(ldapUser.getPrincipal(), password);
+ } else {
+ s_logger.debug("user with principal "+ ldapUser.getPrincipal() + " is disabled in ldap");
+ }
+ } catch (NoLdapUserMatchingQueryException e) {
+ s_logger.debug(e.getMessage());
+ }
+ }
+ }
+ if (!result && user != null) {
action = ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT;
}
- return new Pair(result, action);
+ }
- } else {
- return new Pair(false, ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT);
+ return new Pair(result, action);
+ }
+
+ 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, domainId, username, null,
+ UUID.randomUUID().toString(), UUID.randomUUID().toString(), User.Source.LDAP);
+ }
+
+ private void disableUserInCloudStack(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 a64899af296..56b39a8b3d1 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
@@ -39,6 +39,9 @@ public class LdapConfiguration implements Configurable{
private static final ConfigKey ldapProvider = new ConfigKey(String.class, "ldap.provider", "Advanced", "openldap", "ldap provider ex:openldap, microsoftad",
true, ConfigKey.Scope.Global, null);
+ private static final ConfigKey ldapEnableNestedGroups = new ConfigKey(Boolean.class, "ldap.nested.groups.enable", "Advanced", "true",
+ "if true, nested groups will also be queried", true, ConfigKey.Scope.Global, null);
+
private final static int scope = SearchControls.SUBTREE_SCOPE;
@Inject
@@ -108,7 +111,8 @@ public class LdapConfiguration implements Configurable{
}
public String[] getReturnAttributes() {
- return new String[] {getUsernameAttribute(), getEmailAttribute(), getFirstnameAttribute(), getLastnameAttribute(), getCommonNameAttribute()};
+ return new String[] {getUsernameAttribute(), getEmailAttribute(), getFirstnameAttribute(), getLastnameAttribute(), getCommonNameAttribute(),
+ getUserAccountControlAttribute()};
}
public int getScope() {
@@ -159,6 +163,10 @@ public class LdapConfiguration implements Configurable{
return "cn";
}
+ public String getUserAccountControlAttribute() {
+ return "userAccountControl";
+ }
+
public Long getReadTimeout() {
return ldapReadTimeout.value();
}
@@ -178,6 +186,10 @@ public class LdapConfiguration implements Configurable{
return provider;
}
+ public boolean isNestedGroupsEnabled() {
+ return ldapEnableNestedGroups.value();
+ }
+
@Override
public String getConfigComponentName() {
return LdapConfiguration.class.getSimpleName();
@@ -185,6 +197,6 @@ public class LdapConfiguration implements Configurable{
@Override
public ConfigKey>[] getConfigKeys() {
- return new ConfigKey>[] {ldapReadTimeout, ldapPageSize, ldapProvider};
+ return new ConfigKey>[] {ldapReadTimeout, ldapPageSize, ldapProvider, ldapEnableNestedGroups};
}
}
\ No newline at end of file
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..6af2c4ebd95 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
@@ -25,12 +25,15 @@ import org.apache.cloudstack.api.response.LdapUserResponse;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.utils.Pair;
import com.cloud.utils.component.PluggableService;
+import org.apache.cloudstack.api.response.LinkDomainToLdapResponse;
public interface LdapManager extends PluggableService {
+ enum LinkType { GROUP, OU;}
+
LdapConfigurationResponse addConfiguration(String hostname, int port) throws InvalidParameterValueException;
- boolean canAuthenticate(String username, String password);
+ boolean canAuthenticate(String principal, String password);
LdapConfigurationResponse createLdapConfigurationResponse(LdapConfigurationVO configuration);
@@ -40,6 +43,8 @@ public interface LdapManager extends PluggableService {
LdapUser getUser(final String username) throws NoLdapUserMatchingQueryException;
+ LdapUser getUser(String username, String type, String name) throws NoLdapUserMatchingQueryException;
+
List getUsers() throws NoLdapUserMatchingQueryException;
List getUsersInGroup(String groupName) throws NoLdapUserMatchingQueryException;
@@ -49,4 +54,8 @@ public interface LdapManager extends PluggableService {
Pair, Integer> listConfigurations(LdapListConfigurationCmd cmd);
List searchUsers(String query) throws NoLdapUserMatchingQueryException;
+
+ LinkDomainToLdapResponse linkDomainToLdap(Long domainId, String type, String name, short accountType);
+
+ public LdapTrustMapVO getDomainLinkedToLdap(long domainId);
}
\ 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 8e912b8b030..3fd928225fc 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
@@ -25,6 +25,10 @@ import javax.inject.Inject;
import javax.naming.NamingException;
import javax.naming.ldap.LdapContext;
+import org.apache.cloudstack.api.command.LinkDomainToLdapCmd;
+import org.apache.cloudstack.api.response.LinkDomainToLdapResponse;
+import org.apache.cloudstack.ldap.dao.LdapTrustMapDao;
+import org.apache.commons.lang.Validate;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
@@ -61,6 +65,9 @@ public class LdapManagerImpl implements LdapManager, LdapValidator {
@Inject LdapUserManagerFactory _ldapUserManagerFactory;
+ @Inject
+ LdapTrustMapDao _ldapTrustMapDao;
+
public LdapManagerImpl() {
super();
@@ -99,17 +106,14 @@ public class LdapManagerImpl implements LdapManager, LdapValidator {
}
@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) {
try {
- final LdapUser user = getUser(escapedUsername);
- final String principal = user.getPrincipal();
final LdapContext context = _ldapContextFactory.createUserContext(principal, password);
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;
}
}
@@ -120,7 +124,7 @@ public class LdapManagerImpl implements LdapManager, LdapValidator {
context.close();
}
} catch (final NamingException e) {
- s_logger.warn(e.getMessage(),e);
+ s_logger.warn(e.getMessage(), e);
}
}
@@ -168,6 +172,7 @@ public class LdapManagerImpl implements LdapManager, LdapValidator {
cmdList.add(LdapImportUsersCmd.class);
cmdList.add(LDAPConfigCmd.class);
cmdList.add(LDAPRemoveCmd.class);
+ cmdList.add(LinkDomainToLdapCmd.class);
return cmdList;
}
@@ -188,6 +193,21 @@ public class LdapManagerImpl implements LdapManager, LdapValidator {
}
}
+ @Override
+ public LdapUser getUser(final String username, final String type, final String name) throws NoLdapUserMatchingQueryException {
+ LdapContext context = null;
+ try {
+ context = _ldapContextFactory.createBindContext();
+ final String escapedUsername = LdapUtils.escapeLDAPSearchFilter(username);
+ return _ldapUserManagerFactory.getInstance(_ldapConfiguration.getLdapProvider()).getUser(escapedUsername, type, name, context);
+ } catch (NamingException | IOException e) {
+ s_logger.debug("ldap Exception: ",e);
+ throw new NoLdapUserMatchingQueryException("No Ldap User found for username: "+username + "name: " + name + "of type: " + type);
+ } finally {
+ closeContext(context);
+ }
+ }
+
@Override
public List getUsers() throws NoLdapUserMatchingQueryException {
LdapContext context = null;
@@ -243,4 +263,22 @@ public class LdapManagerImpl implements LdapManager, LdapValidator {
closeContext(context);
}
}
+
+ @Override
+ public 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));
+ LinkDomainToLdapResponse response = new LinkDomainToLdapResponse(vo.getDomainId(), vo.getType().toString(), vo.getName(), vo.getAccountType());
+ return response;
+ }
+
+ @Override
+ public LdapTrustMapVO getDomainLinkedToLdap(long domainId){
+ return _ldapTrustMapDao.findByDomainId(domainId);
+ }
}
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..8b1363816fa
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapTrustMapVO.java
@@ -0,0 +1,115 @@
+/*
+ * 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_type")
+ private short accountType;
+
+
+ public LdapTrustMapVO() {
+ }
+
+ public LdapTrustMapVO(long domainId, LdapManager.LinkType type, String name, short accountType) {
+ this.domainId = domainId;
+ this.type = type;
+ this.name = name;
+ this.accountType = accountType;
+ }
+
+ @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;
+ }
+
+ @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 (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) 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..c4c334b5b6e 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
@@ -23,14 +23,16 @@ public class LdapUser implements Comparable {
private final String lastname;
private final String username;
private final String domain;
+ private final boolean disabled;
- 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) {
this.username = username;
this.email = email;
this.firstname = firstname;
this.lastname = lastname;
this.principal = principal;
this.domain = domain;
+ this.disabled = disabled;
}
@Override
@@ -74,6 +76,11 @@ public class LdapUser implements Comparable {
return domain;
}
+ public boolean isDisabled() {
+ return disabled;
+ }
+
+
@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 c1bfe742616..4e2bcf816b2 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
@@ -32,6 +32,8 @@ public interface LdapUserManager {
public LdapUser getUser(final String username, final LdapContext context) throws NamingException, IOException;
+ public LdapUser getUser(final String username, final String type, final String name, final LdapContext context) throws NamingException, IOException;
+
public List getUsers(final LdapContext context) throws NamingException, IOException;
public List getUsers(final String username, final LdapContext context) throws NamingException, IOException;
diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/OpenLdapUserManagerImpl.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/OpenLdapUserManagerImpl.java
index 11e6bcfc92a..0c3e0d71705 100644
--- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/OpenLdapUserManagerImpl.java
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/OpenLdapUserManagerImpl.java
@@ -63,7 +63,9 @@ public class OpenLdapUserManagerImpl implements LdapUserManager {
domain = domain.replace("," + _ldapConfiguration.getBaseDn(), "");
domain = domain.replace("ou=", "");
- return new LdapUser(username, email, firstname, lastname, principal, domain);
+ boolean disabled = isUserDisabled(result);
+
+ return new LdapUser(username, email, firstname, lastname, principal, domain, disabled);
}
private String generateSearchFilter(final String username) {
@@ -128,6 +130,48 @@ public class OpenLdapUserManagerImpl implements LdapUserManager {
}
}
+ @Override
+ public LdapUser getUser(final String username, final String type, final String name, final LdapContext context) throws NamingException, IOException {
+ String basedn;
+ if("OU".equals(type)) {
+ basedn = name;
+ } else {
+ basedn = _ldapConfiguration.getBaseDn();
+ }
+
+ final StringBuilder userObjectFilter = new StringBuilder();
+ userObjectFilter.append("(objectClass=");
+ userObjectFilter.append(_ldapConfiguration.getUserObject());
+ userObjectFilter.append(")");
+
+ 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 ("GROUP".equals(type)) {
+ memberOfFilter.append("(").append(getMemberOfAttribute()).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);
+ }
+
+ protected String getMemberOfAttribute() {
+ return "memberof";
+ }
+
@Override
public List getUsers(final LdapContext context) throws NamingException, IOException {
return getUsers(null, context);
@@ -191,6 +235,30 @@ public class OpenLdapUserManagerImpl implements LdapUserManager {
return searchUsers(null, context);
}
+ protected boolean isUserDisabled(SearchResult result) throws NamingException {
+ return false;
+ }
+
+ public LdapUser searchUser(final String basedn, final String searchString, final LdapContext context) throws NamingException, IOException {
+ final SearchControls searchControls = new SearchControls();
+
+ searchControls.setSearchScope(_ldapConfiguration.getScope());
+ searchControls.setReturningAttributes(_ldapConfiguration.getReturnAttributes());
+
+ NamingEnumeration results = context.search(basedn, searchString, searchControls);
+ final List users = new ArrayList();
+ while (results.hasMoreElements()) {
+ final SearchResult result = results.nextElement();
+ users.add(createUser(result));
+ }
+
+ 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) throws NamingException, IOException {
@@ -212,7 +280,9 @@ public class OpenLdapUserManagerImpl implements LdapUserManager {
results = context.search(basedn, generateSearchFilter(username), searchControls);
while (results.hasMoreElements()) {
final SearchResult result = results.nextElement();
- users.add(createUser(result));
+ if (!isUserDisabled(result)) {
+ users.add(createUser(result));
+ }
}
Control[] contextControls = context.getResponseControls();
if (contextControls != null) {
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..7ef3799b3d8
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapTrustMapDao.java
@@ -0,0 +1,27 @@
+/*
+ * 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;
+
+public interface LdapTrustMapDao extends GenericDao {
+ LdapTrustMapVO findByDomainId(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..fb0d74d122d
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/dao/LdapTrustMapDaoImpl.java
@@ -0,0 +1,48 @@
+/*
+ * 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 javax.ejb.Local;
+
+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;
+
+@Component
+@Local(value = {LdapTrustMapDao.class})
+public class LdapTrustMapDaoImpl extends GenericDaoBase implements LdapTrustMapDao {
+ private final SearchBuilder domainIdSearch;
+
+ public LdapTrustMapDaoImpl() {
+ super();
+ domainIdSearch = createSearchBuilder();
+ domainIdSearch.and("domainId", domainIdSearch.entity().getDomainId(), SearchCriteria.Op.EQ);
+ domainIdSearch.done();
+ }
+
+ @Override
+ public LdapTrustMapVO findByDomainId(long domainId) {
+ final SearchCriteria sc = domainIdSearch.create();
+ sc.setParameters("domainId", domainId);
+ return findOneBy(sc);
+ }
+}
diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/ADLdapUserManagerImplSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/ADLdapUserManagerImplSpec.groovy
index 6fafa3edf22..93b1b17a460 100644
--- a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/ADLdapUserManagerImplSpec.groovy
+++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/ADLdapUserManagerImplSpec.groovy
@@ -39,10 +39,11 @@ class ADLdapUserManagerImplSpec extends spock.lang.Specification {
adLdapUserManager._ldapConfiguration = ldapConfiguration;
}
- def "test generate AD search filter"() {
+ 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:
@@ -52,6 +53,20 @@ class ADLdapUserManagerImplSpec extends spock.lang.Specification {
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() >> ["username", "firstname", "lastname", "email"]
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..ca19e8c633b 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,84 +16,234 @@
// under the License.
package groovy.org.apache.cloudstack.ldap
+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.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 {
def "Test a failed authentication due to user not being found within cloudstack"() {
- given: "We have an LdapManager, userAccountDao and ldapAuthenticator and the user doesn't exist within cloudstack."
+ given: "We have an LdapManager, userAccountDao and ldapAuthenticator and the user doesn't exist within cloudstack."
LdapManager ldapManager = Mock(LdapManager)
UserAccountDao userAccountDao = Mock(UserAccountDao)
userAccountDao.getUserAccount(_, _) >> null
def ldapAuthenticator = new LdapAuthenticator(ldapManager, userAccountDao)
- when: "A user authentications"
+ when: "A user authentications"
def result = ldapAuthenticator.authenticate("rmurphy", "password", 0, null)
- then: "their authentication fails"
- result.first() == false
+ then: "their authentication fails"
+ result.first() == false
}
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") >> ldapUser
+ ldapManager.canAuthenticate(_, _) >> false
- UserAccountDao userAccountDao = Mock(UserAccountDao)
- userAccountDao.getUserAccount(_, _) >> new UserAccountVO()
- def ldapAuthenticator = new LdapAuthenticator(ldapManager, userAccountDao)
+ UserAccountDao userAccountDao = Mock(UserAccountDao)
+ userAccountDao.getUserAccount(_, _) >> new UserAccountVO()
+ def ldapAuthenticator = new LdapAuthenticator(ldapManager, userAccountDao)
- when: "The user authenticates with an incorrect password"
- def result = ldapAuthenticator.authenticate("rmurphy", "password", 0, null)
+ when: "The user authenticates with an incorrect password"
+ def result = ldapAuthenticator.authenticate("rmurphy", "password", 0, null)
- then: "their authentication fails"
- result.first() == false
+ then: "their authentication fails"
+ result.first() == false
}
def "Test failed authentication due to ldap not being configured"() {
- given: "We have an LdapManager, A configured LDAP server, a userAccountDao and LdapAuthenticator"
- def ldapManager = Mock(LdapManager)
- ldapManager.isLdapEnabled() >> false
+ given: "We have an LdapManager, A configured LDAP server, a userAccountDao and LdapAuthenticator"
+ def ldapManager = Mock(LdapManager)
+ ldapManager.isLdapEnabled() >> false
- UserAccountDao userAccountDao = Mock(UserAccountDao)
- userAccountDao.getUserAccount(_, _) >> new UserAccountVO()
+ UserAccountDao userAccountDao = Mock(UserAccountDao)
+ userAccountDao.getUserAccount(_, _) >> new UserAccountVO()
- def ldapAuthenticator = new LdapAuthenticator(ldapManager, userAccountDao)
- when: "The user authenticates"
- def result = ldapAuthenticator.authenticate("rmurphy", "password", 0, null)
- then: "their authentication fails"
- result.first() == false
+ def ldapAuthenticator = new LdapAuthenticator(ldapManager, userAccountDao)
+ when: "The user authenticates"
+ def result = ldapAuthenticator.authenticate("rmurphy", "password", 0, null)
+ then: "their authentication fails"
+ 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") >> ldapUser
- UserAccountDao userAccountDao = Mock(UserAccountDao)
- userAccountDao.getUserAccount(_, _) >> new UserAccountVO()
- def ldapAuthenticator = new LdapAuthenticator(ldapManager, userAccountDao)
+ UserAccountDao userAccountDao = Mock(UserAccountDao)
+ userAccountDao.getUserAccount(_, _) >> new UserAccountVO()
+ def ldapAuthenticator = new LdapAuthenticator(ldapManager, userAccountDao)
- when: "The user authenticates with an incorrect password"
- def result = ldapAuthenticator.authenticate("rmurphy", "password", 0, null)
+ when: "The user authenticates with an incorrect password"
+ def result = ldapAuthenticator.authenticate("rmurphy", "password", 0, null)
- then: "their authentication passes"
- result.first() == true
- }
+ then: "their authentication passes"
+ result.first() == true
+ }
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)
+ //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)
+ 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)
+ 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)
+ 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/LdapConfigurationSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapConfigurationSpec.groovy
index 4f93adff435..6f967cc6d8b 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
@@ -127,7 +127,7 @@ class LdapConfigurationSpec extends spock.lang.Specification {
when: "Get return attributes is called"
String[] returnAttributes = ldapConfiguration.getReturnAttributes()
then: "An array containing uid, mail, givenname, sn and cn is returned"
- returnAttributes == ["uid", "mail", "givenname", "sn", "cn"]
+ returnAttributes == ["uid", "mail", "givenname", "sn", "cn", "userAccountControl"]
}
def "Test that getScope returns SearchControls.SUBTREE_SCOPE"() {
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 1f17e704f52..a0b20bbcb13 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
@@ -53,7 +53,7 @@ class LdapCreateAccountCmdSpec extends spock.lang.Specification {
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")
+ ldapManager.getUser(_) >> new LdapUser("rmurphy", "rmurphy@cloudstack.org", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering", false)
AccountService accountService = Mock(AccountService)
def ldapCreateAccountCmd = Spy(LdapCreateAccountCmd, constructorArgs: [ldapManager, accountService])
ldapCreateAccountCmd.getCurrentContext() >> Mock(CallContext)
@@ -105,7 +105,7 @@ class LdapCreateAccountCmdSpec extends spock.lang.Specification {
AccountService accountService = Mock(AccountService)
def ldapCreateAccountCmd = new LdapCreateAccountCmd(ldapManager, accountService);
when: "a user with an username, email, firstname and lastname is validated"
- def result = ldapCreateAccountCmd.validateUser(new LdapUser("username", "email", "firstname", "lastname", "principal", "domain"))
+ def result = ldapCreateAccountCmd.validateUser(new LdapUser("username", "email", "firstname", "lastname", "principal", "domain", false))
then: "the result is true"
result == true
}
@@ -116,7 +116,7 @@ class LdapCreateAccountCmdSpec extends spock.lang.Specification {
AccountService accountService = Mock(AccountService)
def ldapCreateAccountCmd = new LdapCreateAccountCmd(ldapManager, accountService)
when: "A user with no email address attempts to validate"
- ldapCreateAccountCmd.validateUser(new LdapUser("username", null, "firstname", "lastname", "principal", "domain"))
+ ldapCreateAccountCmd.validateUser(new LdapUser("username", null, "firstname", "lastname", "principal", "domain", false))
then: "An exception is thrown"
thrown Exception
}
@@ -127,7 +127,7 @@ class LdapCreateAccountCmdSpec extends spock.lang.Specification {
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"))
+ ldapCreateAccountCmd.validateUser(new LdapUser("username", "email", null, "lastname", "principal", false))
then: "An exception is thrown"
thrown Exception
}
@@ -138,7 +138,7 @@ class LdapCreateAccountCmdSpec extends spock.lang.Specification {
AccountService accountService = Mock(AccountService)
def ldapCreateAccountCmd = new LdapCreateAccountCmd(ldapManager, accountService)
when: "A user with no lastname attempts to validate"
- ldapCreateAccountCmd.validateUser(new LdapUser("username", "email", "firstname", null, "principal", "domain"))
+ ldapCreateAccountCmd.validateUser(new LdapUser("username", "email", "firstname", null, "principal", "domain", false))
then: "An exception is thown"
thrown Exception
}
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 6e0759f85c1..514caeb17da 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
@@ -53,8 +53,8 @@ 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"))
+ users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering", false))
+ users.add(new LdapUser("bob", "bob@test.com", "Robert", "Young", "cn=bob,ou=engineering,dc=cloudstack,dc=org", "engineering", false))
ldapManager.getUsers() >> users
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")
@@ -81,8 +81,8 @@ 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"))
+ users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering", false))
+ users.add(new LdapUser("bob", "bob@test.com", "Robert", "Young", "cn=bob,ou=engineering,dc=cloudstack,dc=org", "engineering", false))
ldapManager.getUsersInGroup("TestGroup") >> users
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")
@@ -110,8 +110,8 @@ 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"))
+ users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering", false))
+ users.add(new LdapUser("bob", "bob@test.com", "Robert", "Young", "cn=bob,ou=engineering,dc=cloudstack,dc=org", "engineering", false))
ldapManager.getUsersInGroup("TestGroup") >> users
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")
@@ -139,8 +139,8 @@ 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"))
+ users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering", false))
+ users.add(new LdapUser("bob", "bob@test.com", "Robert", "Young", "cn=bob,ou=engineering,dc=cloudstack,dc=org", "engineering", false))
ldapManager.getUsers() >> users
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")
@@ -169,8 +169,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)
+ def ldapUser2 = new LdapUser("bob", "bob@test.com", "Robert", "Young", "cn=bob,ou=engineering,dc=cloudstack,dc=org", "engineering", false);
Domain domain = new DomainVO(expectedDomainName, 1L, 1L, expectedDomainName, UUID.randomUUID().toString());
if (varDomainId != null) {
@@ -204,7 +204,7 @@ 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"))
+ users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering", false))
ldapManager.getUsers() >> users
LdapUserResponse response1 = new LdapUserResponse("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering")
ldapManager.createLdapUserResponse(_) >>> response1
@@ -234,7 +234,7 @@ 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"))
+ users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering", false))
ldapManager.getUsers() >> users
LdapUserResponse response1 = new LdapUserResponse("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering")
ldapManager.createLdapUserResponse(_) >>> response1
@@ -263,7 +263,7 @@ 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"))
+ users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering", false))
ldapManager.getUsers() >> 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..5247a1ec895 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
@@ -53,7 +53,7 @@ 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))
+ users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null, false))
ldapManager.getUsers() >> users
LdapUserResponse response = new LdapUserResponse("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null)
ldapManager.createLdapUserResponse(_) >> response
@@ -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)
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)
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 4c62a4eb67e..c9af0020848 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
@@ -24,6 +24,9 @@ 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 org.apache.cloudstack.api.command.LinkDomainToLdapCmd
+import org.apache.cloudstack.api.response.LinkDomainToLdapResponse
+import org.apache.cloudstack.ldap.dao.LdapTrustMapDao
import javax.naming.NamingException
import javax.naming.ldap.InitialLdapContext
@@ -35,6 +38,8 @@ 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"() {
given: "We have an LdapConfigurationDao, LdapContextFactory, LdapUserManager and LdapManager"
@@ -111,7 +116,7 @@ class LdapManagerImplSpec extends spock.lang.Specification {
def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManagerFactory, ldapConfiguration)
when: "A ldap user response is generated"
def result = ldapManager.createLdapUserResponse(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org",
- "engineering"))
+ "engineering", false))
then: "The result of the response should match the given ldap user"
result.username == "rmurphy"
result.email == "rmurphy@test.com"
@@ -131,7 +136,7 @@ class LdapManagerImplSpec extends spock.lang.Specification {
ldapUserManagerFactory.getInstance(_) >> ldapUserManager
ldapContextFactory.createBindContext() >> null
List users = new ArrayList<>();
- users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null))
+ users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null, false))
ldapUserManager.getUsers(_) >> users;
def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManagerFactory, ldapConfiguration)
when: "We search for a group of users"
@@ -149,7 +154,7 @@ class LdapManagerImplSpec extends spock.lang.Specification {
def ldapConfiguration = Mock(LdapConfiguration)
ldapUserManagerFactory.getInstance(_) >> ldapUserManager
ldapContextFactory.createBindContext() >> null
- ldapUserManager.getUser(_, _) >> new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null)
+ ldapUserManager.getUser(_, _) >> new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null, false)
def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManagerFactory, ldapConfiguration)
when: "We search for a user"
def result = ldapManager.getUser("rmurphy")
@@ -194,22 +199,6 @@ class LdapManagerImplSpec extends spock.lang.Specification {
result == false
}
- def "Test successful failed result from canAuthenticate due to user not found"() {
- given: "We have an LdapConfigurationDao, LdapContextFactory, LdapUserManager and LdapManager"
- def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl)
- def ldapContextFactory = Mock(LdapContextFactory)
- def ldapUserManager = Mock(LdapUserManager)
- def ldapUserManagerFactory = Mock(LdapUserManagerFactory)
- ldapUserManagerFactory.getInstance(_) >> ldapUserManager
- def ldapConfiguration = Mock(LdapConfiguration)
- def ldapManager = Spy(LdapManagerImpl, constructorArgs: [ldapConfigurationDao, ldapContextFactory, ldapUserManagerFactory, ldapConfiguration])
- ldapManager.getUser(_) >> { throw new NamingException() }
- when: "The user attempts to authenticate and the user is not found"
- def result = ldapManager.canAuthenticate("rmurphy", "password")
- then: "the authentication fails"
- result == false
- }
-
def "Test successful failed result from deleteConfiguration due to configuration not existing"() {
given: "We have an LdapConfigurationDao, LdapContextFactory, LdapUserManager and LdapManager"
def ldapConfigurationDao = Mock(LdapConfigurationDaoImpl)
@@ -293,7 +282,7 @@ class LdapManagerImplSpec extends spock.lang.Specification {
ldapContextFactory.createBindContext() >> null;
List users = new ArrayList();
- users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering"))
+ users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,ou=engineering,dc=cloudstack,dc=org", "engineering", false))
ldapUserManager.getUsers(_, _) >> users;
def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManagerFactory, ldapConfiguration)
@@ -364,6 +353,7 @@ class LdapManagerImplSpec extends spock.lang.Specification {
cmdList.add(LdapImportUsersCmd.class);
cmdList.add(LDAPConfigCmd.class);
cmdList.add(LDAPRemoveCmd.class);
+ cmdList.add(LinkDomainToLdapCmd.class)
return cmdList
}
@@ -434,7 +424,7 @@ class LdapManagerImplSpec extends spock.lang.Specification {
ldapUserManagerFactory.getInstance(_) >> ldapUserManager
ldapContextFactory.createBindContext() >> null
List users = new ArrayList<>();
- users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", "engineering"))
+ users.add(new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", "engineering", false))
ldapUserManager.getUsersInGroup("engineering", _) >> users;
def ldapManager = new LdapManagerImpl(ldapConfigurationDao, ldapContextFactory, ldapUserManagerFactory, ldapConfiguration)
when: "We search for a group of users"
@@ -442,4 +432,154 @@ class LdapManagerImplSpec extends spock.lang.Specification {
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)
+
+ 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)
+
+ 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..55510875899 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))
ldapManager.searchUsers(_) >> users
LdapUserResponse response = new LdapUserResponse("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null)
ldapManager.createLdapUserResponse(_) >> response
diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserSpec.groovy
index 6df947be22a..8ddfc9a23b8 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)
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)
+ def userB = new LdapUser(usernameB, "", "", "", "", "", false)
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)
+ def userB = new LdapUser(usernameB, "", "", "", "", "", false)
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)
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)
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)
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)
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)
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)
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..9d667bf4cfb
--- /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)
+
+ 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 = 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", false)
+
+ _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
+ result.getType() == type
+ result.getName() == name
+ result.getAdminId() == null
+ }
+
+ def "test with valid params and with admin who doesnt exist in cloudstack"() {
+ def domainId = 1;
+ 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, 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
+ result.getType() == type
+ result.getName() == name
+ result.getAdminId() == String.valueOf(accountId)
+ }
+
+ def "test when admin doesnt exist in ldap"() {
+ 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) >> {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
+ 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 = 1;
+ 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, 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) >> { 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
+ result.getType() == type
+ result.getName() == name
+ result.getAdminId() == null
+ }
+
+}
diff --git a/server/src/com/cloud/user/AccountManagerImpl.java b/server/src/com/cloud/user/AccountManagerImpl.java
index 2d07e841601..edc8ad87b78 100644
--- a/server/src/com/cloud/user/AccountManagerImpl.java
+++ b/server/src/com/cloud/user/AccountManagerImpl.java
@@ -2145,14 +2145,10 @@ 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(userSource != User.Source.UNKNOWN) {
if(!authenticator.getName().equalsIgnoreCase(userSource.name())){
@@ -2177,6 +2173,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())) {
@@ -2196,6 +2193,11 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
s_logger.debug("Unable to authenticate user with username " + username + " in domain " + domainId);
}
+ if (userAccount == null) {
+ s_logger.warn("Unable to find an user with username " + username + " in domain " + domainId);
+ return null;
+ }
+
if (userAccount.getState().equalsIgnoreCase(Account.State.enabled.toString())) {
if (!isInternalAccount(userAccount.getId())) {
// Internal accounts are not disabled
diff --git a/setup/db/db/schema-452to460.sql b/setup/db/db/schema-452to460.sql
index e013b2bf85c..1f236081a7e 100644
--- a/setup/db/db/schema-452to460.sql
+++ b/setup/db/db/schema-452to460.sql
@@ -399,3 +399,13 @@ CREATE TABLE `cloud`.`external_bigswitch_bcf_devices` (
CONSTRAINT `fk_external_bigswitch_bcf_devices__physical_network_id` FOREIGN KEY (`physical_network_id`) REFERENCES `physical_network`(`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,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_ldap_trust_map__domain_id` (`domain_id`),
+ CONSTRAINT `fk_ldap_trust_map__domain_id` FOREIGN KEY (`domain_id`) REFERENCES `domain` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css
index c170bde1c93..61301d58155 100644
--- a/ui/css/cloudstack3.css
+++ b/ui/css/cloudstack3.css
@@ -12853,6 +12853,14 @@ div.ui-dialog div.autoscaler div.field-group div.form-container form div.form-it
background-position: -230px -677px;
}
+.linktoldap .icon {
+ background-position: -197px -65px;
+}
+
+.linktoldap:hover .icon {
+ background-position: -197px -647px;
+}
+
.label-hovered {
cursor: pointer;
color: #0000FF !important;
diff --git a/ui/dictionary2.jsp b/ui/dictionary2.jsp
index d415266bce6..66faa0d0619 100644
--- a/ui/dictionary2.jsp
+++ b/ui/dictionary2.jsp
@@ -1066,6 +1066,10 @@ under the License.
'label.ovm3.vip': '',
'label.local.file': '',
'label.local.storage.enabled.system.vms': '',
+'label.link.domain.to.ldap': '',
+'message.link.domain.to.ldap': '',
+'label.ldap.link.type': '',
+'label.account.type': ''
'label.create.ssh.key.pair': '',
'label.fingerprint': '',
'label.host.tag': '',
diff --git a/ui/scripts/docs.js b/ui/scripts/docs.js
index 809c398cd2f..ed6ab0c938c 100755
--- a/ui/scripts/docs.js
+++ b/ui/scripts/docs.js
@@ -1317,5 +1317,16 @@ cloudStack.docs = {
helpOvm3Vip: {
desc: 'The VIP used by the pool and cluster',
externalLink: ''
+ },
+ helpLdapGroupName: {
+ desc: 'Fully qualified name of OU/GROUP in LDAP',
+ externalLink: ''
+ },
+ helpLdapGroupType: {
+ desc: 'Type of LDAP name provided. Can be either GROUP/OU',
+ externalLink: ''
+ },
+ helpLdapLinkDomainAdmin: {
+ desc: 'domain admin of the linked domain. Specify a username in GROUP/OU of LDAP'
}
};
diff --git a/ui/scripts/domains.js b/ui/scripts/domains.js
index 7f8220e7c0c..dcec93d53f4 100644
--- a/ui/scripts/domains.js
+++ b/ui/scripts/domains.js
@@ -313,6 +313,109 @@
}
},
+ linktoldap: {
+ label: 'label.link.domain.to.ldap',
+
+ action: function(args) {
+ var data = {
+ domainid: args.context.domains[0].id,
+ type: args.data.type,
+ name: args.data.name,
+ accounttype: args.data.accounttype
+ };
+
+ if (args.data.admin != null && args.data.admin.length > 0) {
+ $.extend(data, {
+ admin: args.data.admin
+ });
+ }
+
+ $.ajax({
+ url: createURL('linkDomainToLdap'),
+ data: data,
+ success: function(json) {
+ var item = json.linkdomaintoldapresponse.LinkDomainToLdap.domainid;
+ args.response.success({
+ data: item
+ });
+ },
+ error: function(XMLHttpResponse) {
+ var errorMsg = parseXMLHttpResponse(XMLHttpResponse);
+ args.response.error(errorMsg);
+ }
+ });
+ },
+
+ messages: {
+ notification: function(args) {
+ return 'label.link.domain.to.ldap';
+ }
+ },
+
+ createForm: {
+ title: 'label.link.domain.to.ldap',
+ desc: 'message.link.domain.to.ldap',
+ fields: {
+ type: {
+ label: 'label.ldap.link.type',
+ docID: 'helpLdapGroupType',
+ validation: {
+ required: true
+ },
+ select: function(args) {
+ var items = [];
+ items.push({
+ id: "GROUP",
+ description: "GROUP"
+ }); //regular-user
+ items.push({
+ id: "OU",
+ description: "OU"
+ }); //root-admin
+ args.response.success({
+ data: items
+ });
+ }
+ },
+ name: {
+ label: 'label.name',
+ docID: 'helpLdapGroupName',
+ validation: {
+ required: true
+ }
+ },
+ accounttype: {
+ label: 'label.account.type',
+ docID: 'helpAccountType',
+ validation: {
+ required: true
+ },
+ select: function(args) {
+ var items = [];
+ items.push({
+ id: 0,
+ description: "Normal User"
+ }); //regular-user
+ items.push({
+ id: 2,
+ description: "Domain Admin"
+ }); //root-admin
+ args.response.success({
+ data: items
+ });
+ }
+ },
+ admin: {
+ label: 'label.domain.admin',
+ docID: 'helpLdapLinkDomainAdmin',
+ validation: {
+ required: false
+ }
+ }
+ }
+ }
+ },
+
updateResourceCount: {
label: 'label.action.update.resource.count',
messages: {
@@ -652,6 +755,9 @@
if (jsonObj.level != 0) { //ROOT domain (whose level is 0) is not allowed to delete
allowedActions.push("delete");
}
+ if(isLdapEnabled()) {
+ allowedActions.push("linktoldap")
+ }
} else if (isDomainAdmin()) {
if (args.context.domains[0].id != g_domainid) {
allowedActions.push("edit"); //merge updateResourceLimit into edit