package org.ovirt.engine.core.bll; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.concurrent.TimeUnit; import org.ovirt.engine.core.bll.adbroker.AdActionType; import org.ovirt.engine.core.bll.adbroker.LdapBrokerUtils; import org.ovirt.engine.core.bll.adbroker.LdapFactory; import org.ovirt.engine.core.bll.adbroker.LdapSearchByIdParameters; import org.ovirt.engine.core.bll.adbroker.LdapSearchByUserIdListParameters; import org.ovirt.engine.core.bll.adbroker.UsersDomainsCacheManagerService; import org.ovirt.engine.core.common.businessentities.AdRefStatus; import org.ovirt.engine.core.common.businessentities.AdUser; import org.ovirt.engine.core.common.businessentities.AsyncTaskStatusEnum; import org.ovirt.engine.core.common.businessentities.DbUser; 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.compat.Guid; import org.ovirt.engine.core.compat.LogCompat; import org.ovirt.engine.core.compat.LogFactoryCompat; import org.ovirt.engine.core.compat.StringHelper; import org.ovirt.engine.core.dal.dbbroker.DbFacade; import org.ovirt.engine.core.utils.linq.LinqUtils; import org.ovirt.engine.core.utils.linq.Predicate; import org.ovirt.engine.core.utils.timer.OnTimerMethodAnnotation; import org.ovirt.engine.core.utils.timer.SchedulerUtilQuartzImpl; public class DbUserCacheManager { private static DbUserCacheManager _instance = new DbUserCacheManager(); private String jobId; private boolean initialized = false; private static class UsersPerDomainPredicate implements Predicate<DbUser> { private final List<String> domains; public UsersPerDomainPredicate(List<String> domains) { this.domains = domains; } @Override public boolean eval(DbUser t) { // The predicate is used to filter out users which are not in one of // the domains that is defined by the "DomainName" configuration // value return domains.contains(t.getdomain()); } } public static DbUserCacheManager getInstance() { return _instance; } private DbUserCacheManager() { } public void init() { if (!initialized) { // clean all user sessions in DB DbFacade.getInstance().getDbUserDAO().removeAllSessions(); int mRefreshRate = Config.<Integer> GetValue(ConfigValues.UserRefreshRate); jobId = SchedulerUtilQuartzImpl.getInstance().scheduleAFixedDelayJob(this, "OnTimer", new Class[] {}, new Object[] {}, 0, mRefreshRate, TimeUnit.SECONDS); initialized = true; } } @Override protected void finalize() throws Throwable { Dispose(); } /** * detect differences between current DB users and the directory server users/groups and persist them * * @param dbUser * DB user * @param adUser * LDAP user * @param updatedUsers * list of changed users. */ private void updateDBUserFromADUser(DbUser dbUser, AdUser adUser, HashSet<Guid> updatedUsers) { boolean succeded = false; // AdUser adUser = // LdapFactory.Instance.GetAdUserByUserIdAndDomain(dbUser.user_id, // dbUser.domain); if ((adUser == null) || (adUser.getUserId().equals(Guid.Empty)) || (!adUser.getUserId().equals(dbUser.getuser_id()))) { if (dbUser.getstatus() != 0) { log.warnFormat("User {0} not found in directory server, its status switched to InActive", dbUser.getname()); dbUser.setstatus(0); succeded = true; } } else { if (dbUser.getstatus() == 0) { log.warnFormat("Inactive User {0} found in directory server, its status switched to Active", dbUser.getname()); dbUser.setstatus(1); succeded = true; } if (!StringHelper.EqOp(dbUser.getname(), adUser.getName())) { dbUser.setname(adUser.getName()); succeded = true; } if (!StringHelper.EqOp(dbUser.getsurname(), adUser.getSurName())) { dbUser.setsurname(adUser.getSurName()); succeded = true; } if (!StringHelper.EqOp(dbUser.getdomain(), adUser.getDomainControler())) { dbUser.setdomain(adUser.getDomainControler()); succeded = true; } if (!StringHelper.EqOp(dbUser.getusername(), adUser.getUserName())) { dbUser.setusername(adUser.getUserName()); succeded = true; } if (!StringHelper.EqOp(dbUser.getgroups(), adUser.getGroup())) { dbUser.setgroups(adUser.getGroup()); succeded = true; updatedUsers.add(dbUser.getuser_id()); } if (!StringHelper.EqOp(dbUser.getdepartment(), adUser.getDepartment())) { dbUser.setdepartment(adUser.getDepartment()); succeded = true; } if (!StringHelper.EqOp(dbUser.getrole(), adUser.getTitle())) { dbUser.setrole(adUser.getTitle()); succeded = true; } if (!StringHelper.EqOp(dbUser.getemail(), adUser.getEmail())) { dbUser.setemail(adUser.getEmail()); succeded = true; } if (!StringHelper.EqOp(dbUser.getGroupIds(), adUser.getGroupIds())) { dbUser.setGroupIds(adUser.getGroupIds()); succeded = true; } if (succeded) { dbUser.setstatus(dbUser.getstatus() + 1); } } if (succeded) { DbFacade.getInstance().getDbUserDAO().update(dbUser); } else { } } public void refreshAllUserData(List<ad_groups> updatedGroups) { try { log.info("DbUserCacheManager::refreshAllUserData() - entered"); List<DbUser> allUsers = DbFacade.getInstance().getDbUserDAO().getAll(); List<String> domainsList = LdapBrokerUtils.getDomainsList(true); List<DbUser> filteredUsers = LinqUtils.filter(allUsers, new UsersPerDomainPredicate(domainsList)); java.util.HashMap<String, java.util.HashMap<Guid, DbUser>> userByDomains = new java.util.HashMap<String, java.util.HashMap<Guid, DbUser>>(); /** * Filter all users by domains */ for (DbUser user : filteredUsers) { java.util.HashMap<Guid, DbUser> domainUser; if (!userByDomains.containsKey(user.getdomain())) { domainUser = new java.util.HashMap<Guid, DbUser>(); userByDomains.put(user.getdomain(), domainUser); } else { domainUser = userByDomains.get(user.getdomain()); } domainUser.put(user.getuser_id(), user); } if (userByDomains.size() != 0) { /** * refresh users in each domain separately */ for (String domain : userByDomains.keySet()) { java.util.ArrayList<AdUser> adUsers = (java.util.ArrayList<AdUser>) LdapFactory.getInstance(domain) .RunAdAction( AdActionType.GetAdUserByUserIdList, new LdapSearchByUserIdListParameters(domain, new java.util.ArrayList<Guid>(userByDomains .get(domain).keySet()),false)).getReturnValue(); HashSet<Guid> updatedUsers = new HashSet<Guid>(); if (adUsers == null) { log.warn("No users returned from directory server during refresh users"); } else { LdapBrokerUtils.performGroupPopulationForUsers(adUsers,domain,updatedGroups); for (AdUser adUser : adUsers) { updateDBUserFromADUser(userByDomains.get(domain).get(adUser.getUserId()), adUser, updatedUsers); userByDomains.get(domain).remove(adUser.getUserId()); } } Collection<DbUser> usersForDomain = userByDomains.get(domain).values(); if (usersForDomain == null) { log.warnFormat("No users for domain {0}",domain); } else { for (DbUser dbUser : usersForDomain) { if (dbUser.getstatus() != 0) { log.warnFormat("User {0} not found in directory sevrer, its status switched to InActive", dbUser.getname()); dbUser.setstatus(AsyncTaskStatusEnum.unknown.getValue()); DbFacade.getInstance().getDbUserDAO().update(dbUser); } } } // update lastAdminCheckStatus property for users that their // group or role was changed if (updatedUsers.size() > 0) { DbFacade.getInstance().updateLastAdminCheckStatus(updatedUsers.toArray(new Guid[updatedUsers .size()])); } } } } catch (RuntimeException e) { log.error("DbUserCacheManager::refreshAllUserData() - failed with exception", e); } } @OnTimerMethodAnnotation("OnTimer") public void OnTimer() { List<ad_groups> groups = updateGroups(); refreshAllUserData(groups); } private List<ad_groups> updateGroups() { List<ad_groups> groups = DbFacade.getInstance().getAdGroupDAO().getAll(); for (ad_groups group : groups) { /** * Vitaly workaround. Temporary treatment on missing group domains */ // Waiting for the GUI team to fix the ad_group class. When the // class is fixed, // domain name will be passed correctly to the backend, and the // following code should not occur if (group.getdomain() == null && group.getname().contains("@")) { StringBuilder logMsg = new StringBuilder(); logMsg.append("domain name for ad group ") .append(group.getname()) .append(" is null. This should not occur, please check that domain name is passed corectly from client"); log.warn(logMsg.toString()); String partAfterAtSign = group.getname().split("[@]", -1)[1]; String newDomainName = partAfterAtSign; if (partAfterAtSign.contains("/")) { String partPreviousToSlashSign = partAfterAtSign.split("[/]", -1)[0]; newDomainName = partPreviousToSlashSign; } group.setdomain(newDomainName); } // We check if the domain is null or empty for internal groups. // An internal group does not have a domain, and there is no need to query // the ldap server for it. Note that if we will add support in the future for // domain-less groups in the ldap server then this code will have to change in order // to fetch for them if (group.getdomain() != null && !group.getdomain().isEmpty()) { if (UsersDomainsCacheManagerService.getInstance().getDomain(group.getdomain()) == null) { log.errorFormat("Cannot query for group {0} from domain {1} because the domain is not configured. Please use the manage domains utility if you wish to add this domain.", group.getname(), group.getdomain()); } else { ad_groups groupFromAD = (ad_groups) LdapFactory .getInstance(group.getdomain()) .RunAdAction(AdActionType.GetAdGroupByGroupId, new LdapSearchByIdParameters(group.getdomain(), group.getid())) .getReturnValue(); if (group.getstatus() == AdRefStatus.Active && (groupFromAD == null || groupFromAD.getstatus() == AdRefStatus.Inactive)) { group.setstatus(AdRefStatus.Inactive); DbFacade.getInstance().getAdGroupDAO().update(group); } else if (groupFromAD != null && (!StringHelper.EqOp(group.getname(), groupFromAD.getname()) || group.getstatus() != groupFromAD .getstatus() || !StringHelper.EqOp(group.getDistinguishedName(), groupFromAD.getDistinguishedName()))) { DbFacade.getInstance().getAdGroupDAO().update(groupFromAD); } // memberOf is not persistent and should be set in the returned groups list from the LDAP queries if (groupFromAD != null) { group.setMemberOf(groupFromAD.getMemberOf()); } } } } return groups; } public void Dispose() { if (jobId != null) { SchedulerUtilQuartzImpl.getInstance().deleteJob(jobId); } } private static LogCompat log = LogFactoryCompat.getLog(DbUserCacheManager.class); }