package org.ovirt.engine.core.bll.adbroker;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.ejb.Local;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import org.jboss.ejb3.annotation.Depends;
import org.jboss.ejb3.annotation.Management;
import org.jboss.ejb3.annotation.Service;
import org.ovirt.engine.core.bll.DbUserCacheManager;
import org.ovirt.engine.core.common.businessentities.ad_groups;
import org.ovirt.engine.core.common.config.Config;
import org.ovirt.engine.core.common.config.ConfigValues;
import org.ovirt.engine.core.common.utils.EnumUtils;
import org.ovirt.engine.core.compat.LogCompat;
import org.ovirt.engine.core.compat.LogFactoryCompat;
import org.ovirt.engine.core.dal.dbbroker.generic.DomainsPasswordMap;
import org.ovirt.engine.core.dns.DnsSRVLocator.DnsSRVResult;
import org.ovirt.engine.core.ldap.LdapSRVLocator;
import org.ovirt.engine.core.utils.ejb.BeanProxyType;
import org.ovirt.engine.core.utils.ejb.BeanType;
import org.ovirt.engine.core.utils.ejb.EjbUtils;
import org.ovirt.engine.core.utils.kerberos.AuthenticationResult;
import org.ovirt.engine.core.utils.kerberos.KerberosUtils;
@Service
@Management(UsersDomainsCacheManager.class)
@Local(UsersDomainsCacheManager.class)
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
@Depends({"jboss.j2ee:ear=engine.ear,jar=engine-bll.jar,name=Backend,service=EJB3",
"jboss.j2ee:ear=engine.ear,jar=engine-scheduler.jar,name=Scheduler,service=EJB3",
"jboss.j2ee:ear=engine.ear,jar=engine-bll.jar,name=KerberosManager,service=EJB3",
"jboss.j2ee:ear=engine.ear,jar=engine-vdsbroker.jar,name=VdsBroker,service=EJB3"})
public class UsersDomainsCacheManagerService implements UsersDomainsCacheManager {
private static LogCompat log = LogFactoryCompat.getLog(UsersDomainsCacheManagerService.class);
private Map<String, Domain> domainsByName = new HashMap<String, Domain>();
private Map<String, ConcurrentHashMap<String, UserDomainInfo>> domainsUsersInfoByUserNameAndDomainName =
new HashMap<String, ConcurrentHashMap<String, UserDomainInfo>>();
private Map<String, ConcurrentHashMap<String, ad_groups>> groupsPerDomain =
new HashMap<String, ConcurrentHashMap<String, ad_groups>>();
private Map<String, URI> ldapServerPerDomain = new HashMap<String, URI>();
private final String DEFAULT_SECURITY_AUTHENTICATION_KEY = "default";
private Map<String, LDAPSecurityAuthentication> ldapSecurityAuthenticationPerDomain =
new HashMap<String, LDAPSecurityAuthentication>();
private Map<String, String> userPerDomain = new HashMap<String, String>();
private Map<String, String> passwordPerDomain = new HashMap<String, String>();
@Override
public void addDomain(Domain domain) {
domainsByName.put(domain.getName(), domain);
}
private void fillLdapServersMap() {
String ldapServerPerDomainEntry = Config.<String> GetValue(ConfigValues.LdapServers);
if (!ldapServerPerDomainEntry.isEmpty()) {
String[] domainServerPairs = ldapServerPerDomainEntry.split(",");
int ldapPort = Config.<Integer> GetValue(ConfigValues.LDAPServerPort);
for (String domainServerPair : domainServerPairs) {
String[] parts = domainServerPair.split(":");
String domain = parts[0].trim().toLowerCase();
URI ldapURI;
try {
ldapURI = new URI("ldap://" + parts[1].trim() + ":" + ldapPort);
ldapServerPerDomain.put(domain, ldapURI);
} catch (URISyntaxException e) {
log.errorFormat("Failed constructing LDAP server URL for domain {0}", domain);
}
}
}
}
// This code is the exact code as in SysprepHandler, until we have a suitable location that them both can
// use
// Note that every change in one will probably require the same change in the other
private void fillUsersMap() {
String userPerDomainEntry = Config.<String> GetValue(ConfigValues.AdUserName);
if (!userPerDomainEntry.isEmpty()) {
String[] domainUserPairs = userPerDomainEntry.split(",");
for (String domainUserPair : domainUserPairs) {
String[] parts = domainUserPair.split(":");
String domain = parts[0].trim().toLowerCase();
String userName = parts[1].trim();
userPerDomain.put(domain, userName);
}
}
}
private void fillPasswordsMap() {
passwordPerDomain = Config.<DomainsPasswordMap> GetValue(ConfigValues.AdUserPassword);
}
private void fillLdapSecurityAuthenticationMap() {
String ldapSecurityAuthEntry = Config.<String> GetValue(ConfigValues.LDAPSecurityAuthentication);
if (!ldapSecurityAuthEntry.isEmpty()) {
String[] ldapSecurityPairs = ldapSecurityAuthEntry.split(",");
for (String ldapSecurityPair : ldapSecurityPairs) {
String[] parts = ldapSecurityPair.split(":");
String domain = parts[0].trim().toLowerCase();
String authModeStr = parts[1].trim().toUpperCase();
LDAPSecurityAuthentication authMode =
EnumUtils.valueOf(LDAPSecurityAuthentication.class, authModeStr, true);
ldapSecurityAuthenticationPerDomain.put(domain, authMode);
}
}
}
/**
* This method is called upon the bean creation as part
* of the management Service bean lifecycle.
*/
public void create() {
log.infoFormat("UsersDomainsCacheManager: {0}", new java.util.Date());
String authMethod = Config.<String> GetValue(ConfigValues.AuthenticationMethod);
if (!authMethod.equalsIgnoreCase("LDAP")) {
return;
}
List<String> domains = LdapBrokerUtils.getDomainsList(true);
fillLdapServersMap();
fillLdapSecurityAuthenticationMap();
fillUsersMap();
fillPasswordsMap();
for (String domainName : domains) {
domainName = domainName.toLowerCase();
domainsUsersInfoByUserNameAndDomainName.put(domainName, new ConcurrentHashMap<String, UserDomainInfo>());
Domain domain = new Domain(domainName);
domain.setLdapProviderType(LdapProviderType.general);
domain.setLdapSecurityAuthentication(getDomainSecurityAuthentication(domainName));
domainsByName.put(domainName, domain);
domain.setUserName(userPerDomain.get(domainName));
domain.setPassword(passwordPerDomain.get(domainName));
// Each domain has LDAP servers that one of them should be used to
// perform an LDAP query against the domain
obtainLDAPServersForDomain(domain);
groupsPerDomain.put(domain.getName(), new ConcurrentHashMap<String, ad_groups>());
}
DbUserCacheManager.getInstance().init();
log.infoFormat("DbUserCacheManager: {0}", new java.util.Date());
}
private LDAPSecurityAuthentication getDomainSecurityAuthentication(String domainName) {
LDAPSecurityAuthentication securityAuthentication = ldapSecurityAuthenticationPerDomain.get(domainName);
if (securityAuthentication != null) {
return securityAuthentication;
} else {
securityAuthentication = ldapSecurityAuthenticationPerDomain.get(DEFAULT_SECURITY_AUTHENTICATION_KEY);
if (securityAuthentication != null) {
return securityAuthentication;
} else {
return LDAPSecurityAuthentication.GSSAPI;
}
}
}
private void obtainLDAPServersForDomain(Domain domain) {
URI ldapServerURI = ldapServerPerDomain.get(domain.getName());
if (ldapServerURI != null) {
domain.addLDAPServer(ldapServerURI);
return;
}
LdapSRVLocator locator = new LdapSRVLocator();
DnsSRVResult results;
try {
results = locator.getLdapServers(domain.getName());
if (results == null || results.getNumOfValidAddresses() == 0) {
log.warnFormat("Error in getting LDAP servers for domain {0}. Constructing an LDAP URL based on domain name",
domain.getName());
constructLDAPUrlOnDNSFailure(domain);
return;
}
for (int counter = 0; counter < results.getNumOfValidAddresses(); counter++) {
String address = results.getAddresses()[counter];
try {
URI ldapURI = locator.constructURI("LDAP", address);
domain.addLDAPServer(ldapURI);
} catch (URISyntaxException e) {
log.errorFormat("Error in getting LDAP url based on srv record for address {0}", address);
}
}
} catch (Exception ex) {
AuthenticationResult result = KerberosUtils.convertDNSException(ex);
log.warnFormat("Error in getting LDAP servers for domain {0}: {1}. Constructing an LDAP URL based on domain name",
domain.getName(),
result.getDetailedMessage());
constructLDAPUrlOnDNSFailure(domain);
}
}
private void constructLDAPUrlOnDNSFailure(Domain domain) {
int ldapPort = Config.<Integer> GetValue(ConfigValues.LDAPServerPort);
StringBuilder ldapURL = new StringBuilder();
ldapURL.append("ldap://").append(domain.getName()).append(":").append(ldapPort);
try {
URI uri = new URI(ldapURL.toString());
domain.addLDAPServer(uri);
} catch (URISyntaxException e) {
log.error("Failed constructing LDAP server URL for domain ");
}
}
public static UsersDomainsCacheManager getInstance() {
return EjbUtils.<UsersDomainsCacheManager> findBean(BeanType.USERS_DOMAINS_CACHE, BeanProxyType.LOCAL);
}
@Override
public UserDomainInfo associateUserWithDomain(String userName, String domainName) {
domainName = domainName.toLowerCase();
UserDomainInfo userDomainInfo = new UserDomainInfo(userName, domainName);
ConcurrentHashMap<String, UserDomainInfo> usersDomainInfoByDomainName =
domainsUsersInfoByUserNameAndDomainName.get(domainName);
// FOr the given user name, put the user domain info in the map only if
// absent - this is atomic, and prevents to threads from putting several
// entries for same user
usersDomainInfoByDomainName.putIfAbsent(userName, userDomainInfo);
return usersDomainInfoByDomainName.get(userName);
}
@Override
public UserDomainInfo getUserDomainInfo(String userName, String domainName) {
domainName = domainName.toLowerCase();
ConcurrentHashMap<String, UserDomainInfo> usersDomainInfoByDomainName =
domainsUsersInfoByUserNameAndDomainName.get(domainName);
return usersDomainInfoByDomainName.get(userName);
}
@Override
public Domain getDomain(String domainName) {
domainName = domainName.toLowerCase();
return domainsByName.get(domainName);
}
@Override
public void removeUserDomainInfo(String userName, String domainName) {
domainName = domainName.toLowerCase();
ConcurrentHashMap<String, UserDomainInfo> usersDomainInfoByDomainName =
domainsUsersInfoByUserNameAndDomainName.get(domainName);
usersDomainInfoByDomainName.remove(userName);
}
@Override
public void removeDomain(String domainName) {
domainName = domainName.toLowerCase();
domainsByName.remove(domainName);
}
}