RE: LDAP Import Isuue.

Jamie Sammons, modified 1 Year ago. New Member Posts: 4 Join Date: 12/19/24 Recent Posts

Hi Team,

 

I have written custom code for LDAP user import. This code works fine on all servers, but I am facing an issue on the production server. Please check the log error image below. If anyone can help us, it would be appreciated.

Displaying image.png

custom code :

package utils.ldapsync.service;

import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.model.User;
import com.liferay.portal.kernel.security.auth.CompanyThreadLocal;
import com.liferay.portal.kernel.security.ldap.LDAPSettingsUtil;
import com.liferay.portal.kernel.service.UserLocalServiceUtil;
import com.liferay.portal.kernel.util.PrefsPropsUtil;
import com.liferay.portal.kernel.util.Validator;
import com.liferay.portal.security.ldap.SafeLdapContext;
import com.liferay.portal.security.ldap.SafePortalLDAP;
import com.liferay.portal.security.ldap.configuration.ConfigurationProvider;
import com.liferay.portal.security.ldap.configuration.LDAPServerConfiguration;
import com.liferay.portal.security.ldap.exportimport.LDAPToPortalConverter;
import com.liferay.portal.security.ldap.exportimport.LDAPUser;
import com.liferay.portal.security.ldap.exportimport.LDAPUserImporter;

import com.liferay.portal.security.ldap.exportimport.configuration.LDAPImportConfiguration;
import com.liferay.portal.security.ldap.util.LDAPUtil;
import com.youngsoft.utils.ldapsync.constants.LDAPConstants;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceReference;
import org.osgi.service.component.annotations.*;

import javax.naming.NamingEnumeration;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

@Component(
        immediate = true,
        property = {"service.ranking:Integer=100"},
        service = LDAPUserImporter.class
)
public class CustomLDAPUserImporterImpl implements LDAPUserImporter {

    private static final Log _log = LogFactoryUtil.getLog(CustomLDAPUserImporterImpl.class);

    @Reference(
        target = "(factoryPid=com.liferay.portal.security.ldap.configuration.LDAPServerConfiguration)"
    )
    private ConfigurationProvider<LDAPServerConfiguration> _ldapServerConfigurationProvider;

    @Reference
    private volatile LDAPUserImporter _defaultLDAPUserImporter;

    @Reference(
            policy = ReferencePolicy.DYNAMIC,
            policyOption = ReferencePolicyOption.GREEDY
    )
    private volatile SafePortalLDAP _safePortalLDAP;

    private LDAPUserImporter getDefaultLDAPUserImporter() {
        if (_defaultLDAPUserImporter == null) {
            ServiceReference<LDAPUserImporter> serviceReference =
                    FrameworkUtil.getBundle(getClass()).getBundleContext()
                            .getServiceReference(LDAPUserImporter.class);

            _defaultLDAPUserImporter = FrameworkUtil.getBundle(getClass())
                    .getBundleContext()
                    .getService(serviceReference);
        }
        return _defaultLDAPUserImporter;
    }

    @Override
    public User importUser(long ldapServerId, long companyId, SafeLdapContext safeLdapContext, Attributes attributes, String password) throws Exception {
        _log.info("Entering importUser with SafeLdapContext");
        _log.debug("LDAP Server ID: " + ldapServerId + ", Company ID: " + companyId);
        User user = getDefaultLDAPUserImporter().importUser(ldapServerId, companyId, safeLdapContext, attributes, password);
        if (user != null) {
            _log.info("User imported successfully: " + user.getFullName());
        } else {
            _log.warn("User import returned null.");
        }
        _log.info("Exiting importUser with SafeLdapContext");
        return user;
    }
    @Override
    public long getLastImportTime() throws Exception {
        _log.info("Getting last import time.");
        long lastImportTime = _defaultLDAPUserImporter.getLastImportTime();
        _log.debug("Last import time: " + lastImportTime);
        return lastImportTime;
    }

    @Override
    public User importUser(long ldapServerId, long companyId, String emailAddress, String screenName) throws Exception {
        _log.info("Entering importUser with email and screenName");
        _log.debug("Email Address: " + emailAddress + ", Screen Name: " + screenName);
        User user = getDefaultLDAPUserImporter().importUser(ldapServerId, companyId, emailAddress, screenName);
        if (user != null) {
            _log.info("User imported successfully: " + user.getFullName());
        } else {
            _log.warn("User import returned null.");
        }
        _log.info("Exiting importUser with email and screenName");
        return user;
    }

    @Override
    public User importUser(long companyId, String emailAddress, String screenName) throws Exception {
        _log.info("Entering importUser without LDAP server");
        _log.debug("Company ID: " + companyId + ", Email Address: " + emailAddress + ", Screen Name: " + screenName);

        User user = getDefaultLDAPUserImporter().importUser(companyId, emailAddress, screenName);

        if (user != null) {
            _log.info("User imported successfully: " + user.getFullName());
        } else {
            _log.warn("User import returned null.");
        }

        _log.info("Exiting importUser without LDAP server");
        return user;
    }

