update oidc+partey

This commit is contained in:
2022-05-12 09:32:54 +02:00
parent eb829bfa26
commit b876bad3e7
42 changed files with 3061 additions and 440 deletions
@@ -0,0 +1,67 @@
/**
*
*/
package de.bstly.we.businesslogic;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.Session;
import org.springframework.stereotype.Component;
import com.google.common.collect.Sets;
import de.bstly.we.model.User;
import de.bstly.we.security.LocalRememberMeServices;
import de.bstly.we.security.LocalRememberMeServices.ConcurrentToken;
/**
* The Class SessionManager.
*/
@Component
public class SessionManager {
@Autowired
private FindByIndexNameSessionRepository<? extends Session> sessionRepository;
@Autowired
private LocalRememberMeServices rememberMeServices;
/**
* Delete sessions for user.
*
* @param user the user
*/
public void deleteSessionsForUser(User user) {
Map<String, ? extends Session> usersSessions = sessionRepository.findByPrincipalName(user.getUsername());
for (Session session : usersSessions.values()) {
if (session.getAttribute("userId") != null && session.getAttribute("userId").equals(user.getId())) {
sessionRepository.deleteById(session.getId());
}
}
}
/**
* Concurrent clean up.
*/
@Scheduled(cron = "${rememberme.concurrent.cron:0 */5 * * * * }")
public void concurrentCleanUp() {
Set<String> delete = Sets.newHashSet();
for (Entry<String, ConcurrentToken> entry : rememberMeServices.getConcurrentTokens().entrySet()) {
if (Instant.now().minus(rememberMeServices.getConcurrentTimeout(), ChronoUnit.SECONDS)
.isAfter(entry.getValue().getRenewed())) {
delete.add(entry.getKey());
}
}
for (String key : delete) {
rememberMeServices.getConcurrentTokens().remove(key);
}
}
}
@@ -39,8 +39,8 @@ import de.bstly.we.model.PermissionMapping;
import de.bstly.we.model.ProfileFieldType;
import de.bstly.we.model.User;
import de.bstly.we.model.UserProfileField;
import de.bstly.we.model.Visibility;
import de.bstly.we.model.UserStatus;
import de.bstly.we.model.Visibility;
/**
* The Class UserController.
@@ -3,17 +3,42 @@
*/
package de.bstly.we.security;
import javax.servlet.http.HttpServletRequest;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.log.LogMessage;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.rememberme.CookieTheftException;
import org.springframework.security.web.authentication.rememberme.InvalidCookieException;
import org.springframework.security.web.authentication.rememberme.PersistentRememberMeToken;
import org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationException;
import com.google.common.collect.Maps;
/**
* The Class LocalRememberMeServices.
*/
public class LocalRememberMeServices extends PersistentTokenBasedRememberMeServices {
private PersistentTokenRepository tokenRepository;
@Value("${rememberme.concurrent.enabled:true}")
private boolean concurrent;
@Value("${rememberme.concurrent.timeout:120}")
private long concurrentTimeout; // default 2 minutes
private Map<String, ConcurrentToken> concurrentTokens = Maps.newHashMap();
/**
* Instantiates a new local remember me services.
*
@@ -24,6 +49,7 @@ public class LocalRememberMeServices extends PersistentTokenBasedRememberMeServi
public LocalRememberMeServices(String key, UserDetailsService userDetailsService,
PersistentTokenRepository tokenRepository) {
super(key, userDetailsService, tokenRepository);
this.tokenRepository = tokenRepository;
}
/*
@@ -45,4 +71,174 @@ public class LocalRememberMeServices extends PersistentTokenBasedRememberMeServi
return super.rememberMeRequested(request, parameter);
}
@Override
protected UserDetails processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request,
HttpServletResponse response) {
if (cookieTokens.length != 2) {
throw new InvalidCookieException("Cookie token did not contain " + 2 + " tokens, but contained '"
+ Arrays.asList(cookieTokens) + "'");
}
String presentedSeries = cookieTokens[0];
String presentedToken = cookieTokens[1];
PersistentRememberMeToken token = this.tokenRepository.getTokenForSeries(presentedSeries);
if (token == null) {
// No series match, so we can't authenticate using this cookie
throw new RememberMeAuthenticationException("No persistent token found for series id: " + presentedSeries);
}
boolean concurrentToken = false;
String newTokenData = generateTokenData();
// We have a match for this user/series combination
this.logger.trace("concurrentTokens: " + concurrentTokens);
if (!presentedToken.equals(token.getTokenValue())) {
if (concurrent && concurrentTokens.containsKey(presentedToken)) {
this.logger.debug("found token '" + presentedToken + "' in concurrent tokens");
if (concurrentTokens.get(presentedToken).getSeries().equals(presentedSeries)) {
this.logger.debug("found series '" + presentedSeries + "' in concurrent tokens");
if (Instant.now().minus(concurrentTimeout, ChronoUnit.SECONDS)
.isBefore(concurrentTokens.get(presentedToken).getRenewed())) {
newTokenData = token.getTokenValue();
concurrentToken = true;
} else {
this.logger.debug("cocurrent token expired!");
concurrentTokens.remove(presentedToken);
}
}
}
if (!concurrentToken) {
// Token doesn't match series value. Delete all logins for this user and throw
// an exception to warn them.
this.tokenRepository.removeUserTokens(token.getUsername());
throw new CookieTheftException(this.messages.getMessage(
"PersistentTokenBasedRememberMeServices.cookieStolen",
"Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack."));
}
}
if (token.getDate().getTime() + getTokenValiditySeconds() * 1000L < System.currentTimeMillis()) {
throw new RememberMeAuthenticationException("Remember-me login has expired");
}
// Token also matches, so login is valid. Update the token value, keeping the
// *same* series number.
this.logger.debug(LogMessage.format("Refreshing persistent login token for user '%s', series '%s'",
token.getUsername(), token.getSeries()));
if (!concurrentToken && !concurrentTokens.containsKey(presentedToken)) {
concurrentTokens.put(presentedToken, new ConcurrentToken(presentedSeries, Instant.now()));
}
PersistentRememberMeToken newToken = new PersistentRememberMeToken(token.getUsername(), token.getSeries(),
newTokenData, new Date());
try {
this.tokenRepository.updateToken(newToken.getSeries(), newToken.getTokenValue(), newToken.getDate());
addCookie(newToken, request, response);
} catch (Exception ex) {
this.logger.error("Failed to update token: ", ex);
throw new RememberMeAuthenticationException("Autologin failed due to data access problem");
}
return getUserDetailsService().loadUserByUsername(token.getUsername());
}
/**
* Removes the user tokens.
*
* @param username the username
*/
public void removeUserTokens(String username) {
tokenRepository.removeUserTokens(username);
}
/**
* Adds the cookie.
*
* @param token the token
* @param request the request
* @param response the response
*/
private void addCookie(PersistentRememberMeToken token, HttpServletRequest request, HttpServletResponse response) {
setCookie(new String[] { token.getSeries(), token.getTokenValue() }, getTokenValiditySeconds(), request,
response);
}
/**
* Gets the concurrent tokens.
*
* @return the concurrent tokens
*/
public Map<String, ConcurrentToken> getConcurrentTokens() {
if (concurrentTokens == null) {
concurrentTokens = Maps.newHashMap();
}
return concurrentTokens;
}
/**
* Gets the concurrent timeout.
*
* @return the concurrent timeout
*/
public long getConcurrentTimeout() {
return concurrentTimeout;
}
/**
* The Class ConcurrentToken.
*/
public static class ConcurrentToken {
private String series;
private Instant renewed;
/**
* Instantiates a new concurrent token.
*
* @param series the series
* @param renewed the renewed
*/
public ConcurrentToken(String series, Instant renewed) {
super();
this.series = series;
this.renewed = renewed;
}
/**
* Gets the series.
*
* @return the series
*/
public String getSeries() {
return series;
}
/**
* Sets the series.
*
* @param series the new series
*/
public void setSeries(String series) {
this.series = series;
}
/**
* Gets the renewed.
*
* @return the renewed
*/
public Instant getRenewed() {
return renewed;
}
/**
* Sets the renewed.
*
* @param renewed the new renewed
*/
public void setRenewed(Instant renewed) {
this.renewed = renewed;
}
}
}