Merge pull request #755 from karuturi/CLOUDSTACK-8647-2

Cloudstack:8647 LDAP Trust AD and AutoimportToday, CloudStack can automatically import LDAP users based on the configuration to a domain or an account. However, any new users in LDAP aren't automatically reflected. The admin has to manually import them again.
This feature enables admin to map LDAP group/OU to a CloudStack domain and any changes are reflected in ACS as well.
FS: https://cwiki.apache.org/confluence/display/CLOUDSTACK/WIP%3A+LDAP%3A+Trust+AD+and+Auto+Import

testcases output:
```
-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running groovy.org.apache.cloudstack.ldap.NoLdapUserMatchingQueryExceptionSpec
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.216 sec - in groovy.org.apache.cloudstack.ldap.NoLdapUserMatchingQueryExceptionSpec
Running groovy.org.apache.cloudstack.ldap.LdapManagerImplSpec
log4j:WARN No appenders could be found for logger (org.apache.cloudstack.ldap.LdapManagerImpl).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
using type:
using type: null
using type: TEST
using type: TEST TEST
using name:
using name: null
using accountType: -1
using accountType: 1
using accountType: 3
using accountType: 4
using accountType: 5
using accountType: 6
using accountType: 20000
using accountType: -500000
Tests run: 29, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.387 sec - in groovy.org.apache.cloudstack.ldap.LdapManagerImplSpec
Running groovy.org.apache.cloudstack.ldap.LdapListUsersCmdSpec
Tests run: 6, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.041 sec - in groovy.org.apache.cloudstack.ldap.LdapListUsersCmdSpec
Running groovy.org.apache.cloudstack.ldap.LdapAddConfigurationCmdSpec
Tests run: 6, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.019 sec - in groovy.org.apache.cloudstack.ldap.LdapAddConfigurationCmdSpec
Running groovy.org.apache.cloudstack.ldap.LdapUserSpec
Tests run: 9, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.021 sec - in groovy.org.apache.cloudstack.ldap.LdapUserSpec
Running groovy.org.apache.cloudstack.ldap.LdapAuthenticatorSpec
Tests run: 10, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.082 sec - in groovy.org.apache.cloudstack.ldap.LdapAuthenticatorSpec
Running groovy.org.apache.cloudstack.ldap.LdapConfigurationVOSpec
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.004 sec - in groovy.org.apache.cloudstack.ldap.LdapConfigurationVOSpec
Running groovy.org.apache.cloudstack.ldap.OpenLdapUserManagerSpec
Tests run: 12, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.094 sec - in groovy.org.apache.cloudstack.ldap.OpenLdapUserManagerSpec
Running groovy.org.apache.cloudstack.ldap.LdapDeleteConfigurationCmdSpec
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.008 sec - in groovy.org.apache.cloudstack.ldap.LdapDeleteConfigurationCmdSpec
Running groovy.org.apache.cloudstack.ldap.LdapUserResponseSpec
Tests run: 7, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.01 sec - in groovy.org.apache.cloudstack.ldap.LdapUserResponseSpec
Running groovy.org.apache.cloudstack.ldap.LdapUserManagerFactorySpec
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.027 sec - in groovy.org.apache.cloudstack.ldap.LdapUserManagerFactorySpec
Running groovy.org.apache.cloudstack.ldap.ADLdapUserManagerImplSpec
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.012 sec - in groovy.org.apache.cloudstack.ldap.ADLdapUserManagerImplSpec
Running groovy.org.apache.cloudstack.ldap.LdapCreateAccountCmdSpec
Tests run: 11, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.168 sec - in groovy.org.apache.cloudstack.ldap.LdapCreateAccountCmdSpec
Running groovy.org.apache.cloudstack.ldap.LdapImportUsersCmdSpec
Tests run: 9, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.063 sec - in groovy.org.apache.cloudstack.ldap.LdapImportUsersCmdSpec
Running groovy.org.apache.cloudstack.ldap.LinkDomainToLdapCmdSpec
Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.019 sec - in groovy.org.apache.cloudstack.ldap.LinkDomainToLdapCmdSpec
Running groovy.org.apache.cloudstack.ldap.LdapSearchUserCmdSpec
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.011 sec - in groovy.org.apache.cloudstack.ldap.LdapSearchUserCmdSpec
Running groovy.org.apache.cloudstack.ldap.LdapListConfigurationCmdSpec
Tests run: 6, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.01 sec - in groovy.org.apache.cloudstack.ldap.LdapListConfigurationCmdSpec
Running groovy.org.apache.cloudstack.ldap.NoSuchLdapUserExceptionSpec
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.005 sec - in groovy.org.apache.cloudstack.ldap.NoSuchLdapUserExceptionSpec
Running groovy.org.apache.cloudstack.ldap.LdapConfigurationResponseSpec
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.006 sec - in groovy.org.apache.cloudstack.ldap.LdapConfigurationResponseSpec
Running groovy.org.apache.cloudstack.ldap.LdapConfigurationSpec
asserting for provider configuration: openldap
asserting for provider configuration: microsoftad
asserting for provider configuration:
asserting for provider configuration:
asserting for provider configuration: xyz
asserting for provider configuration: MicrosoftAd
asserting for provider configuration: OpenLdap
asserting for provider configuration: MicrosoftAD
Tests run: 19, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.053 sec - in groovy.org.apache.cloudstack.ldap.LdapConfigurationSpec
Running groovy.org.apache.cloudstack.ldap.LdapContextFactorySpec
Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.099 sec - in groovy.org.apache.cloudstack.ldap.LdapContextFactorySpec
Running groovy.org.apache.cloudstack.ldap.LdapConfigurationDaoImplSpec
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.027 sec - in groovy.org.apache.cloudstack.ldap.LdapConfigurationDaoImplSpec
Running groovy.org.apache.cloudstack.ldap.LdapUtilsSpec
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.01 sec - in groovy.org.apache.cloudstack.ldap.LdapUtilsSpec

Results :

Tests run: 156, Failures: 0, Errors: 0, Skipped: 0
```

