This is an automated email from the ASF dual-hosted git repository.
dhavalrajpara pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ranger.git
The following commit(s) were added to refs/heads/master by this push:
new b31611f7d RANGER-5271 : Last login date and time on Ranger Home page
(#632)
b31611f7d is described below
commit b31611f7de263bbc426e65f2967478cc75b57d1a
Author: Dhaval Rajpara <[email protected]>
AuthorDate: Wed Sep 3 12:01:15 2025 +0530
RANGER-5271 : Last login date and time on Ranger Home page (#632)
---
.../src/main/java/org/apache/ranger/biz/SessionMgr.java | 12 ++++++++++++
.../src/main/java/org/apache/ranger/biz/UserMgr.java | 1 +
.../java/org/apache/ranger/db/XXAuthSessionDao.java | 17 +++++++++++++++++
.../main/java/org/apache/ranger/view/VXPortalUser.java | 14 ++++++++++++++
.../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, 62 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 68874e86c..ae5e339a3 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
@@ -475,6 +475,18 @@ public synchronized void
refreshPermissionsIfNeeded(UserSessionBase userSession)
}
}
+ 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.equalsIgnoreCase(userSession.getXXPortalUser().getLoginId())) {
return true;
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 0686f37a5..5516ba43e 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
@@ -648,6 +648,7 @@ public VXPortalUser
mapXXPortalUserToVXPortalUser(XXPortalUser user, Collection<
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 3deb41e52..0f8016be7 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
@@ -68,6 +68,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 utcDate = DateUtil.getUTCDate();
Date authWindowStartTime = new Date((utcDate != null ?
utcDate.getTime() : System.currentTimeMillis()) - timeRangezSecond * 1000L);
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 c9ceeb0c4..f7e100855 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
@@ -23,9 +23,12 @@
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;
import org.apache.ranger.common.AppConstants;
+import org.apache.ranger.json.JsonDateSerializer;
import java.util.Collection;
+import java.util.Date;
import java.util.List;
import java.util.Map;
@@ -90,6 +93,8 @@ public class VXPortalUser extends VXDataObject implements
java.io.Serializable {
* sync Source Attribute.
*/
protected String syncSource;
+ @JsonSerialize(using = JsonDateSerializer.class)
+ protected Date lastLoginTime;
/**
* Configuration properties.
@@ -318,6 +323,7 @@ public String toString() {
str += "userRoleList={" + userRoleList + "} ";
str += "otherAttributes={" + otherAttributes + "} ";
str += "syncSource={" + syncSource + "} ";
+ str += "lastLoginTime={" + lastLoginTime + "} ";
str += "}";
return str;
}
@@ -385,4 +391,12 @@ public String getSyncSource() {
public void setSyncSource(final String syncSource) {
this.syncSource = syncSource;
}
+
+ public Date getLastLoginTime() {
+ return lastLoginTime;
+ }
+
+ public void setLastLoginTime(Date lastLoginTime) {
+ this.lastLoginTime = lastLoginTime;
+ }
}
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 8f61e66e3..265479422 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 51bc0e071..ea5f33e07 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
@@ -1317,7 +1317,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 d57082026..4006f4e04 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
@@ -107,7 +107,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();