Friday 12 November 2021

Generate reports for a users last log-in in AEM

There are cases where we need to generate reports for a users last log-in in AEM. 

I have seen many help blogs but none of them worked for me. Below given an approach which worked for me.

Approach

Utilising 'AuthenticationInfoPostProcessor' service in combination with ACS commons, its going to be easy to generate such reports. This is tested on AEM 6.5 version.

Step1: Deploy below Java code which will capture lastLogin information.
    

Java class which captures the users last login and update the user node

Note: Modify the conditions as per your project requirement. 

--Java class START ---

package yourpackage.core.services;

import java.text.SimpleDateFormat;
import java.util.Date;

import javax.jcr.Session;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.api.security.user.UserManager;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.auth.core.spi.AuthenticationInfo;
import org.apache.sling.auth.core.spi.AuthenticationInfoPostProcessor;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component(name = "UserProfileService", service = AuthenticationInfoPostProcessor.class, immediate = true)
    
public class UserProfileService implements AuthenticationInfoPostProcessor {
    
    /**
     * This class generate Last login property of any user profile
     *
     * @param authenticationinfo
     * @param servletrequest
     * @param servletresponse
     */
    
    private static final Logger LOGGER = LoggerFactory.getLogger(UserProfileService.class);
    @Reference
    private ResourceResolverFactory resourceResolverFactory;

    @Override
    public void postProcess(AuthenticationInfo info, HttpServletRequest request, HttpServletResponse response)
            throws LoginException {

    /**
         * Users last logged in will be his last active time in AEM
         * Executed only when it is a logout operation to ensure the last active time is captured
         * Ensure to update the code with relevant condition
         */
        if ((info != null && info.getAuthType() == null) || (request != null && request.getServletPath() != null
                && (*Your condition 1*))) {
            LOGGER.debug("AuthenticationInfo is null. " + "we can skip post processing this request.");
            return;
        }
        
        ResourceResolver resourceResolver = null;        
        Session session = null;
        UserManager userManager = null;
        Authorizable auth = null;

        try {
            resourceResolver = resourceResolverFactory.getResourceResolver(info);
            session = resourceResolver.adaptTo(Session.class);
            userManager = resourceResolver.adaptTo(UserManager.class);
            auth = userManager.getAuthorizable(session.getUserID());
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
                //Anonymous users need not be checked
                if (auth.getID() != null && (*Your condition 2*)) {
                    LOGGER.info("Logged in Users log in");
                    //Profile will have a new property
                    auth.setProperty("profile/lastLoggedIn", session.getValueFactory().createValue(sdf.format(new Date())));
                    session.save();
                    session.logout();
                }

        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }
}

--Java class END---

 
In my case I had used below conditions.
(*Your condition 1*) - !request.getServletPath().equals("/system/sling/logout.html")
(*Your condition 2*) - !auth.getID().equals("anonymous")
  

Step 2: ACS Commons Report
Now in ACS common reports create a new report with query of type JCRSQL2



SELECT * FROM  [rep:User] as nodes WHERE  ISDESCENDANTNODE("/home/users")
AND nodes.[profile/lastLoggedIn] IS NOT NULL
AND NOT ISDESCENDANTNODE([/home/users/community])
AND NOT ISDESCENDANTNODE([/home/users/mac])
AND NOT ISDESCENDANTNODE([/home/users/rep:policy])
AND NOT ISDESCENDANTNODE([/home/users/screens])
AND NOT ISDESCENDANTNODE([/home/users/system])

And configure the report column as below.



Now you will be able to Generate the final report as shown below.





 Demo Video

No comments:

Post a Comment