* pr/755:
  CLOUDSTACK-8647: linkdomaintoldap shouldnt fail when createuseraccount fails
  CLOUDSTACK-8647 removed duplicate key in create sql of ldap_trust_map
  CLOUDSTACK-8647: string formatting
  CLOUDSTACK-8647: updated with review comments
  CLOUDSTACK-8647: unittests for LdapAuthenticatorSpec
  CLOUDSTACK-8647: formatted LdapAuthenticatorSpec
  CLOUDSTACK-8647: UI for trust AD feature
  CLOUDSTACK-8647 added unittests for new methods  in ldapmanager
  CLOUDSTACK-8647 unittests for LinkDomainToLdap api command
  CLOUDSTACK-8647: fixed unittests
  CLOUDSTACK-8647 support for assigning and admin to linked ldap domain
  CLOUDSTACK-8647 added nested group enabled config in ldap
  CLOUDSTACK-8647 added account_type to the linkDomainToLdap API
  CLOUDSTACK-8647 changed the authentication flow
  CLOUDSTACK-8647 added new api linkLdapToDomain
  CLOUDSTACK-8647: added cmd and response class for the new api

Signed-off-by: Rajani Karuturi <rajani.karuturi@citrix.com>
This commit is contained in:
Rajani Karuturi 2015-09-05 09:30:15 +05:30
commit 5881035e7b
33 changed files with 1420 additions and 135 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -771,6 +771,7 @@ deleteLdapConfiguration=3
listLdapUsers=3
ldapCreateAccount=3
importLdapUsers=3
linkDomainToLdap=3
#### juniper-contrail commands

View File

@ -35,5 +35,6 @@
<bean id="LdapConfigurationDao"
class="org.apache.cloudstack.ldap.dao.LdapConfigurationDaoImpl" />
<bean id="LdapConfiguration" class="org.apache.cloudstack.ldap.LdapConfiguration" />
<bean id="LdapTrustMapDao" class="org.apache.cloudstack.ldap.dao.LdapTrustMapDaoImpl" />
</beans>

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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<LdapUser> 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;
}
}
}

View File

@ -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<Boolean, ActionOnFailedAuthentication>(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<Boolean, ActionOnFailedAuthentication>(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<Boolean, ActionOnFailedAuthentication>(result, action);
}
} else {
return new Pair<Boolean, ActionOnFailedAuthentication>(false, ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT);
return new Pair<Boolean, ActionOnFailedAuthentication>(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());
}
}

View File

@ -39,6 +39,9 @@ public class LdapConfiguration implements Configurable{
private static final ConfigKey<String> ldapProvider = new ConfigKey<String>(String.class, "ldap.provider", "Advanced", "openldap", "ldap provider ex:openldap, microsoftad",
true, ConfigKey.Scope.Global, null);
private static final ConfigKey<Boolean> ldapEnableNestedGroups = new ConfigKey<Boolean>(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};
}
}

