keycloak-aplcache
Changes
integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java 129(+100 -29)
Details
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java
index c9470db..90c8d9f 100755
--- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java
@@ -20,7 +20,12 @@ import org.keycloak.adapters.RefreshableKeycloakSession;
import org.keycloak.adapters.ResourceMetadata;
import org.keycloak.adapters.as7.config.CatalinaAdapterConfigLoader;
import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.adapters.action.AdminAction;
import org.keycloak.representations.adapters.action.PushNotBeforeAction;
+import org.keycloak.representations.adapters.action.SessionStats;
+import org.keycloak.representations.adapters.action.SessionStatsAction;
+import org.keycloak.representations.adapters.action.UserStats;
+import org.keycloak.representations.adapters.action.UserStatsAction;
import org.keycloak.representations.adapters.config.AdapterConfig;
import org.keycloak.adapters.config.RealmConfiguration;
import org.keycloak.adapters.config.RealmConfigurationLoader;
@@ -35,7 +40,9 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
import java.util.Set;
/**
@@ -99,6 +106,21 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
return; // we failed to verify the request
}
pushNotBefore(input, response);
+ return;
+ } else if (requestURI.endsWith(AdapterConstants.K_GET_SESSION_STATS)) {
+ JWSInput input = verifyAdminRequest(request, response);
+ if (input == null) {
+ return; // we failed to verify the request
+ }
+ getSessionStats(input, response);
+ return;
+ } else if (requestURI.endsWith(AdapterConstants.K_GET_USER_STATS)) {
+ JWSInput input = verifyAdminRequest(request, response);
+ if (input == null) {
+ return; // we failed to verify the request
+ }
+ getUserStats(input, response);
+ return;
}
checkKeycloakSession(request);
super.invoke(request, response);
@@ -136,63 +158,112 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
String token = StreamUtil.readString(request.getInputStream());
if (token == null) {
log.warn("admin request failed, no token");
- response.sendError(HttpServletResponse.SC_FORBIDDEN, "no token");
+ response.sendError(403, "no token");
return null;
}
JWSInput input = new JWSInput(token);
boolean verified = false;
try {
- verified = RSAProvider.verify(input, resourceMetadata.getRealmKey());
+ verified = RSAProvider.verify(input, realmConfiguration.getMetadata().getRealmKey());
} catch (Exception ignore) {
}
if (!verified) {
log.warn("admin request failed, unable to verify token");
- response.sendError(HttpServletResponse.SC_FORBIDDEN, "verification failed");
+ response.sendError(403, "verification failed");
return null;
}
return input;
}
+
+ protected boolean validateAction(HttpServletResponse response, AdminAction action) throws IOException {
+ if (!action.validate()) {
+ log.warn("admin request failed, not validated" + action.getAction());
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Not validated");
+ return false;
+ }
+ if (action.isExpired()) {
+ log.warn("admin request failed, expired token");
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Expired token");
+ return false;
+ }
+ if (!resourceMetadata.getResourceName().equals(action.getResource())) {
+ log.warn("Resource name does not match");
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Resource name does not match");
+ return false;
+
+ }
+ return true;
+ }
+
protected void pushNotBefore(JWSInput token, HttpServletResponse response) throws IOException {
- try {
- log.info("->> pushNotBefore: ");
- PushNotBeforeAction action = JsonSerialization.readValue(token.getContent(), PushNotBeforeAction.class);
- if (action.isExpired()) {
- log.warn("admin request failed, expired token");
- response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Expired token");
- return;
- }
- if (!resourceMetadata.getResourceName().equals(action.getResource())) {
- log.warn("Resource name does not match");
- response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Resource name does not match");
- return;
+ log.info("->> pushNotBefore: ");
+ PushNotBeforeAction action = JsonSerialization.readValue(token.getContent(), PushNotBeforeAction.class);
+ if (!validateAction(response, action)) {
+ return;
+ }
+ realmConfiguration.setNotBefore(action.getNotBefore());
+ response.setStatus(HttpServletResponse.SC_NO_CONTENT);
+ }
+
+ protected UserStats getUserStats(String user) {
+ UserStats stats = new UserStats();
+ Long loginTime = userSessionManagement.getUserLoginTime(user);
+ if (loginTime != null) {
+ stats.setLoggedIn(true);
+ stats.setWhenLoggedIn(loginTime);
+ } else {
+ stats.setLoggedIn(false);
+ }
+ return stats;
+ }
+
+
+ protected void getSessionStats(JWSInput token, HttpServletResponse response) throws IOException {
+ log.info("->> getSessionStats: ");
+ SessionStatsAction action = JsonSerialization.readValue(token.getContent(), SessionStatsAction.class);
+ if (!validateAction(response, action)) {
+ return;
+ }
+ SessionStats stats = new SessionStats();
+ stats.setActiveSessions(userSessionManagement.getActiveSessions());
+ stats.setActiveUsers(userSessionManagement.getActiveUsers().size());
+ if (action.isListUsers() && userSessionManagement.getActiveSessions() > 0) {
+ Map<String, UserStats> list = new HashMap<String, UserStats>();
+ for (String user : userSessionManagement.getActiveUsers()) {
+ list.put(user, getUserStats(user));
}
- realmConfiguration.setNotBefore(action.getNotBefore());
- } catch (Exception e) {
- log.warn("failed to logout", e);
- response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Failed to logout");
+ stats.setUsers(list);
}
- response.setStatus(HttpServletResponse.SC_NO_CONTENT);
+ response.setStatus(200);
+ response.setContentType("application/json");
+ JsonSerialization.writeValueToStream(response.getOutputStream(), stats);
+
+ }
+ protected void getUserStats(JWSInput token, HttpServletResponse response) throws IOException {
+ log.info("->> getUserStats: ");
+ UserStatsAction action = JsonSerialization.readValue(token.getContent(), UserStatsAction.class);
+ if (!validateAction(response, action)) {
+ return;
+ }
+ String user = action.getUser();
+ UserStats stats = getUserStats(user);
+ response.setStatus(200);
+ response.setContentType("application/json");
+ JsonSerialization.writeValueToStream(response.getOutputStream(), stats);
}
+
protected void remoteLogout(JWSInput token, HttpServletResponse response) throws IOException {
try {
log.debug("->> remoteLogout: ");
LogoutAction action = JsonSerialization.readValue(token.getContent(), LogoutAction.class);
- if (action.isExpired()) {
- log.warn("admin request failed, expired token");
- response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Expired token");
+ if (!validateAction(response, action)) {
return;
}
- if (!resourceMetadata.getResourceName().equals(action.getResource())) {
- log.warn("Resource name does not match");
- response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Resource name does not match");
- return;
-
- }
String user = action.getUser();
if (user != null) {
log.debug("logout of session for: " + user);
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/UserSessionManagement.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/UserSessionManagement.java
index 0d18f1a..5e829bf 100755
--- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/UserSessionManagement.java
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/UserSessionManagement.java
@@ -8,8 +8,10 @@ import org.jboss.logging.Logger;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
@@ -20,19 +22,63 @@ import java.util.concurrent.ConcurrentHashMap;
*/
public class UserSessionManagement implements SessionListener {
private static final Logger log = Logger.getLogger(UserSessionManagement.class);
- protected ConcurrentHashMap<String, Map<String, Session>> userSessionMap = new ConcurrentHashMap<String, Map<String, Session>>();
+ protected ConcurrentHashMap<String, UserSessions> userSessionMap = new ConcurrentHashMap<String, UserSessions>();
- protected void login(Session session, String username) {
- Map<String, Session> map = userSessionMap.get(username);
- if (map == null) {
- final Map<String, Session> value = new HashMap<String, Session>();
- map = userSessionMap.putIfAbsent(username, value);
- if (map == null) {
- map = value;
+ public static class UserSessions {
+ protected Map<String, Session> sessions = new ConcurrentHashMap<String, Session>();
+ protected long loggedIn = System.currentTimeMillis();
+
+
+ public Map<String, Session> getSessions() {
+ return sessions;
+ }
+
+ public long getLoggedIn() {
+ return loggedIn;
+ }
+ }
+
+ public int getNumUserLogins() {
+ return userSessionMap.size();
+ }
+
+ public int getActiveSessions() {
+ int active = 0;
+ synchronized (userSessionMap) {
+ for (UserSessions sessions : userSessionMap.values()) {
+ active += sessions.getSessions().size();
}
+
}
- synchronized (map) {
- map.put(session.getId(), session);
+ return active;
+ }
+
+ /**
+ *
+ * @param username
+ * @return null if user not logged in
+ */
+ public Long getUserLoginTime(String username) {
+ UserSessions sessions = userSessionMap.get(username);
+ if (sessions == null) return null;
+ return sessions.getLoggedIn();
+ }
+
+ public Set<String> getActiveUsers() {
+ HashSet<String> set = new HashSet<String>();
+ set.addAll(userSessionMap.keySet());
+ return set;
+ }
+
+
+ protected void login(Session session, String username) {
+ synchronized (userSessionMap) {
+ UserSessions userSessions = userSessionMap.get(username);
+ if (userSessions == null) {
+ userSessions = new UserSessions();
+ userSessionMap.put(username, userSessions);
+ }
+ userSessions.getSessions().put(session.getId(), session);
}
session.addSessionListener(this);
}
@@ -43,32 +89,24 @@ public class UserSessionManagement implements SessionListener {
for (String user : users) logout(user);
}
- public void logoutAllBut(String but) {
- List<String> users = new ArrayList<String>();
- users.addAll(userSessionMap.keySet());
- for (String user : users) {
- if (!but.equals(user)) logout(user);
- }
- }
-
-
public void logout(String user) {
log.debug("logoutUser: " + user);
- Map<String, Session> map = userSessionMap.remove(user);
- if (map == null) {
+ UserSessions sessions = null;
+ synchronized (userSessionMap) {
+ sessions = userSessionMap.remove(user);
+
+ }
+ if (sessions == null) {
log.debug("no session for user: " + user);
return;
+
}
log.debug("found session for user");
- synchronized (map) {
- for (Session session : map.values()) {
- log.debug("invalidating session for user: " + user);
- session.setPrincipal(null);
- session.setAuthType(null);
- session.getSession().invalidate();
- }
+ for (Session session : sessions.getSessions().values()) {
+ session.setPrincipal(null);
+ session.setAuthType(null);
+ session.getSession().invalidate();
}
-
}
public void sessionEvent(SessionEvent event) {
@@ -85,13 +123,14 @@ public class UserSessionManagement implements SessionListener {
session.setAuthType(null);
String username = principal.getUserPrincipal().getName();
- Map<String, Session> map = userSessionMap.get(username);
- if (map == null) return;
- synchronized (map) {
- map.remove(session.getId());
- if (map.isEmpty()) userSessionMap.remove(username);
+ synchronized (userSessionMap) {
+ UserSessions sessions = userSessionMap.get(username);
+ if (sessions != null) {
+ sessions.getSessions().remove(session.getId());
+ if (sessions.getSessions().isEmpty()) {
+ userSessionMap.remove(username);
+ }
+ }
}
-
-
}
}
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletAdminActionsHandler.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletAdminActionsHandler.java
index 092b99b..0e329b4 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletAdminActionsHandler.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletAdminActionsHandler.java
@@ -69,27 +69,6 @@ public class ServletAdminActionsHandler implements HttpHandler {
this.realmConfig = realmConfig;
}
- protected JWSInput verifyAdminRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
- String token = StreamUtil.readString(request.getInputStream());
- if (token == null) {
- log.warn("admin request failed, no token");
- response.sendError(StatusCodes.FORBIDDEN, "no token");
- return null;
- }
-
- JWSInput input = new JWSInput(token);
- boolean verified = false;
- try {
- verified = RSAProvider.verify(input, realmConfig.getMetadata().getRealmKey());
- } catch (Exception ignore) {
- }
- if (!verified) {
- log.warn("admin request failed, unable to verify token");
- response.sendError(StatusCodes.FORBIDDEN, "verification failed");
- return null;
- }
- return input;
- }
@@ -135,6 +114,29 @@ public class ServletAdminActionsHandler implements HttpHandler {
return;
}
+ protected JWSInput verifyAdminRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ String token = StreamUtil.readString(request.getInputStream());
+ if (token == null) {
+ log.warn("admin request failed, no token");
+ response.sendError(StatusCodes.FORBIDDEN, "no token");
+ return null;
+ }
+
+ JWSInput input = new JWSInput(token);
+ boolean verified = false;
+ try {
+ verified = RSAProvider.verify(input, realmConfig.getMetadata().getRealmKey());
+ } catch (Exception ignore) {
+ }
+ if (!verified) {
+ log.warn("admin request failed, unable to verify token");
+ response.sendError(StatusCodes.FORBIDDEN, "verification failed");
+ return null;
+ }
+ return input;
+ }
+
+
protected boolean validateAction(HttpServletResponse response, AdminAction action) throws IOException {
if (!action.validate()) {
log.warn("admin request failed, not validated" + action.getAction());