[ 
https://issues.apache.org/jira/browse/KNOX-2778?focusedWorklogId=799680&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-799680
 ]

ASF GitHub Bot logged work on KNOX-2778:
----------------------------------------

                Author: ASF GitHub Bot
            Created on: 10/Aug/22 09:58
            Start Date: 10/Aug/22 09:58
    Worklog Time Spent: 10m 
      Work Description: MrtnBalazs commented on code in PR #615:
URL: https://github.com/apache/knox/pull/615#discussion_r942261075


##########
gateway-server/src/main/java/org/apache/knox/gateway/session/control/InMemoryConcurrentSessionVerifier.java:
##########
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.knox.gateway.session.control;
+
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.knox.gateway.config.GatewayConfig;
+import org.apache.knox.gateway.services.ServiceLifecycleException;
+import org.apache.knox.gateway.services.security.token.impl.JWT;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class InMemoryConcurrentSessionVerifier implements 
ConcurrentSessionVerifier {
+  private Set<String> privilegedUsers;
+  private Set<String> nonPrivilegedUsers;
+  private int privilegedUserConcurrentSessionLimit;
+  private int nonPrivilegedUserConcurrentSessionLimit;
+  private Map<String, Set<SessionJWT>> concurrentSessionCounter;
+  private final Lock sessionCountModifyLock = new ReentrantLock();
+
+  @Override
+  public boolean verifySessionForUser(String username, JWT jwtToken) {
+    if (!privilegedUsers.contains(username) && 
!nonPrivilegedUsers.contains(username)) {
+      return true;
+    }
+
+    sessionCountModifyLock.lock();
+    try {
+      int validTokenNumber = countValidTokensForUser(username);
+      if (privilegedUserCheckLimitReached(username, validTokenNumber) || 
nonPrivilegedUserCheckLimitReached(username, validTokenNumber)) {
+        return false;
+      }
+      concurrentSessionCounter.putIfAbsent(username, new HashSet<>());
+      concurrentSessionCounter.compute(username, (key, sessionTokenSet) -> 
addTokenForUser(sessionTokenSet, jwtToken));
+    } finally {
+      sessionCountModifyLock.unlock();
+    }
+    return true;
+  }
+
+  private int countValidTokensForUser(String username) {
+    return (int) concurrentSessionCounter
+            .getOrDefault(username, Collections.emptySet())
+            .stream()
+            .filter(each -> !each.hasExpired())
+            .count();
+  }
+
+  private boolean privilegedUserCheckLimitReached(String username, int 
validTokenNumber) {
+    if (privilegedUserConcurrentSessionLimit < 0) {
+      return false;
+    }
+    return privilegedUsers.contains(username) && (validTokenNumber >= 
privilegedUserConcurrentSessionLimit);
+  }
+
+  private boolean nonPrivilegedUserCheckLimitReached(String username, int 
validTokenNumber) {
+    if (nonPrivilegedUserConcurrentSessionLimit < 0) {
+      return false;
+    }
+    return nonPrivilegedUsers.contains(username) && (validTokenNumber >= 
nonPrivilegedUserConcurrentSessionLimit);
+  }
+
+  @Override
+  public void sessionEndedForUser(String username, String token) {
+    if (StringUtils.isNotBlank(token)) {
+      sessionCountModifyLock.lock();
+      try {
+        concurrentSessionCounter.computeIfPresent(username, (key, 
sessionTokenSet) -> removeTokenFromUser(sessionTokenSet, token));
+      } finally {
+        sessionCountModifyLock.unlock();
+      }
+    }
+  }
+
+  private Set<SessionJWT> removeTokenFromUser(Set<SessionJWT> sessionTokenSet, 
String token) {
+    sessionTokenSet.removeIf(sessionToken -> 
sessionToken.getToken().equals(token));
+    if (sessionTokenSet.isEmpty()) {
+      return null;
+    }
+    return sessionTokenSet;
+  }
+
+  private Set<SessionJWT> addTokenForUser(Set<SessionJWT> sessionTokenSet, JWT 
jwtToken) {
+    sessionTokenSet.add(new SessionJWT(jwtToken));
+    return sessionTokenSet;
+  }
+
+  @Override
+  public void init(GatewayConfig config, Map<String, String> options) throws 
ServiceLifecycleException {
+    this.privilegedUsers = config.getPrivilegedUsers();
+    this.nonPrivilegedUsers = config.getNonPrivilegedUsers();

Review Comment:
   Yes, there are two groups privileged and non-privileged, each has a limit. 
Users who are in neither of the groups can have unlimited sessions. @smolnar82 
mentioned at the begging of the task that there might be overlapping between 
the groups so we need to have two groups configured, but if you suggest that 
configuring one group is enough, than there might have been some 
misunderstanding in this topic.





Issue Time Tracking
-------------------

    Worklog Id:     (was: 799680)
    Time Spent: 4h 40m  (was: 4.5h)

> Enforce concurrent session limit in KnoxSSO
> -------------------------------------------
>
>                 Key: KNOX-2778
>                 URL: https://issues.apache.org/jira/browse/KNOX-2778
>             Project: Apache Knox
>          Issue Type: Sub-task
>          Components: Server
>    Affects Versions: 2.0.0
>            Reporter: Sandor Molnar
>            Assignee: Balazs Marton
>            Priority: Major
>             Fix For: 2.0.0
>
>          Time Spent: 4h 40m
>  Remaining Estimate: 0h
>
> Once, KNOX-2777 is ready, the next step is to wire that verifier 
> implementation into the KnoxSSO flow such as it throws an authorization error 
> (FORBIDDEN; 403) when a user tries to log in to UIs (both Knox's own UIs or 
> UIs proxied by Knox) but that user exceeds the configured concurrent session 
> limit.
> Basic logout handling should be covered too:
>  * manually clicking on the logout button
>  * subscribing to a session timeout event (you may want to talk to [~smore] 
> about this)



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to