    @Override
    public User importUserByScreenName(long companyId, String screenName) throws Exception {
        _log.info("Entering importUserByScreenName");
        _log.debug("Company ID: " + companyId + ", Screen Name: " + screenName);

        User user = getDefaultLDAPUserImporter().importUserByScreenName(companyId, screenName);
        if (user != null) {
            _log.info("User imported successfully: " + user.getFullName());
        } else {
            _log.warn("User import returned null.");
        }
        _log.info("Exiting importUserByScreenName");
        return user;
    }

    @Override
    public User importUserByUuid(long ldapServerId, long companyId, String uuid) throws Exception {
        _log.info("Entering importUserByUuid with LDAP server");
        _log.debug("LDAP Server ID: " + ldapServerId + ", Company ID: " + companyId + ", UUID: " + uuid);

        User user = getDefaultLDAPUserImporter().importUserByUuid(ldapServerId, companyId, uuid);

        if (user != null) {
            _log.info("User imported successfully: " + user.getFullName());
        } else {
            _log.warn("User import returned null.");
        }

        _log.info("Exiting importUserByUuid with LDAP server");
        return user;
    }
    

    @Override
    public User importUserByUuid(long companyId, String uuid) throws Exception {
        _log.info("Entering importUserByUuid without LDAP server");
        _log.debug("Company ID: " + companyId + ", UUID: " + uuid);

        User user = getDefaultLDAPUserImporter().importUserByUuid(companyId, uuid);

        if (user != null) {
            _log.info("User imported successfully: " + user.getFullName());
        } else {
            _log.warn("User import returned null.");
        }

        _log.info("Exiting importUserByUuid without LDAP server");
        return user;
    }

    @Override
    public void importUsers() throws Exception {
        _log.info("Entering global importUsers");
        getDefaultLDAPUserImporter().importUsers();
        _log.info("Exiting global importUsers");
    }

    @Override
    public void importUsers(long companyId) throws Exception {
        _log.info("Entering importUsers for a specific company");
        _log.debug("Company ID: " + companyId);

        // Fetch users from LDAP
        List<User> usersFromLDAP = getUsersFromLDAP(companyId);

        // Only import existing users
        for (User user : usersFromLDAP) {
            try {
                // Skip new users or deactivate users based on your custom logic
                _log.info("Importing user: " + user.getFullName());
                getDefaultLDAPUserImporter().importUser(companyId, user.getEmailAddress(), user.getScreenName());
            } catch (Exception e) {
                _log.error("Error importing user: " + user.getScreenName(), e);
            }
        }

        _log.info("Exiting importUsers for a specific company");
    }

    @Override
    public void importUsers(long ldapServerId, long companyId) throws Exception {
        _log.info("Entering importUsers for LDAP server and company");
        _log.debug("LDAP Server ID: " + ldapServerId + ", Company ID: " + companyId);
        getDefaultLDAPUserImporter().importUsers(ldapServerId, companyId);
        _log.info("Exiting importUsers for LDAP server and company");
    }

    public List<User> getUsersFromLDAP(long companyId) {
        List<User> users = new ArrayList<>();
        try {
            Collection<LDAPServerConfiguration> ldapServerConfigurations =
                    _ldapServerConfigurationProvider.getConfigurations(companyId);
            if (ldapServerConfigurations.isEmpty()) {
                _log.error("No LDAP server configurations found for companyId: " + companyId);
                return users; // Return empty list if no configurations are available
            }
            for (LDAPServerConfiguration ldapServerConfiguration : ldapServerConfigurations) {
                long ldapServerId = ldapServerConfiguration.ldapServerId();
                SafeLdapContext safeLdapContext = _safePortalLDAP.getSafeLdapContext(
                        ldapServerId, companyId);
                if (safeLdapContext == null) {
                    _log.warn("Failed to get SafeLdapContext for LDAP server ID: " + ldapServerId);
                    continue;
                }
                String searchBase = ldapServerConfiguration.baseDN();
                String searchFilter = "(objectClass=person)"; // Adjust as needed
                SearchControls searchControls = new SearchControls();
                searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
                searchControls.setReturningAttributes(new String[]{"uid", "mail", "givenName", "sn"});
                NamingEnumeration<SearchResult> results = safeLdapContext.search(
                        searchBase, searchFilter, searchControls);
                // Process each LDAP entry
                while (results.hasMore()) {
                    SearchResult result = results.next();
                    Attributes attributes = result.getAttributes();
                    String screenName = getAttribute(attributes, "displayName");
                    String email = getAttribute(attributes, "mail");
                    String firstName = getAttribute(attributes, "givenName");
                    String lastName = getAttribute(attributes, "sn");
                    if (Validator.isNull(email)) {
                        _log.warn("Skipping LDAP entry due to missing email: " + result);
                        continue;
                    }
                    User existingUser = UserLocalServiceUtil.fetchUserByEmailAddress(companyId, email);
                    if (existingUser != null) {
                        users.add(existingUser);
                    } else {
                        _log.info("User not found in Liferay: " + email);
                    }
                }
                safeLdapContext.close(); // Always close the context to release resources
            }

        } catch (Exception e) {
            _log.error("Error fetching users from LDAP", e);
        }

        return users;
    }
    private String getAttribute(Attributes attributes, String attributeName) {
        try {
            if (attributes.get(attributeName) != null) {
                return (String) attributes.get(attributeName).get();
            }
        } catch (Exception e) {
            _log.warn("Error retrieving attribute: " + attributeName, e);
        }
        return null;
    }

}

 