View File

@ -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<LdapUser> getUsers() throws NoLdapUserMatchingQueryException;
List<LdapUser> getUsersInGroup(String groupName) throws NoLdapUserMatchingQueryException;
@ -49,4 +54,8 @@ public interface LdapManager extends PluggableService {
Pair<List<? extends LdapConfigurationVO>, Integer> listConfigurations(LdapListConfigurationCmd cmd);
List<LdapUser> searchUsers(String query) throws NoLdapUserMatchingQueryException;
LinkDomainToLdapResponse linkDomainToLdap(Long domainId, String type, String name, short accountType);
public LdapTrustMapVO getDomainLinkedToLdap(long domainId);
}

View File

@ -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<LdapUser> 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);
}
}

View File

@ -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;
}
}

View File

@ -23,14 +23,16 @@ public class LdapUser implements Comparable<LdapUser> {
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<LdapUser> {
return domain;
}
public boolean isDisabled() {
return disabled;
}
@Override
public int hashCode() {
return getUsername().hashCode();

View File

@ -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<LdapUser> getUsers(final LdapContext context) throws NamingException, IOException;
public List<LdapUser> getUsers(final String username, final LdapContext context) throws NamingException, IOException;

View File

@ -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<LdapUser> 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<SearchResult> results = context.search(basedn, searchString, searchControls);
final List<LdapUser> users = new ArrayList<LdapUser>();
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<LdapUser> 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) {

View File

@ -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, Long> {
LdapTrustMapVO findByDomainId(long domainId);
}

View File

@ -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<LdapTrustMapVO, Long> implements LdapTrustMapDao {
private final SearchBuilder<LdapTrustMapVO> 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<LdapTrustMapVO> sc = domainIdSearch.create();
sc.setParameters("domainId", domainId);
return findOneBy(sc);
}
}

View File

@ -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"]

View File

@ -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<Boolean, UserAuthenticator.ActionOnFailedAuthentication> 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<Boolean, UserAuthenticator.ActionOnFailedAuthentication> 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<Boolean, UserAuthenticator.ActionOnFailedAuthentication> 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<Boolean, UserAuthenticator.ActionOnFailedAuthentication> 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<Boolean, UserAuthenticator.ActionOnFailedAuthentication> result = ldapAuthenticator.authenticate(username, "password", domainId, null)
then:
result.first() == false
result.second() == UserAuthenticator.ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT
}
}

View File

@ -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"() {

View File

@ -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
}

View File

@ -53,8 +53,8 @@ class LdapImportUsersCmdSpec extends spock.lang.Specification {
def accountService = Mock(AccountService)
List<LdapUser> 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<LdapUser> 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<LdapUser> 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<LdapUser> 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<LdapUser> 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<LdapUser> 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<LdapUser> 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

View File

@ -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<LdapUser> 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<UserResponse>()
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"

View File

@ -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<LdapUser> 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<LdapUser> users = new ArrayList<LdapUser>();
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<LdapUser> 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
}
}

View File

@ -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<LdapUser> 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

View File

@ -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 "

View File

@ -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
}
}

View File

@ -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<ActionOnFailedAuthentication> actionsOnFailedAuthenticaion = new HashSet<ActionOnFailedAuthentication>();
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

View File

@ -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;

View File

@ -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;

View File

@ -1066,6 +1066,10 @@ under the License.
'label.ovm3.vip': '<fmt:message key="label.ovm3.vip" />',
'label.local.file': '<fmt:message key="label.local.file" />',
'label.local.storage.enabled.system.vms': '<fmt:message key="label.local.storage.enabled.system.vms" />',
'label.link.domain.to.ldap': '<fmt:message key="label.link.domain.to.ldap" />',
'message.link.domain.to.ldap': '<fmt:message key="message.link.domain.to.ldap" />',
'label.ldap.link.type': '<fmt:message key="label.ldap.link.type" />',
'label.account.type': '<fmt:message key="label.account.type" />'
'label.create.ssh.key.pair': '<fmt:message key="label.create.ssh.key.pair" />',
'label.fingerprint': '<fmt:message key="label.fingerprint" />',
'label.host.tag': '<fmt:message key="label.host.tag" />',

View File

@ -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'
}
};

View File

@ -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