This is an automated email from the ASF dual-hosted git repository.

madhan pushed a commit to branch ranger-2.8
in repository https://gitbox.apache.org/repos/asf/ranger.git


The following commit(s) were added to refs/heads/ranger-2.8 by this push:
     new d6ce6687e RANGER-5271 : Last login date and time on Ranger Home page 
(#655)
d6ce6687e is described below

commit d6ce6687e7fa6c014d1933e19fb10f51ca9640e6
Author: Dhaval Rajpara <[email protected]>
AuthorDate: Sun Oct 5 06:55:36 2025 +0530

    RANGER-5271 : Last login date and time on Ranger Home page (#655)
---
 .../src/main/java/org/apache/ranger/biz/SessionMgr.java | 13 +++++++++++++
 .../src/main/java/org/apache/ranger/biz/UserMgr.java    |  2 ++
 .../java/org/apache/ranger/db/XXAuthSessionDao.java     | 17 +++++++++++++++++
 .../main/java/org/apache/ranger/view/VXPortalUser.java  | 15 +++++++++++++++
 .../src/main/resources/META-INF/jpa_named_queries.xml   |  5 +++++
 .../src/main/webapp/react-webapp/src/styles/style.css   |  2 +-
 .../src/main/webapp/react-webapp/src/views/Layout.jsx   | 11 +++++++++++
 .../webapp/react-webapp/src/views/SideBar/SideBar.jsx   |  2 +-
 8 files changed, 65 insertions(+), 2 deletions(-)

diff --git a/security-admin/src/main/java/org/apache/ranger/biz/SessionMgr.java 
b/security-admin/src/main/java/org/apache/ranger/biz/SessionMgr.java
index bca2e2ea6..4ae81cb61 100644
--- a/security-admin/src/main/java/org/apache/ranger/biz/SessionMgr.java
+++ b/security-admin/src/main/java/org/apache/ranger/biz/SessionMgr.java
@@ -21,6 +21,7 @@
 
 import java.util.ArrayList;
 import java.util.Calendar;
+import java.util.Date;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -326,6 +327,18 @@ public XXAuthSession processFailureLogin(int authStatus, 
int authType,
                return gjAuthSession;
        }
 
+    public Date getLastSuccessLoginAuthTimeByUserId(String loginId) {
+        XXAuthSession xXAuthSession = 
daoManager.getXXAuthSession().getLastSuccessLoginAuthSessionByUserId(loginId);
+
+        if (xXAuthSession != null) {
+            return 
authSessionService.populateViewBean(xXAuthSession).getAuthTime();
+        } else {
+            logger.info("Session cleaned up or  User logged in for first 
time");
+        }
+
+        return null;
+    }
+
        protected boolean validateUserSession(UserSessionBase userSession,
                        String currentLoginId) {
                if (currentLoginId
diff --git a/security-admin/src/main/java/org/apache/ranger/biz/UserMgr.java 
b/security-admin/src/main/java/org/apache/ranger/biz/UserMgr.java
index f7433f6f2..14cbfa907 100644
--- a/security-admin/src/main/java/org/apache/ranger/biz/UserMgr.java
+++ b/security-admin/src/main/java/org/apache/ranger/biz/UserMgr.java
@@ -585,7 +585,9 @@ public VXPortalUser 
mapXXPortalUserToVXPortalUser(XXPortalUser user,
 
                        userProfile.setUserRoleList(userRoleList);
                }
+               
userProfile.setLastLoginTime(sessionMgr.getLastSuccessLoginAuthTimeByUserId(sess.getLoginId()));
                userProfile.setUserSource(user.getUserSource());
+
                return userProfile;
        }
 
diff --git 
a/security-admin/src/main/java/org/apache/ranger/db/XXAuthSessionDao.java 
b/security-admin/src/main/java/org/apache/ranger/db/XXAuthSessionDao.java
index b49d192b5..297546ed1 100644
--- a/security-admin/src/main/java/org/apache/ranger/db/XXAuthSessionDao.java
+++ b/security-admin/src/main/java/org/apache/ranger/db/XXAuthSessionDao.java
@@ -71,6 +71,23 @@ public List<XXAuthSession> getAuthSessionByUserId(Long 
userId){
                }
        }
 
+    public XXAuthSession getLastSuccessLoginAuthSessionByUserId(String 
loginId) {
+        if (loginId == null) {
+            return null;
+        }
+        try {
+            List<XXAuthSession> sessions = getEntityManager()
+                    
.createNamedQuery("XXAuthSession.getSuccessAuthSessionsByUserId", tClass)
+                    .setParameter("loginId", loginId)
+                    .setParameter("authStatus", 
XXAuthSession.AUTH_STATUS_SUCCESS)
+                    .setMaxResults(2)
+                    .getResultList();
+            return sessions.size() >= 2 ? sessions.get(1) : null;
+        } catch (NoResultException ignoreNoResultFound) {
+            return null;
+        }
+    }
+
        public long getRecentAuthFailureCountByLoginId(String loginId, int 
timeRangezSecond){
                Date authWindowStartTime = new 
Date(DateUtil.getUTCDate().getTime() - timeRangezSecond * 1000);
 
diff --git 
a/security-admin/src/main/java/org/apache/ranger/view/VXPortalUser.java 
b/security-admin/src/main/java/org/apache/ranger/view/VXPortalUser.java
index 27bae7e8a..6db4d9d9a 100644
--- a/security-admin/src/main/java/org/apache/ranger/view/VXPortalUser.java
+++ b/security-admin/src/main/java/org/apache/ranger/view/VXPortalUser.java
@@ -20,14 +20,17 @@
  package org.apache.ranger.view;
 
 import java.util.Collection;
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 
 import org.apache.ranger.common.AppConstants;
+import org.apache.ranger.json.JsonDateSerializer;
 import com.fasterxml.jackson.annotation.JsonAutoDetect;
 import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 
 @JsonAutoDetect(getterVisibility=Visibility.NONE, 
setterVisibility=Visibility.NONE, fieldVisibility=Visibility.ANY)
 @JsonInclude(JsonInclude.Include.NON_NULL)
@@ -95,6 +98,9 @@ public class VXPortalUser extends VXDataObject implements 
java.io.Serializable {
         */
        protected String syncSource;
 
+       @JsonSerialize(using = JsonDateSerializer.class)
+       protected Date lastLoginTime;
+
        /**
         * Configuration properties.
         *
@@ -348,6 +354,14 @@ public void setSyncSource(final String syncSource) {
                this.syncSource = syncSource;
        }
 
+    public Date getLastLoginTime() {
+        return lastLoginTime;
+    }
+
+    public void setLastLoginTime(Date lastLoginTime) {
+        this.lastLoginTime = lastLoginTime;
+    }
+
        /**
         * This return the bean content in string format
         * @return formatedStr
@@ -366,6 +380,7 @@ public String toString( ) {
                str += "userRoleList={" + userRoleList + "} ";
                str += "otherAttributes={" + otherAttributes + "} ";
                str += "syncSource={" + syncSource + "} ";
+               str += "lastLoginTime={" + lastLoginTime + "} ";
                str += "}";
                return str;
        }
diff --git a/security-admin/src/main/resources/META-INF/jpa_named_queries.xml 
b/security-admin/src/main/resources/META-INF/jpa_named_queries.xml
index 6f7108eac..9db7bf1ca 100755
--- a/security-admin/src/main/resources/META-INF/jpa_named_queries.xml
+++ b/security-admin/src/main/resources/META-INF/jpa_named_queries.xml
@@ -48,6 +48,11 @@
                <query>DELETE FROM XXAuthSession obj WHERE obj.id in :ids
                </query>
        </named-query>
+       <named-query name="XXAuthSession.getSuccessAuthSessionsByUserId">
+               <query>SELECT obj FROM XXAuthSession obj WHERE obj.loginId = 
:loginId and obj.authStatus = :authStatus order by obj.id DESC
+               </query>
+       </named-query>
+
 
        <!-- XXPortalUser -->
        <named-query name="XXPortalUser.findByEmailAddress">
diff --git a/security-admin/src/main/webapp/react-webapp/src/styles/style.css 
b/security-admin/src/main/webapp/react-webapp/src/styles/style.css
index 646ebebd5..57adcf354 100644
--- a/security-admin/src/main/webapp/react-webapp/src/styles/style.css
+++ b/security-admin/src/main/webapp/react-webapp/src/styles/style.css
@@ -1309,7 +1309,7 @@ header {
 }
 
 .top-scroll {
-  bottom: 3px;
+  bottom: 10px;
   right: 5px;
 }
 
diff --git a/security-admin/src/main/webapp/react-webapp/src/views/Layout.jsx 
b/security-admin/src/main/webapp/react-webapp/src/views/Layout.jsx
index 1edb38423..d6199b41f 100644
--- a/security-admin/src/main/webapp/react-webapp/src/views/Layout.jsx
+++ b/security-admin/src/main/webapp/react-webapp/src/views/Layout.jsx
@@ -40,6 +40,7 @@ import { Loader } from "../components/CommonComponents";
 import { Suspense } from "react";
 import { PathAssociateWithModule } from "../utils/XAEnums";
 import { flatMap, values } from "lodash";
+import dateFormat from "dateformat";
 
 const Layout = () => {
   let location = useLocation();
@@ -94,6 +95,13 @@ const Layout = () => {
     activate();
   };
 
+  const lastLoginInformation = userProfile?.lastLoginTime
+    ? `You last logged in on, ${dateFormat(
+        new Date(userProfile.lastLoginTime),
+        "dddd, mmm dd, yyyy, hh:MM:ss TT"
+      )}`
+    : "";
+
   useEffect(() => {
     const interval = setInterval(() => {
       if (isPrompted()) {
@@ -170,6 +178,9 @@ const Layout = () => {
                 >
                   Licensed under the Apache License, Version 2.0
                 </a>
+                <span className="float-end mb-2 me-4" style={{ color: "#999" 
}}>
+                  {lastLoginInformation}
+                </span>
               </p>
             </div>
           </footer>
diff --git 
a/security-admin/src/main/webapp/react-webapp/src/views/SideBar/SideBar.jsx 
b/security-admin/src/main/webapp/react-webapp/src/views/SideBar/SideBar.jsx
index 19078216b..6a5db4502 100644
--- a/security-admin/src/main/webapp/react-webapp/src/views/SideBar/SideBar.jsx
+++ b/security-admin/src/main/webapp/react-webapp/src/views/SideBar/SideBar.jsx
@@ -125,7 +125,7 @@ export const SideBar = () => {
 
   const userProps = getUserProfile();
   const loginId = (
-    <span className="login-id user-name">{userProps?.loginId}</span>
+    <span className="login-id user-name p-0">{userProps?.loginId}</span>
   );
 
   let location = useLocation();

Reply via email to