Thank You,

Vishal S Karjinni
 

thumbnail
Zsigmond Rab, modified 1 Year ago. Liferay Master Posts: 764 Join Date: 1/5/10 Recent Posts

Hi Vishal,

The image that would show the error I guess is broken for me. Could you please check it? Can you please share the error in text format for example?

Thanks,
Zsigmond

Olaf Kock, modified 1 Year ago. New Member Posts: 4 Join Date: 12/19/24 Recent Posts

Hi Zsigmond,

 

Please check for errors in the text format. I am grateful for your time with us.

Exception in thread "liferay/scheduled_user_ldap_import-2" java.lang.StackOverflowError
        at java.text.DateFormatSymbols.getProviderInstance(DateFormatSymbols.java:362)
        at java.text.DateFormatSymbols.getInstance(DateFormatSymbols.java:340)
        at java.util.Calendar.getDisplayName(Calendar.java:2110)
        at java.text.SimpleDateFormat.subFormat(SimpleDateFormat.java:1125)
        at java.text.SimpleDateFormat.format(SimpleDateFormat.java:966)
        at java.text.SimpleDateFormat.format(SimpleDateFormat.java:936)
        at org.apache.log4j.pattern.DatePatternConverter$DefaultZoneDateFormat.format(DatePatternConverter.java:96)
        at java.text.DateFormat.format(DateFormat.java:345)
        at org.apache.log4j.pattern.CachedDateFormat.format(CachedDateFormat.java:283)
        at org.apache.log4j.pattern.DatePatternConverter.format(DatePatternConverter.java:180)
        at org.apache.log4j.pattern.BridgePatternConverter.format(BridgePatternConverter.java:119)
        at org.apache.log4j.EnhancedPatternLayout.format(EnhancedPatternLayout.java:546)
        at org.apache.log4j.WriterAppender.subAppend(WriterAppender.java:310)
        at org.apache.log4j.rolling.RollingFileAppender.subAppend(RollingFileAppender.java:396)
        at org.apache.log4j.WriterAppender.append(WriterAppender.java:162)
        at org.apache.log4j.AppenderSkeleton.doAppend(AppenderSkeleton.java:251)
        at org.apache.log4j.helpers.AppenderAttachableImpl.appendLoopOnAppenders(AppenderAttachableImpl.java:66)
        at org.apache.log4j.Category.callAppenders(Category.java:206)
        at org.apache.log4j.Category.forcedLog(Category.java:391)
        at org.apache.log4j.Category.log(Category.java:856)
        at com.liferay.portal.log.Log4jLogImpl.info(Log4jLogImpl.java:79)
        at com.liferay.portal.kernel.log.LogWrapper.info(LogWrapper.java:123)
        at com.liferay.portal.kernel.log.SanitizerLogWrapper.info(SanitizerLogWrapper.java:139)
        at com.youngsoft.utils.ldapsync.service.CustomLDAPUserImporterImpl.getLastImportTime(CustomLDAPUserImporterImpl.java:82)
        at com.youngsoft.utils.ldapsync.service.CustomLDAPUserImporterImpl.getLastImportTime(CustomLDAPUserImporterImpl.java:83)
        at com.youngsoft.utils.ldapsync.service.CustomLDAPUserImporterImpl.getLastImportTime(CustomLDAPUserImporterImpl.java:83)
 

Thank you,
Vishal S Karjinni

thumbnail
Zsigmond Rab, modified 1 Year ago. Liferay Master Posts: 764 Join Date: 1/5/10 Recent Posts

Hi Vishal,

Well, in the source you shared the line 83 contains a } only, however, I think, the problem is at the

long lastImportTime = _defaultLDAPUserImporter.getLastImportTime();

call in the CustomLDAPUserImporterImpl.getLastImportTime() method. It seems to me from the code that the value of the _defaultLDAPUserImporter is an instance of the CustomLDAPUserImporterImpl so the call above always calls the same method which results a StackOverflowError. It should be debugged to make it sure, though.

Regards,
Zsigmond