mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
CLOUDSTACK-8647 changed the authentication flow
added check to see if domain is linked to ldap. If yes and the user is member of the group/OU, authenticate and import user.
This commit is contained in:
parent
e3ddde841e
commit
7109689fde
@ -79,4 +79,17 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,6 +17,9 @@
|
||||
package org.apache.cloudstack.ldap;
|
||||
|
||||
import com.cloud.server.auth.DefaultUserAuthenticator;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.AccountService;
|
||||
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
|
||||
public AccountService _accountService;
|
||||
|
||||
public LdapAuthenticator() {
|
||||
super();
|
||||
@ -52,22 +58,64 @@ 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) {
|
||||
action = ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT;
|
||||
if (_ldapManager.isLdapEnabled()) {
|
||||
LdapTrustMapVO ldapTrustMapVO = _ldapManager.getDomainLinkedToLdap(domainId);
|
||||
if(ldapTrustMapVO != null) {
|
||||
try {
|
||||
LdapUser ldapUser = _ldapManager.getUser(username, ldapTrustMapVO.getType(), ldapTrustMapVO.getName());
|
||||
if(!ldapUser.isDisabled()) {
|
||||
result = _ldapManager.canAuthenticate(ldapUser.getPrincipal(), password);
|
||||
if(result) {
|
||||
final UserAccount user = _userAccountDao.getUserAccount(username, domainId);
|
||||
if (user == null) {
|
||||
// import user to cloudstack
|
||||
createCloudStackUserAccount(ldapUser, domainId);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//disable user in cloudstack
|
||||
disableUserInCloudStack(ldapUser, domainId);
|
||||
}
|
||||
} catch (NoLdapUserMatchingQueryException e) {
|
||||
s_logger.debug(e.getMessage());
|
||||
}
|
||||
|
||||
} else {
|
||||
//domain is not linked to ldap follow normal authentication
|
||||
final UserAccount user = _userAccountDao.getUserAccount(username, domainId);
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Pair<Boolean, ActionOnFailedAuthentication>(result, action);
|
||||
|
||||
} else {
|
||||
return new Pair<Boolean, ActionOnFailedAuthentication>(false, ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT);
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
action = ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT;
|
||||
}
|
||||
return new Pair<Boolean, ActionOnFailedAuthentication>(result, action);
|
||||
}
|
||||
|
||||
private void createCloudStackUserAccount(LdapUser user, long domainId) {
|
||||
String username = user.getUsername();
|
||||
_accountService.createUserAccount(username, "", user.getFirstname(), user.getLastname(), user.getEmail(), "GMT", username, Account.ACCOUNT_TYPE_DOMAIN_ADMIN, domainId,
|
||||
username, null, UUID.randomUUID().toString(), UUID.randomUUID().toString(), User.Source.LDAP);
|
||||
}
|
||||
|
||||
private void disableUserInCloudStack(LdapUser ldapUser, long domainId) {
|
||||
final UserAccount user = _userAccountDao.getUserAccount(ldapUser.getUsername(), domainId);
|
||||
_accountService.lockUser(user.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -108,7 +108,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 +160,10 @@ public class LdapConfiguration implements Configurable{
|
||||
return "cn";
|
||||
}
|
||||
|
||||
public String getUserAccountControlAttribute() {
|
||||
return "userAccountControl";
|
||||
}
|
||||
|
||||
public Long getReadTimeout() {
|
||||
return ldapReadTimeout.value();
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ public interface LdapManager extends PluggableService {
|
||||
|
||||
LdapConfigurationResponse addConfiguration(String hostname, int port) throws InvalidParameterValueException;
|
||||
|
||||
boolean canAuthenticate(String username, String password);
|
||||
boolean canAuthenticate(String principal, String password);
|
||||
|
||||
LdapConfigurationResponse createLdapConfigurationResponse(LdapConfigurationVO configuration);
|
||||
|
||||
@ -41,6 +41,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;
|
||||
@ -52,4 +54,6 @@ public interface LdapManager extends PluggableService {
|
||||
List<LdapUser> searchUsers(String query) throws NoLdapUserMatchingQueryException;
|
||||
|
||||
LinkDomainToLdapResponse linkDomainToLdap(Long domainId, String type, String name);
|
||||
|
||||
public LdapTrustMapVO getDomainLinkedToLdap(long domainId);
|
||||
}
|
||||
@ -105,17 +105,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;
|
||||
}
|
||||
}
|
||||
@ -126,7 +123,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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,6 +192,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;
|
||||
@ -257,4 +269,9 @@ public class LdapManagerImpl implements LdapManager, LdapValidator {
|
||||
LdapTrustMapVO ldapTrustMapVO = _ldapTrustMapDao.persist(new LdapTrustMapVO(domainId, type, name));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LdapTrustMapVO getDomainLinkedToLdap(long domainId){
|
||||
return _ldapTrustMapDao.findByDomainId(domainId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,43 @@ 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("(memberof=");
|
||||
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);
|
||||
}
|
||||
@Override
|
||||
public List<LdapUser> getUsers(final LdapContext context) throws NamingException, IOException {
|
||||
return getUsers(null, context);
|
||||
@ -191,6 +230,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 +275,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) {
|
||||
|
||||
@ -23,4 +23,5 @@ import org.apache.cloudstack.ldap.LdapTrustMapVO;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
|
||||
public interface LdapTrustMapDao extends GenericDao<LdapTrustMapVO, Long> {
|
||||
LdapTrustMapVO findByDomainId(long domainId);
|
||||
}
|
||||
|
||||
@ -20,6 +20,8 @@ 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;
|
||||
|
||||
@ -28,7 +30,19 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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())){
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user