rameeshm commented on code in PR #1200:
URL: https://github.com/apache/ranger/pull/1200#discussion_r3907133628


##########
security-admin/src/main/java/org/apache/ranger/biz/SessionMgr.java:
##########
@@ -498,6 +527,183 @@ public Date getLastSuccessLoginAuthTimeByUserId(String 
loginId) {
         return null;
     }
 
+    public static boolean isConcurrentSessionExpired(HttpSession session) {
+        if (session == null) {
+            return false;
+        }
+
+        try {
+            return 
Boolean.TRUE.equals(session.getAttribute(SESSION_ATTR_CONCURRENT_EXPIRED));
+        } catch (IllegalStateException e) {
+            return false;
+        }
+    }
+
+    public static boolean isConcurrentSessionExpiredSso(HttpSession session) {
+        if (session == null) {
+            return false;
+        }
+
+        try {
+            return 
Boolean.TRUE.equals(session.getAttribute(SESSION_ATTR_CONCURRENT_EXPIRED_SSO));
+        } catch (IllegalStateException e) {
+            return false;
+        }
+    }
+
+    /**
+     * When {@code ranger.session.limit.concurrency} is exceeded, expire the 
oldest UI sessions
+     * so the new login succeeds. SSO sessions are marked expired for Knox 
logout redirect.
+     * The count is taken from this JVM's in-memory session list, not 
cluster-wide.
+     * Find-and-expire is serialized per loginId so two concurrent UI logins 
for the same user
+     * cannot both observe a count under the limit.
+     */
+    protected void enforceConcurrentSessionLimit(String loginId, HttpSession 
currentSession) {
+        int limit = 
PropertiesUtil.getIntProperty(PROP_SESSION_LIMIT_CONCURRENCY, 0);
+
+        if (limit <= 0 || StringUtils.isBlank(loginId) || currentSession == 
null) {
+            return;
+        }
+
+        Object lock = 
CONCURRENT_SESSION_LOCKS.computeIfAbsent(loginId.toLowerCase(Locale.ROOT), id 
-> new Object());
+
+        synchronized (lock) {
+            List<HttpSession> otherSessions = 
findActiveUiSessionsForUser(loginId, currentSession);
+
+            if (otherSessions.size() < limit) {
+                return;
+            }
+
+            otherSessions.sort(Comparator.comparingLong(session -> {
+                try {
+                    return session.getCreationTime();
+                } catch (IllegalStateException e) {
+                    return 0L;
+                }
+            }));
+
+            int toExpire = otherSessions.size() - limit + 1;
+
+            logger.info("Concurrent session limit {} exceeded for user {}; 
expiring {} older session(s)", limit, loginId, toExpire);
+
+            for (int i = 0; i < toExpire; i++) {
+                expireConcurrentSession(otherSessions.get(i));
+            }
+        }
+    }
+
+    /**
+     * Plugin and secure download URLs. Used both to skip x_auth_sess rows 
(unless
+     * {@code ranger.downloadpolicy.session.log.enabled} is true) and to 
exclude
+     * those sessions from the UI concurrent-session quota.
+     */
+    static boolean isPluginOrSecureDownloadRequest(String uri) {
+        if (StringUtils.isEmpty(uri)) {
+            return false;
+        }
+
+        return uri.contains("/secure/policies/download/")
+                || uri.contains("/secure/download/")
+                || uri.contains("/plugins/policies/download/")
+                || uri.contains("/tags/download/")
+                || uri.contains("/roles/download/")
+                || uri.contains("/xusers/download/")
+                || uri.contains("/gds/download/");
+    }
+
+    static boolean isBrowserUserAgent(String userAgent) {
+        if (StringUtils.isBlank(userAgent)) {
+            return false;
+        }
+
+        String agents = 
PropertiesUtil.getProperty("ranger.krb.browser-useragents-regex", 
DEFAULT_BROWSER_USER_AGENTS);
+
+        if (StringUtils.isBlank(agents)) {
+            agents = DEFAULT_BROWSER_USER_AGENTS;
+        }
+
+        for (String agentPrefix : agents.split(",")) {
+            if (StringUtils.isNotBlank(agentPrefix) && 
userAgent.toLowerCase().startsWith(agentPrefix.trim().toLowerCase())) {

Review Comment:
   userAgent.toLowerCase() can be sent this method instead of looping and 
converting it again and again.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to