update oidc+partey
This commit is contained in:
parent
eb829bfa26
commit
b876bad3e7
@ -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.ProfileFieldType;
|
||||||
import de.bstly.we.model.User;
|
import de.bstly.we.model.User;
|
||||||
import de.bstly.we.model.UserProfileField;
|
import de.bstly.we.model.UserProfileField;
|
||||||
import de.bstly.we.model.Visibility;
|
|
||||||
import de.bstly.we.model.UserStatus;
|
import de.bstly.we.model.UserStatus;
|
||||||
|
import de.bstly.we.model.Visibility;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Class UserController.
|
* The Class UserController.
|
||||||
|
@ -3,17 +3,42 @@
|
|||||||
*/
|
*/
|
||||||
package de.bstly.we.security;
|
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.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.PersistentTokenBasedRememberMeServices;
|
||||||
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
|
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.
|
* The Class LocalRememberMeServices.
|
||||||
*/
|
*/
|
||||||
public class LocalRememberMeServices extends PersistentTokenBasedRememberMeServices {
|
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.
|
* Instantiates a new local remember me services.
|
||||||
*
|
*
|
||||||
@ -24,6 +49,7 @@ public class LocalRememberMeServices extends PersistentTokenBasedRememberMeServi
|
|||||||
public LocalRememberMeServices(String key, UserDetailsService userDetailsService,
|
public LocalRememberMeServices(String key, UserDetailsService userDetailsService,
|
||||||
PersistentTokenRepository tokenRepository) {
|
PersistentTokenRepository tokenRepository) {
|
||||||
super(key, userDetailsService, tokenRepository);
|
super(key, userDetailsService, tokenRepository);
|
||||||
|
this.tokenRepository = tokenRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -45,4 +71,174 @@ public class LocalRememberMeServices extends PersistentTokenBasedRememberMeServi
|
|||||||
return super.rememberMeRequested(request, parameter);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -149,27 +149,21 @@ public class JukeboxManager implements SmartInitializingSingleton {
|
|||||||
if (!StringUtils.hasText(config.getAccessToken()) || config.getExpires() == null
|
if (!StringUtils.hasText(config.getAccessToken()) || config.getExpires() == null
|
||||||
|| config.getExpires().isBefore(Instant.now())) {
|
|| config.getExpires().isBefore(Instant.now())) {
|
||||||
|
|
||||||
String authHeader = "Basic "
|
String authHeader = "Basic " + new String(Base64.getEncoder()
|
||||||
+ new String(Base64.getEncoder()
|
.encode(String.format("%s:%s", config.getClientId(), config.getClientSecret()).getBytes()));
|
||||||
.encode(String
|
WebClient.RequestBodySpec request = WebClient.builder().baseUrl(config.getAccountUrl()).build()
|
||||||
.format("%s:%s", config.getClientId(), config.getClientSecret())
|
.method(HttpMethod.POST).uri(uriBuilder -> uriBuilder.path("/api/token").build())
|
||||||
.getBytes()));
|
|
||||||
WebClient.RequestBodySpec request = WebClient.builder().baseUrl(config.getAccountUrl())
|
|
||||||
.build().method(HttpMethod.POST)
|
|
||||||
.uri(uriBuilder -> uriBuilder.path("/api/token").build())
|
|
||||||
.header(HttpHeaders.AUTHORIZATION, authHeader)
|
.header(HttpHeaders.AUTHORIZATION, authHeader)
|
||||||
.header(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded");
|
.header(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded");
|
||||||
|
|
||||||
request.bodyValue("grant_type=refresh_token&refresh_token="
|
request.bodyValue("grant_type=refresh_token&refresh_token=" + config.getRefreshToken());
|
||||||
+ config.getRefreshToken());
|
|
||||||
|
|
||||||
String jsonString = request.retrieve().bodyToMono(String.class).block();
|
String jsonString = request.retrieve().bodyToMono(String.class).block();
|
||||||
|
|
||||||
if (StringUtils.hasText(jsonString)) {
|
if (StringUtils.hasText(jsonString)) {
|
||||||
JsonObject response = JsonParser.parseString(jsonString).getAsJsonObject();
|
JsonObject response = JsonParser.parseString(jsonString).getAsJsonObject();
|
||||||
config.setAccessToken(response.get("access_token").getAsString());
|
config.setAccessToken(response.get("access_token").getAsString());
|
||||||
config.setExpires(Instant.now().plus(response.get("expires_in").getAsLong(),
|
config.setExpires(Instant.now().plus(response.get("expires_in").getAsLong(), ChronoUnit.SECONDS));
|
||||||
ChronoUnit.SECONDS));
|
|
||||||
|
|
||||||
if (response.has("refresh_token")) {
|
if (response.has("refresh_token")) {
|
||||||
config.setRefreshToken(response.get("refresh_token").getAsString());
|
config.setRefreshToken(response.get("refresh_token").getAsString());
|
||||||
@ -197,8 +191,7 @@ public class JukeboxManager implements SmartInitializingSingleton {
|
|||||||
|
|
||||||
WebClient.RequestBodySpec request = webClient.method(HttpMethod.GET)
|
WebClient.RequestBodySpec request = webClient.method(HttpMethod.GET)
|
||||||
.uri(uriBuilder -> uriBuilder.path("/v1/me/player").build())
|
.uri(uriBuilder -> uriBuilder.path("/v1/me/player").build())
|
||||||
.header(HttpHeaders.AUTHORIZATION, "Bearer "
|
.header(HttpHeaders.AUTHORIZATION, "Bearer " + config.getAccessToken());
|
||||||
+ config.getAccessToken());
|
|
||||||
|
|
||||||
String jsonString = request.retrieve().bodyToMono(String.class).block();
|
String jsonString = request.retrieve().bodyToMono(String.class).block();
|
||||||
|
|
||||||
@ -222,11 +215,9 @@ public class JukeboxManager implements SmartInitializingSingleton {
|
|||||||
device_ids.add(config.getDeviceId());
|
device_ids.add(config.getDeviceId());
|
||||||
queryParameters.add("device_ids", device_ids.toString());
|
queryParameters.add("device_ids", device_ids.toString());
|
||||||
queryParameters.add("play", "true");
|
queryParameters.add("play", "true");
|
||||||
request = webClient.method(HttpMethod.PUT)
|
request = webClient.method(HttpMethod.PUT).uri(
|
||||||
.uri(uriBuilder -> uriBuilder.path("/v1/me/player")
|
uriBuilder -> uriBuilder.path("/v1/me/player").queryParams(queryParameters).build())
|
||||||
.queryParams(queryParameters).build())
|
.header(HttpHeaders.AUTHORIZATION, "Bearer " + config.getAccessToken());
|
||||||
.header(HttpHeaders.AUTHORIZATION, "Bearer "
|
|
||||||
+ config.getAccessToken());
|
|
||||||
|
|
||||||
request.retrieve().bodyToMono(String.class).block();
|
request.retrieve().bodyToMono(String.class).block();
|
||||||
return getStatus();
|
return getStatus();
|
||||||
@ -280,19 +271,15 @@ public class JukeboxManager implements SmartInitializingSingleton {
|
|||||||
queryParameters.add("context_uri", context_uri);
|
queryParameters.add("context_uri", context_uri);
|
||||||
}
|
}
|
||||||
WebClient.RequestBodySpec request = webClient.method(HttpMethod.PUT)
|
WebClient.RequestBodySpec request = webClient.method(HttpMethod.PUT)
|
||||||
.uri(uriBuilder -> uriBuilder.path("/v1/me/player/play")
|
.uri(uriBuilder -> uriBuilder.path("/v1/me/player/play").queryParams(queryParameters).build())
|
||||||
.queryParams(queryParameters).build())
|
.header(HttpHeaders.AUTHORIZATION, "Bearer " + config.getAccessToken());
|
||||||
.header(HttpHeaders.AUTHORIZATION, "Bearer "
|
|
||||||
+ config.getAccessToken());
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
request.retrieve().bodyToMono(String.class).block();
|
request.retrieve().bodyToMono(String.class).block();
|
||||||
logger.debug("started playback");
|
logger.debug("started playback");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (!StringUtils.hasText(context_uri)
|
if (!StringUtils.hasText(context_uri) && StringUtils.hasText(config.getFallbackContextId())) {
|
||||||
&& StringUtils.hasText(config.getFallbackContextId())) {
|
logger.debug("retry to start playback with: " + config.getFallbackContextId());
|
||||||
logger.debug("retry to start playback with: "
|
|
||||||
+ config.getFallbackContextId());
|
|
||||||
tryStartPlayback(config.getFallbackContextId());
|
tryStartPlayback(config.getFallbackContextId());
|
||||||
} else {
|
} else {
|
||||||
config.setActive(false);
|
config.setActive(false);
|
||||||
@ -321,13 +308,12 @@ public class JukeboxManager implements SmartInitializingSingleton {
|
|||||||
if (offset != null) {
|
if (offset != null) {
|
||||||
queryParameters.add("offset", String.valueOf(offset));
|
queryParameters.add("offset", String.valueOf(offset));
|
||||||
}
|
}
|
||||||
WebClient.RequestBodySpec request = webClient.method(HttpMethod.GET).uri(
|
WebClient.RequestBodySpec request = webClient.method(HttpMethod.GET)
|
||||||
uriBuilder -> uriBuilder.path("/v1/search").queryParams(queryParameters).build())
|
.uri(uriBuilder -> uriBuilder.path("/v1/search").queryParams(queryParameters).build())
|
||||||
.header(HttpHeaders.AUTHORIZATION, "Bearer "
|
.header(HttpHeaders.AUTHORIZATION, "Bearer " + config.getAccessToken());
|
||||||
+ config.getAccessToken());
|
|
||||||
|
|
||||||
String jsonString = request.retrieve().bodyToMono(String.class)
|
String jsonString = request.retrieve().bodyToMono(String.class).onErrorResume(e -> Mono.just(e.getMessage()))
|
||||||
.onErrorResume(e -> Mono.just(e.getMessage())).block();
|
.block();
|
||||||
|
|
||||||
if (StringUtils.hasText(jsonString)) {
|
if (StringUtils.hasText(jsonString)) {
|
||||||
return JsonParser.parseString(jsonString);
|
return JsonParser.parseString(jsonString);
|
||||||
@ -352,14 +338,12 @@ public class JukeboxManager implements SmartInitializingSingleton {
|
|||||||
if (limit != null) {
|
if (limit != null) {
|
||||||
queryParameters.add("limit", String.valueOf(limit));
|
queryParameters.add("limit", String.valueOf(limit));
|
||||||
}
|
}
|
||||||
WebClient.RequestBodySpec request = webClient.method(HttpMethod.GET)
|
WebClient.RequestBodySpec request = webClient.method(HttpMethod.GET).uri(
|
||||||
.uri(uriBuilder -> uriBuilder.path("/v1/me/player/recently-played")
|
uriBuilder -> uriBuilder.path("/v1/me/player/recently-played").queryParams(queryParameters).build())
|
||||||
.queryParams(queryParameters).build())
|
.header(HttpHeaders.AUTHORIZATION, "Bearer " + config.getAccessToken());
|
||||||
.header(HttpHeaders.AUTHORIZATION, "Bearer "
|
|
||||||
+ config.getAccessToken());
|
|
||||||
|
|
||||||
String jsonString = request.retrieve().bodyToMono(String.class)
|
String jsonString = request.retrieve().bodyToMono(String.class).onErrorResume(e -> Mono.just(e.getMessage()))
|
||||||
.onErrorResume(e -> Mono.just(e.getMessage())).block();
|
.block();
|
||||||
|
|
||||||
if (StringUtils.hasText(jsonString)) {
|
if (StringUtils.hasText(jsonString)) {
|
||||||
return JsonParser.parseString(jsonString);
|
return JsonParser.parseString(jsonString);
|
||||||
@ -396,10 +380,8 @@ public class JukeboxManager implements SmartInitializingSingleton {
|
|||||||
MultiValueMap<String, String> queryParameters = new LinkedMultiValueMap<String, String>();
|
MultiValueMap<String, String> queryParameters = new LinkedMultiValueMap<String, String>();
|
||||||
queryParameters.add("device_id", config.getDeviceId());
|
queryParameters.add("device_id", config.getDeviceId());
|
||||||
WebClient.RequestBodySpec request = webClient.method(HttpMethod.PUT)
|
WebClient.RequestBodySpec request = webClient.method(HttpMethod.PUT)
|
||||||
.uri(uriBuilder -> uriBuilder.path("/v1/me/player/play")
|
.uri(uriBuilder -> uriBuilder.path("/v1/me/player/play").queryParams(queryParameters).build())
|
||||||
.queryParams(queryParameters).build())
|
.header(HttpHeaders.AUTHORIZATION, "Bearer " + config.getAccessToken());
|
||||||
.header(HttpHeaders.AUTHORIZATION, "Bearer "
|
|
||||||
+ config.getAccessToken());
|
|
||||||
|
|
||||||
JsonObject body = new JsonObject();
|
JsonObject body = new JsonObject();
|
||||||
if (StringUtils.hasText(config.getFallbackContextId())) {
|
if (StringUtils.hasText(config.getFallbackContextId())) {
|
||||||
@ -419,10 +401,8 @@ public class JukeboxManager implements SmartInitializingSingleton {
|
|||||||
queryParameters.add("uri", uri);
|
queryParameters.add("uri", uri);
|
||||||
queryParameters.add("device_id", config.getDeviceId());
|
queryParameters.add("device_id", config.getDeviceId());
|
||||||
WebClient.RequestBodySpec request = webClient.method(HttpMethod.POST)
|
WebClient.RequestBodySpec request = webClient.method(HttpMethod.POST)
|
||||||
.uri(uriBuilder -> uriBuilder.path("/v1/me/player/queue")
|
.uri(uriBuilder -> uriBuilder.path("/v1/me/player/queue").queryParams(queryParameters).build())
|
||||||
.queryParams(queryParameters).build())
|
.header(HttpHeaders.AUTHORIZATION, "Bearer " + config.getAccessToken());
|
||||||
.header(HttpHeaders.AUTHORIZATION, "Bearer "
|
|
||||||
+ config.getAccessToken());
|
|
||||||
|
|
||||||
request.retrieve().bodyToMono(String.class).block();
|
request.retrieve().bodyToMono(String.class).block();
|
||||||
}
|
}
|
||||||
@ -468,8 +448,7 @@ public class JukeboxManager implements SmartInitializingSingleton {
|
|||||||
|
|
||||||
WebClient.RequestBodySpec request = webClient.method(HttpMethod.GET)
|
WebClient.RequestBodySpec request = webClient.method(HttpMethod.GET)
|
||||||
.uri(uriBuilder -> uriBuilder.path("/v1/me/player/devices").build())
|
.uri(uriBuilder -> uriBuilder.path("/v1/me/player/devices").build())
|
||||||
.header(HttpHeaders.AUTHORIZATION, "Bearer "
|
.header(HttpHeaders.AUTHORIZATION, "Bearer " + config.getAccessToken());
|
||||||
+ config.getAccessToken());
|
|
||||||
|
|
||||||
boolean deviceFound = false;
|
boolean deviceFound = false;
|
||||||
|
|
||||||
@ -482,8 +461,7 @@ public class JukeboxManager implements SmartInitializingSingleton {
|
|||||||
JsonArray devices = response.getAsJsonObject().getAsJsonArray("devices");
|
JsonArray devices = response.getAsJsonObject().getAsJsonArray("devices");
|
||||||
for (JsonElement deviceElement : devices) {
|
for (JsonElement deviceElement : devices) {
|
||||||
JsonObject device = deviceElement.getAsJsonObject();
|
JsonObject device = deviceElement.getAsJsonObject();
|
||||||
if (device.has("id")
|
if (device.has("id") && device.get("id").getAsString().equals(config.getDeviceId())) {
|
||||||
&& device.get("id").getAsString().equals(config.getDeviceId())) {
|
|
||||||
deviceFound = true;
|
deviceFound = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -496,8 +474,7 @@ public class JukeboxManager implements SmartInitializingSingleton {
|
|||||||
try {
|
try {
|
||||||
Runtime.getRuntime().exec(command);
|
Runtime.getRuntime().exec(command);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.warn("Could execute command: "
|
logger.warn("Could execute command: " + command, e);
|
||||||
+ command, e);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,8 +70,8 @@ public class JukeboxController extends BaseController {
|
|||||||
throw new EntityResponseStatusException(HttpStatus.GONE);
|
throw new EntityResponseStatusException(HttpStatus.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (queueList.containsKey(getCurrentUserId()) && queueList.get(getCurrentUserId())
|
if (queueList.containsKey(getCurrentUserId())
|
||||||
.isAfter(Instant.now().minus(1, ChronoUnit.MINUTES))) {
|
&& queueList.get(getCurrentUserId()).isAfter(Instant.now().minus(1, ChronoUnit.MINUTES))) {
|
||||||
throw new EntityResponseStatusException(
|
throw new EntityResponseStatusException(
|
||||||
Duration.between(queueList.get(getCurrentUserId()), Instant.now()).getSeconds(),
|
Duration.between(queueList.get(getCurrentUserId()), Instant.now()).getSeconds(),
|
||||||
HttpStatus.PAYMENT_REQUIRED);
|
HttpStatus.PAYMENT_REQUIRED);
|
||||||
@ -92,10 +92,10 @@ public class JukeboxController extends BaseController {
|
|||||||
throw new EntityResponseStatusException(HttpStatus.GONE);
|
throw new EntityResponseStatusException(HttpStatus.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (searchList.containsKey(getCurrentUserId()) && searchList.get(getCurrentUserId())
|
if (searchList.containsKey(getCurrentUserId())
|
||||||
.isAfter(Instant.now().minus(3, ChronoUnit.SECONDS))) {
|
&& searchList.get(getCurrentUserId()).isAfter(Instant.now().minus(3, ChronoUnit.SECONDS))) {
|
||||||
throw new EntityResponseStatusException(Duration
|
throw new EntityResponseStatusException(
|
||||||
.between(searchList.get(getCurrentUserId()), Instant.now()).getSeconds(),
|
Duration.between(searchList.get(getCurrentUserId()), Instant.now()).getSeconds(),
|
||||||
HttpStatus.PAYMENT_REQUIRED);
|
HttpStatus.PAYMENT_REQUIRED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -137,9 +137,8 @@ public class JukeboxController extends BaseController {
|
|||||||
*/
|
*/
|
||||||
@PreAuthorize("isAuthenticated()")
|
@PreAuthorize("isAuthenticated()")
|
||||||
@GetMapping("/search")
|
@GetMapping("/search")
|
||||||
public void search(@RequestParam("q") String query,
|
public void search(@RequestParam("q") String query, @RequestParam("offset") Optional<Long> offset,
|
||||||
@RequestParam("offset") Optional<Long> offset, HttpServletResponse response)
|
HttpServletResponse response) throws JsonIOException, IOException {
|
||||||
throws JsonIOException, IOException {
|
|
||||||
checkSearchPermission();
|
checkSearchPermission();
|
||||||
response.setContentType("application/json");
|
response.setContentType("application/json");
|
||||||
response.setCharacterEncoding("UTF-8");
|
response.setCharacterEncoding("UTF-8");
|
||||||
|
@ -110,9 +110,8 @@ public class JukeboxManagementController extends BaseController {
|
|||||||
*/
|
*/
|
||||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||||
@GetMapping("/search")
|
@GetMapping("/search")
|
||||||
public void search(@RequestParam("q") String query,
|
public void search(@RequestParam("q") String query, @RequestParam("offset") Optional<Long> offset,
|
||||||
@RequestParam("offset") Optional<Long> offset, HttpServletResponse response)
|
HttpServletResponse response) throws JsonIOException, IOException {
|
||||||
throws JsonIOException, IOException {
|
|
||||||
response.setContentType("application/json");
|
response.setContentType("application/json");
|
||||||
response.setCharacterEncoding("UTF-8");
|
response.setCharacterEncoding("UTF-8");
|
||||||
gson.toJson(jukeboxManager.searchTrack(query, offset.orElse(null)), response.getWriter());
|
gson.toJson(jukeboxManager.searchTrack(query, offset.orElse(null)), response.getWriter());
|
||||||
|
@ -11,7 +11,7 @@ import org.springframework.stereotype.Service;
|
|||||||
|
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
import de.bstly.we.oidc.model.OidcAuthorizationCode;
|
import de.bstly.we.oidc.businesslogic.model.OidcAuthorizationCode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Class OidcAuthorizationCodeManager.
|
* The Class OidcAuthorizationCodeManager.
|
||||||
|
@ -0,0 +1,141 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package de.bstly.we.oidc.businesslogic;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
|
import de.bstly.we.oidc.businesslogic.model.OidcAuthorizationCode;
|
||||||
|
import de.bstly.we.oidc.model.OidcAuthorization;
|
||||||
|
import de.bstly.we.oidc.model.QOidcAuthorization;
|
||||||
|
import de.bstly.we.oidc.repository.OidcAuthorizationRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Class OidcAuthorizationManager.
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class OidcAuthorizationManager {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OidcAuthorizationRepository oidcAuthorizationRepository;
|
||||||
|
|
||||||
|
private QOidcAuthorization qOidcAuthorization = QOidcAuthorization.oidcAuthorization;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hold codes in memory
|
||||||
|
*/
|
||||||
|
private final Map<String, OidcAuthorizationCode> authorizationCodes = Maps.newHashMap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the authorization.
|
||||||
|
*
|
||||||
|
* @param clientId the client id
|
||||||
|
* @param subject the subject
|
||||||
|
* @return the authorization
|
||||||
|
*/
|
||||||
|
public OidcAuthorization getAuthorization(Long clientId, Long subject) {
|
||||||
|
return oidcAuthorizationRepository
|
||||||
|
.findOne(qOidcAuthorization.client.eq(clientId).and(qOidcAuthorization.subject.eq(subject)))
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if is authorized.
|
||||||
|
*
|
||||||
|
* @param clientId the client id
|
||||||
|
* @param subject the subject
|
||||||
|
* @param scopes the scopes
|
||||||
|
* @return true, if is authorized
|
||||||
|
*/
|
||||||
|
public boolean isAuthorized(Long clientId, Long subject, Set<String> scopes) {
|
||||||
|
OidcAuthorization oidcAuthorization = getAuthorization(clientId, subject);
|
||||||
|
if (oidcAuthorization == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return scopes == null
|
||||||
|
|| oidcAuthorization.getScopes() != null && oidcAuthorization.getScopes().containsAll(scopes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authorize.
|
||||||
|
*
|
||||||
|
* @param clientId the client id
|
||||||
|
* @param subject the subject
|
||||||
|
* @param scopes the scopes
|
||||||
|
* @return the oidc authorization
|
||||||
|
*/
|
||||||
|
public OidcAuthorization authorize(Long clientId, Long subject, Set<String> scopes) {
|
||||||
|
OidcAuthorization oidcAuthorization = getAuthorization(clientId, subject);
|
||||||
|
if (oidcAuthorization == null) {
|
||||||
|
oidcAuthorization = new OidcAuthorization();
|
||||||
|
oidcAuthorization.setClient(clientId);
|
||||||
|
oidcAuthorization.setSubject(subject);
|
||||||
|
oidcAuthorization.setScopes(Sets.newHashSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
oidcAuthorization.getScopes().addAll(scopes);
|
||||||
|
|
||||||
|
return oidcAuthorizationRepository.save(oidcAuthorization);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the authorization.
|
||||||
|
*
|
||||||
|
* @param clientId the client id
|
||||||
|
* @param subject the subject
|
||||||
|
*/
|
||||||
|
public void removeAuthorization(Long clientId, Long subject) {
|
||||||
|
OidcAuthorization oidcAuthorization = getAuthorization(clientId, subject);
|
||||||
|
if (oidcAuthorization != null) {
|
||||||
|
oidcAuthorizationRepository.delete(oidcAuthorization);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the code.
|
||||||
|
*
|
||||||
|
* @param clientId the client id
|
||||||
|
* @param redirectUri the redirect uri
|
||||||
|
* @param scopes the scopes
|
||||||
|
* @param subject the subject
|
||||||
|
* @param nonce the nonce
|
||||||
|
* @param springSession the spring session
|
||||||
|
* @return the oidc authorization code
|
||||||
|
*/
|
||||||
|
public OidcAuthorizationCode createCode(String clientId, URI redirectUri, Set<String> scopes, Long subject,
|
||||||
|
String nonce, String springSession) {
|
||||||
|
OidcAuthorizationCode authorizationCode = new OidcAuthorizationCode(clientId, redirectUri, scopes, subject,
|
||||||
|
nonce);
|
||||||
|
authorizationCode.setSpringSession(springSession);
|
||||||
|
authorizationCodes.put(authorizationCode.getCode(), authorizationCode);
|
||||||
|
return authorizationCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the code.
|
||||||
|
*
|
||||||
|
* @param code the code
|
||||||
|
* @return the code
|
||||||
|
*/
|
||||||
|
public OidcAuthorizationCode getCode(String code) {
|
||||||
|
return authorizationCodes.get(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the code.
|
||||||
|
*
|
||||||
|
* @param code the code
|
||||||
|
*/
|
||||||
|
public void removeCode(String code) {
|
||||||
|
authorizationCodes.remove(code);
|
||||||
|
}
|
||||||
|
}
|
@ -5,19 +5,26 @@ package de.bstly.we.oidc.businesslogic;
|
|||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.apache.commons.lang3.RandomStringUtils;
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.PageRequest;
|
import org.springframework.data.domain.PageRequest;
|
||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
import de.bstly.we.oidc.model.OidcAuthorizationGrantType;
|
import de.bstly.we.oidc.businesslogic.model.OidcAuthorizationGrantType;
|
||||||
|
import de.bstly.we.oidc.businesslogic.model.OidcClientAuthenticationMethod;
|
||||||
|
import de.bstly.we.oidc.controller.model.OidcClientInfo;
|
||||||
|
import de.bstly.we.oidc.model.OidcAuthorization;
|
||||||
import de.bstly.we.oidc.model.OidcClient;
|
import de.bstly.we.oidc.model.OidcClient;
|
||||||
import de.bstly.we.oidc.model.OidcClientAuthenticationMethod;
|
|
||||||
import de.bstly.we.oidc.model.QOidcClient;
|
import de.bstly.we.oidc.model.QOidcClient;
|
||||||
import de.bstly.we.oidc.repository.OidcClientRepository;
|
import de.bstly.we.oidc.repository.OidcClientRepository;
|
||||||
|
|
||||||
@ -33,8 +40,32 @@ public class OidcClientManager {
|
|||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private OidcClientRepository oidcClientRepository;
|
private OidcClientRepository oidcClientRepository;
|
||||||
|
@Autowired
|
||||||
|
private OidcAuthorizationManager oidcAuthorizationManager;
|
||||||
private QOidcClient qOidcClient = QOidcClient.oidcClient;
|
private QOidcClient qOidcClient = QOidcClient.oidcClient;
|
||||||
|
|
||||||
|
@Value("${oidc.provider.issuer:}")
|
||||||
|
private String oidcIssuer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the issuer.
|
||||||
|
*
|
||||||
|
* @param request the request
|
||||||
|
* @return the issuer
|
||||||
|
*/
|
||||||
|
public String getIssuer(HttpServletRequest request) {
|
||||||
|
String issuer = oidcIssuer;
|
||||||
|
|
||||||
|
if (!StringUtils.hasText(issuer)) {
|
||||||
|
issuer = request.getScheme() + "://" + request.getServerName();
|
||||||
|
if (request.getServerPort() != 443 && request.getServerPort() != 80) {
|
||||||
|
issuer += ":" + request.getServerPort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return issuer;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the client.
|
* Creates the client.
|
||||||
*
|
*
|
||||||
@ -112,6 +143,16 @@ public class OidcClientManager {
|
|||||||
return oidcClientRepository.save(oidcClient);
|
return oidcClientRepository.save(oidcClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the.
|
||||||
|
*
|
||||||
|
* @param id the id
|
||||||
|
* @return the oidc client
|
||||||
|
*/
|
||||||
|
public OidcClient get(Long id) {
|
||||||
|
return oidcClientRepository.findById(id).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the by client id.
|
* Gets the by client id.
|
||||||
*
|
*
|
||||||
@ -180,4 +221,38 @@ public class OidcClientManager {
|
|||||||
Sort sort = descending ? Sort.by(sortBy).descending() : Sort.by(sortBy).ascending();
|
Sort sort = descending ? Sort.by(sortBy).descending() : Sort.by(sortBy).ascending();
|
||||||
return oidcClientRepository.findAll(PageRequest.of(page, size, sort));
|
return oidcClientRepository.findAll(PageRequest.of(page, size, sort));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the client info.
|
||||||
|
*
|
||||||
|
* @param client the client
|
||||||
|
* @param subject the subject
|
||||||
|
* @return the client info
|
||||||
|
*/
|
||||||
|
public OidcClientInfo getClientInfo(OidcClient client, Long subject) {
|
||||||
|
|
||||||
|
OidcClientInfo info = new OidcClientInfo();
|
||||||
|
info.setClientId(client.getClientId());
|
||||||
|
info.setName(client.getClientName());
|
||||||
|
info.setLoginUrl(client.getLoginUrl());
|
||||||
|
info.setFrontchannelLogoutUri(client.getFrontchannelLogoutUri());
|
||||||
|
info.setFrontchannelLogoutSessionRequired(client.isFrontchannelLogoutSessionRequired());
|
||||||
|
info.setBackchannelLogout(StringUtils.hasText(client.getBackchannelLogoutUri()));
|
||||||
|
|
||||||
|
info.setAuthorize(client.isAuthorize());
|
||||||
|
info.setScopes(client.getScopes());
|
||||||
|
info.setSessions(Lists.newArrayList());
|
||||||
|
|
||||||
|
if (info.getScopes() == null) {
|
||||||
|
info.setScopes(Sets.newHashSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
OidcAuthorization authorization = oidcAuthorizationManager.getAuthorization(client.getId(), subject);
|
||||||
|
|
||||||
|
if (authorization != null) {
|
||||||
|
info.setAuthorizedScopes(authorization.getScopes());
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,442 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package de.bstly.we.oidc.businesslogic;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
|
import org.springframework.util.MultiValueMap;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.reactive.function.BodyInserters;
|
||||||
|
import org.springframework.web.reactive.function.client.WebClient;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import com.nimbusds.jose.JOSEException;
|
||||||
|
import com.nimbusds.jose.JWSAlgorithm;
|
||||||
|
import com.nimbusds.jose.JWSHeader;
|
||||||
|
import com.nimbusds.jose.JWSSigner;
|
||||||
|
import com.nimbusds.jose.jwk.JWK;
|
||||||
|
import com.nimbusds.jwt.JWTClaimsSet;
|
||||||
|
import com.nimbusds.jwt.SignedJWT;
|
||||||
|
|
||||||
|
import de.bstly.we.jwt.businesslogic.JwtKeyManager;
|
||||||
|
import de.bstly.we.jwt.model.JwtKey;
|
||||||
|
import de.bstly.we.oidc.model.OidcClient;
|
||||||
|
import de.bstly.we.oidc.model.OidcSession;
|
||||||
|
import de.bstly.we.oidc.model.QOidcSession;
|
||||||
|
import de.bstly.we.oidc.repository.OidcSessionRepository;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Class OidcSessionManager.
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class OidcSessionManager {
|
||||||
|
|
||||||
|
private Logger logger = LoggerFactory.getLogger(OidcSessionManager.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OidcSessionRepository oidcSessionRepository;
|
||||||
|
@Autowired
|
||||||
|
private OidcClientManager oidcRegisteredClientManager;
|
||||||
|
@Autowired
|
||||||
|
private JwtKeyManager jwtKeyManager;
|
||||||
|
|
||||||
|
protected WebClient webClient = WebClient.builder().build();
|
||||||
|
|
||||||
|
private QOidcSession qOidcSession = QOidcSession.oidcSession;
|
||||||
|
|
||||||
|
public static final int SID_LENGTH = 32;
|
||||||
|
public static final int JWT_ID_LENGTH = 32;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the sid.
|
||||||
|
*
|
||||||
|
* @return the string
|
||||||
|
*/
|
||||||
|
public String createSid() {
|
||||||
|
String sid = new StringBuilder(RandomStringUtils.random(SID_LENGTH, true, true)).insert(8, "-").insert(13, "-")
|
||||||
|
.insert(18, "-").insert(23, "-").toString();
|
||||||
|
|
||||||
|
while (oidcSessionRepository.exists(qOidcSession.sid.eq(sid))) {
|
||||||
|
sid = new StringBuilder(RandomStringUtils.random(SID_LENGTH, true, true)).insert(8, "-").insert(13, "-")
|
||||||
|
.insert(18, "-").insert(23, "-").toString();
|
||||||
|
}
|
||||||
|
return sid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the session.
|
||||||
|
*
|
||||||
|
* @param clientId the client id
|
||||||
|
* @param subject the subject
|
||||||
|
* @param idToken the id token
|
||||||
|
* @param sid the sid
|
||||||
|
* @param springSession the spring session
|
||||||
|
* @return the oidc session
|
||||||
|
* @throws JOSEException the JOSE exception
|
||||||
|
*/
|
||||||
|
public OidcSession createSession(Long clientId, Long subject, String idToken, String sid, String springSession)
|
||||||
|
throws JOSEException {
|
||||||
|
|
||||||
|
OidcSession session = new OidcSession();
|
||||||
|
session.setClientId(clientId);
|
||||||
|
session.setSubject(subject);
|
||||||
|
session.setIdToken(idToken);
|
||||||
|
session.setSid(sid);
|
||||||
|
session.setSpringSession(springSession);
|
||||||
|
return oidcSessionRepository.save(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the by sid.
|
||||||
|
*
|
||||||
|
* @param sid the sid
|
||||||
|
* @return the by sid
|
||||||
|
*/
|
||||||
|
public OidcSession getBySid(String sid) {
|
||||||
|
Assert.isTrue(StringUtils.hasText(sid), "No sid defined!");
|
||||||
|
return oidcSessionRepository.findOne(qOidcSession.sid.eq(sid)).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the by id token.
|
||||||
|
*
|
||||||
|
* @param idToken the id token
|
||||||
|
* @return the by id token
|
||||||
|
*/
|
||||||
|
public OidcSession getByIdToken(String idToken) {
|
||||||
|
return oidcSessionRepository.findOne(qOidcSession.idToken.eq(idToken)).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the all by spring session.
|
||||||
|
*
|
||||||
|
* @param springSession the spring session
|
||||||
|
* @return the all by spring session
|
||||||
|
*/
|
||||||
|
public List<OidcSession> getAllBySpringSession(String springSession) {
|
||||||
|
return Lists.newArrayList(oidcSessionRepository.findAll(qOidcSession.springSession.eq(springSession)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the all by target.
|
||||||
|
*
|
||||||
|
* @param clientId the client id
|
||||||
|
* @return the all by target
|
||||||
|
*/
|
||||||
|
public List<OidcSession> getAllByTarget(Long clientId) {
|
||||||
|
return Lists.newArrayList(oidcSessionRepository.findAll(qOidcSession.client.eq(clientId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the all by subject.
|
||||||
|
*
|
||||||
|
* @param subject the subject
|
||||||
|
* @return the all by subject
|
||||||
|
*/
|
||||||
|
public List<OidcSession> getAllBySubject(Long subject) {
|
||||||
|
return Lists.newArrayList(oidcSessionRepository.findAll(qOidcSession.subject.eq(subject)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the all by target and subject.
|
||||||
|
*
|
||||||
|
* @param clientId the client id
|
||||||
|
* @param subject the subject
|
||||||
|
* @return the all by target and subject
|
||||||
|
*/
|
||||||
|
public List<OidcSession> getAllByTargetAndSubject(Long clientId, Long subject) {
|
||||||
|
return Lists.newArrayList(
|
||||||
|
oidcSessionRepository.findAll(qOidcSession.client.eq(clientId).and(qOidcSession.subject.eq(subject))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete.
|
||||||
|
*
|
||||||
|
* @param session the session
|
||||||
|
*/
|
||||||
|
public void delete(OidcSession session) {
|
||||||
|
if (oidcSessionRepository.existsById(session.getId())) {
|
||||||
|
oidcSessionRepository.delete(session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete by sid.
|
||||||
|
*
|
||||||
|
* @param sid the sid
|
||||||
|
*/
|
||||||
|
public void deleteBySid(String sid) {
|
||||||
|
OidcSession session = getBySid(sid);
|
||||||
|
if (session != null) {
|
||||||
|
delete(session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete by id token.
|
||||||
|
*
|
||||||
|
* @param idToken the id token
|
||||||
|
*/
|
||||||
|
public void deleteByIdToken(String idToken) {
|
||||||
|
OidcSession session = getByIdToken(idToken);
|
||||||
|
if (session != null) {
|
||||||
|
delete(session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all by spring session.
|
||||||
|
*
|
||||||
|
* @param springSession the spring session
|
||||||
|
*/
|
||||||
|
public void deleteAllBySpringSession(String springSession) {
|
||||||
|
for (OidcSession token : getAllBySpringSession(springSession)) {
|
||||||
|
delete(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all by target.
|
||||||
|
*
|
||||||
|
* @param target the target
|
||||||
|
*/
|
||||||
|
public void deleteAllByTarget(Long target) {
|
||||||
|
for (OidcSession token : getAllByTarget(target)) {
|
||||||
|
delete(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all by subject.
|
||||||
|
*
|
||||||
|
* @param subject the subject
|
||||||
|
*/
|
||||||
|
public void deleteAllBySubject(Long subject) {
|
||||||
|
for (OidcSession token : getAllBySubject(subject)) {
|
||||||
|
delete(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Backchannel logout.
|
||||||
|
*
|
||||||
|
* @param sid the sid
|
||||||
|
* @param issuer the issuer
|
||||||
|
*/
|
||||||
|
public void backchannelLogout(String sid, String issuer) {
|
||||||
|
OidcSession session = getBySid(sid);
|
||||||
|
Assert.notNull(session, "invalid sid!");
|
||||||
|
OidcClient client = oidcRegisteredClientManager.get(session.getClientId());
|
||||||
|
if (client == null) {
|
||||||
|
logger.error("OidcSession '" + session.getId() + "' (subject: " + session.getSubject()
|
||||||
|
+ ") with invalid OidcRegisteredClient: " + session.getClientId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
sendBackchannelLogout(session.getSubject(), sid, issuer, client);
|
||||||
|
oidcSessionRepository.delete(session);
|
||||||
|
} catch (JOSEException e) {
|
||||||
|
logger.warn("JOSEException on backchannelLogout!", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Backchannel logout.
|
||||||
|
*
|
||||||
|
* @param clientId the client id
|
||||||
|
* @param subject the subject
|
||||||
|
* @param issuer the issuer
|
||||||
|
*/
|
||||||
|
public void backchannelLogout(Long clientId, Long subject, String issuer) {
|
||||||
|
List<OidcSession> sessions = getAllByTargetAndSubject(clientId, subject);
|
||||||
|
|
||||||
|
if (!sessions.isEmpty()) {
|
||||||
|
OidcSession session = sessions.get(0);
|
||||||
|
OidcClient client = oidcRegisteredClientManager.get(session.getClientId());
|
||||||
|
if (client != null) {
|
||||||
|
try {
|
||||||
|
sendBackchannelLogout(subject, null, issuer, client);
|
||||||
|
} catch (JOSEException e) {
|
||||||
|
logger.warn("JOSEException on backchannelLogout!", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.error("OidcSession '" + session.getId() + "' (subject: " + session.getSubject()
|
||||||
|
+ ") with invalid OidcRegisteredClient: " + session.getClientId());
|
||||||
|
}
|
||||||
|
|
||||||
|
oidcSessionRepository.deleteAll(sessions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Backchannel logout.
|
||||||
|
*
|
||||||
|
* @param subject the subject
|
||||||
|
* @param issuer the issuer
|
||||||
|
*/
|
||||||
|
public void backchannelLogout(Long subject, String issuer) {
|
||||||
|
Set<Long> clients = Sets.newHashSet();
|
||||||
|
for (OidcSession session : getAllBySubject(subject)) {
|
||||||
|
OidcClient client = oidcRegisteredClientManager.get(session.getClientId());
|
||||||
|
if (client == null) {
|
||||||
|
logger.error("OidcSession '" + session.getId() + "' (subject: " + session.getSubject()
|
||||||
|
+ ") with invalid OidcRegisteredClient: " + session.getClientId());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!clients.contains(client.getId())) {
|
||||||
|
try {
|
||||||
|
sendBackchannelLogout(subject, null, issuer, client);
|
||||||
|
clients.add(client.getId());
|
||||||
|
} catch (JOSEException e) {
|
||||||
|
logger.warn("JOSEException on backchannelLogout!", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
oidcSessionRepository.delete(session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Backchannel logout spring.
|
||||||
|
*
|
||||||
|
* @param springSessionId the spring session id
|
||||||
|
* @param issuer the issuer
|
||||||
|
*/
|
||||||
|
public void backchannelLogoutSpring(String springSessionId, String issuer) {
|
||||||
|
for (OidcSession session : getAllBySpringSession(springSessionId)) {
|
||||||
|
OidcClient client = oidcRegisteredClientManager.get(session.getClientId());
|
||||||
|
if (client == null) {
|
||||||
|
logger.error("OidcSession '" + session.getId() + "' (subject: " + session.getSubject()
|
||||||
|
+ ") with invalid OidcRegisteredClient: " + session.getClientId());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
sendBackchannelLogout(session.getSubject(), session.getSid(), issuer, client);
|
||||||
|
oidcSessionRepository.delete(session);
|
||||||
|
} catch (JOSEException e) {
|
||||||
|
logger.warn("JOSEException on backchannelLogout!", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send backchannel logout.
|
||||||
|
*
|
||||||
|
* @param subject the subject
|
||||||
|
* @param sid the sid
|
||||||
|
* @param issuer the issuer
|
||||||
|
* @param client the client
|
||||||
|
* @throws JOSEException the JOSE exception
|
||||||
|
*/
|
||||||
|
// @Async("threadPoolTaskExecutor")
|
||||||
|
public void sendBackchannelLogout(Long subject, String sid, String issuer, OidcClient client) throws JOSEException {
|
||||||
|
|
||||||
|
if (!StringUtils.hasText(client.getBackchannelLogoutUri())) {
|
||||||
|
logger.warn("Could not send backchannel logout for '" + subject
|
||||||
|
+ "' without backchannel_logout_uri of OidcRegisteredClient '" + client.getId()
|
||||||
|
+ "' not possible!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SignedJWT logoutToken = createBackchannelLogoutToken(subject, sid, issuer, client);
|
||||||
|
|
||||||
|
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
|
||||||
|
formData.add("logout_token", logoutToken.serialize());
|
||||||
|
ResponseEntity<?> response = null;
|
||||||
|
try {
|
||||||
|
response = webClient.method(HttpMethod.POST).uri(client.getBackchannelLogoutUri())
|
||||||
|
.contentType(MediaType.APPLICATION_FORM_URLENCODED).body(BodyInserters.fromFormData(formData))
|
||||||
|
.retrieve().onStatus(HttpStatus::isError, error -> Mono.empty()).toBodilessEntity().block();
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.warn("backchannelLogout POST '" + client.getBackchannelLogoutUri() + "' failed", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response != null && !response.getStatusCode().equals(HttpStatus.OK)) {
|
||||||
|
logger.info("backchannelLogout for '" + subject + "' failed with status: " + response.getStatusCode()
|
||||||
|
+ " from " + client.getBackchannelLogoutUri() + "(client: " + client.getId() + ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the backchannel logout token.
|
||||||
|
*
|
||||||
|
* @param subject the subject
|
||||||
|
* @param sid the sid
|
||||||
|
* @param issuer the issuer
|
||||||
|
* @param client the client
|
||||||
|
* @return the signed JWT
|
||||||
|
* @throws JOSEException the JOSE exception
|
||||||
|
*/
|
||||||
|
protected SignedJWT createBackchannelLogoutToken(Long subject, String sid, String issuer, OidcClient client)
|
||||||
|
throws JOSEException {
|
||||||
|
if (!StringUtils.hasText(client.getBackchannelLogoutUri())) {
|
||||||
|
logger.warn("Creation of logout token '" + subject
|
||||||
|
+ "' without backchannel_logout_uri of OidcRegisteredClient '" + client.getId()
|
||||||
|
+ "' not possible!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
JWTClaimsSet.Builder claimsSetBuilder = new JWTClaimsSet.Builder();
|
||||||
|
|
||||||
|
claimsSetBuilder.subject(String.valueOf(subject));
|
||||||
|
claimsSetBuilder.issuer(issuer);
|
||||||
|
claimsSetBuilder.audience(client.getClientId());
|
||||||
|
claimsSetBuilder.issueTime(new Date());
|
||||||
|
claimsSetBuilder.jwtID(RandomStringUtils.random(JWT_ID_LENGTH, true, true));
|
||||||
|
if (StringUtils.hasText(sid)) {
|
||||||
|
claimsSetBuilder.claim("sid", sid);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> events = Maps.newHashMap();
|
||||||
|
events.put("http://schemas.openid.net/event/backchannel-logout", Maps.newHashMap());
|
||||||
|
claimsSetBuilder.claim("events", events);
|
||||||
|
|
||||||
|
JwtKey jwtKey = jwtKeyManager.getLatest(OidcTokenManager.OIDC_JWT_KEY_NAME, false);
|
||||||
|
|
||||||
|
if (jwtKey == null) {
|
||||||
|
throw new JOSEException("No jwtKey found!");
|
||||||
|
}
|
||||||
|
|
||||||
|
JWK jwk = jwtKeyManager.parseKey(jwtKey);
|
||||||
|
|
||||||
|
JWSSigner jwsSigner = jwtKeyManager.createSigner(jwtKey);
|
||||||
|
|
||||||
|
if (jwsSigner == null) {
|
||||||
|
throw new JOSEException("Signer could not be created!");
|
||||||
|
}
|
||||||
|
|
||||||
|
JWSAlgorithm algorithm = jwtKeyManager.getJwsAlgorithm(jwtKey);
|
||||||
|
|
||||||
|
if (algorithm == null) {
|
||||||
|
algorithm = JWSAlgorithm.HS512;
|
||||||
|
}
|
||||||
|
|
||||||
|
JWSHeader.Builder headerBuilder = new JWSHeader.Builder(algorithm);
|
||||||
|
|
||||||
|
headerBuilder.keyID(jwk.getKeyID());
|
||||||
|
|
||||||
|
SignedJWT jwt = new SignedJWT(headerBuilder.build(), claimsSetBuilder.build());
|
||||||
|
jwt.sign(jwsSigner);
|
||||||
|
|
||||||
|
return jwt;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -7,26 +7,35 @@ import java.time.Instant;
|
|||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.commons.lang3.RandomStringUtils;
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.SmartInitializingSingleton;
|
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import com.beust.jcommander.internal.Maps;
|
import com.beust.jcommander.internal.Maps;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import com.nimbusds.jose.JOSEException;
|
import com.nimbusds.jose.JOSEException;
|
||||||
import com.nimbusds.jose.JOSEObjectType;
|
import com.nimbusds.jose.JOSEObjectType;
|
||||||
import com.nimbusds.jose.JWSAlgorithm;
|
import com.nimbusds.jose.JWSAlgorithm;
|
||||||
import com.nimbusds.jose.JWSHeader;
|
import com.nimbusds.jose.JWSHeader;
|
||||||
import com.nimbusds.jose.jwk.JWKSet;
|
|
||||||
import com.nimbusds.jose.jwk.KeyType;
|
import com.nimbusds.jose.jwk.KeyType;
|
||||||
import com.nimbusds.jose.jwk.KeyUse;
|
import com.nimbusds.jose.jwk.KeyUse;
|
||||||
import com.nimbusds.jwt.JWTClaimsSet.Builder;
|
import com.nimbusds.jwt.JWTClaimsSet.Builder;
|
||||||
import com.nimbusds.jwt.SignedJWT;
|
import com.nimbusds.jwt.SignedJWT;
|
||||||
|
import com.querydsl.core.BooleanBuilder;
|
||||||
|
|
||||||
import de.bstly.we.businesslogic.PermissionManager;
|
import de.bstly.we.businesslogic.PermissionManager;
|
||||||
import de.bstly.we.businesslogic.QuotaManager;
|
import de.bstly.we.businesslogic.QuotaManager;
|
||||||
@ -39,6 +48,7 @@ import de.bstly.we.model.Permission;
|
|||||||
import de.bstly.we.model.Quota;
|
import de.bstly.we.model.Quota;
|
||||||
import de.bstly.we.model.User;
|
import de.bstly.we.model.User;
|
||||||
import de.bstly.we.model.UserProfileField;
|
import de.bstly.we.model.UserProfileField;
|
||||||
|
import de.bstly.we.oidc.businesslogic.model.OidcAuthorizationGrantType;
|
||||||
import de.bstly.we.oidc.model.OidcClient;
|
import de.bstly.we.oidc.model.OidcClient;
|
||||||
import de.bstly.we.oidc.model.OidcToken;
|
import de.bstly.we.oidc.model.OidcToken;
|
||||||
import de.bstly.we.oidc.model.QOidcToken;
|
import de.bstly.we.oidc.model.QOidcToken;
|
||||||
@ -50,12 +60,16 @@ import de.bstly.we.oidc.repository.OidcTokenRepository;
|
|||||||
@Service
|
@Service
|
||||||
public class OidcTokenManager implements SmartInitializingSingleton {
|
public class OidcTokenManager implements SmartInitializingSingleton {
|
||||||
|
|
||||||
|
private Logger logger = LoggerFactory.getLogger(OidcTokenManager.class);
|
||||||
|
|
||||||
public static final int ACCESS_TOKEN_LENGTH = 64;
|
public static final int ACCESS_TOKEN_LENGTH = 64;
|
||||||
|
public static final int REFRESH_TOKEN_LENGTH = 64;
|
||||||
|
|
||||||
public static final String OIDC_JWT_KEY_NAME = "oidc";
|
public static final String OIDC_JWT_KEY_NAME = "oidc";
|
||||||
public static final String BEARER_TOKEN_TYPE = "Bearer";
|
public static final String BEARER_TOKEN_TYPE = "Bearer";
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private OidcTokenRepository tokenRepository;
|
private OidcTokenRepository oidcTokenRepository;
|
||||||
@Autowired
|
@Autowired
|
||||||
private UserManager userManager;
|
private UserManager userManager;
|
||||||
@Autowired
|
@Autowired
|
||||||
@ -104,11 +118,40 @@ public class OidcTokenManager implements SmartInitializingSingleton {
|
|||||||
* @return the oidc token
|
* @return the oidc token
|
||||||
*/
|
*/
|
||||||
public OidcToken createToken(OidcClient client, Long userId) {
|
public OidcToken createToken(OidcClient client, Long userId) {
|
||||||
|
return createToken(client, userId, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the token.
|
||||||
|
*
|
||||||
|
* @param client the client
|
||||||
|
* @param userId the user id
|
||||||
|
* @param refreshToken the refresh token
|
||||||
|
* @return the oidc token
|
||||||
|
*/
|
||||||
|
public OidcToken createToken(OidcClient client, Long userId, boolean refreshToken) {
|
||||||
|
return createToken(client, userId,
|
||||||
|
refreshToken ? RandomStringUtils.random(REFRESH_TOKEN_LENGTH, true, true) : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the token.
|
||||||
|
*
|
||||||
|
* @param client the client
|
||||||
|
* @param userId the user id
|
||||||
|
* @param refreshToken the refresh token
|
||||||
|
* @return the oidc token
|
||||||
|
*/
|
||||||
|
public OidcToken createToken(OidcClient client, Long userId, String refreshToken) {
|
||||||
OidcToken token = new OidcToken();
|
OidcToken token = new OidcToken();
|
||||||
token.setUserId(userId);
|
token.setUserId(userId);
|
||||||
token.setAccessToken(RandomStringUtils.random(ACCESS_TOKEN_LENGTH, true, true));
|
token.setAccessToken(RandomStringUtils.random(ACCESS_TOKEN_LENGTH, true, true));
|
||||||
|
if (StringUtils.hasText(refreshToken)) {
|
||||||
|
token.setRefreshToken(refreshToken);
|
||||||
|
}
|
||||||
token.setExpiresIn(client.getTokenLifetime());
|
token.setExpiresIn(client.getTokenLifetime());
|
||||||
return tokenRepository.save(token);
|
token.setCreated(Instant.now());
|
||||||
|
return oidcTokenRepository.save(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -119,18 +162,18 @@ public class OidcTokenManager implements SmartInitializingSingleton {
|
|||||||
* @param nonce the nonce
|
* @param nonce the nonce
|
||||||
* @param scopes the scopes
|
* @param scopes the scopes
|
||||||
* @param issuer the issuer
|
* @param issuer the issuer
|
||||||
|
* @param sid the sid
|
||||||
* @return the oidc token
|
* @return the oidc token
|
||||||
* @throws JOSEException the JOSE exception
|
* @throws JOSEException the JOSE exception
|
||||||
*/
|
*/
|
||||||
public OidcToken createTokenWithIdToken(OidcClient client, Long userId, String nonce, Set<String> scopes,
|
public OidcToken createTokenWithIdToken(OidcClient client, Long userId, String nonce, Set<String> scopes,
|
||||||
String issuer) throws JOSEException {
|
String issuer, String sid) throws JOSEException {
|
||||||
OidcToken token = new OidcToken();
|
|
||||||
token.setClient(client.getId());
|
|
||||||
|
|
||||||
User user = userManager.get(userId);
|
User user = userManager.get(userId);
|
||||||
|
|
||||||
Assert.notNull(user, "User does not exist!");
|
Assert.notNull(user, "User does not exist!");
|
||||||
|
|
||||||
|
OidcToken token = createToken(client, client.getId(),
|
||||||
|
client.getAuthorizationGrantTypes().contains(OidcAuthorizationGrantType.refresh_token));
|
||||||
|
|
||||||
token.setUserId(user.getId());
|
token.setUserId(user.getId());
|
||||||
token.setAccessToken(RandomStringUtils.random(ACCESS_TOKEN_LENGTH, true, true));
|
token.setAccessToken(RandomStringUtils.random(ACCESS_TOKEN_LENGTH, true, true));
|
||||||
token.setExpiresIn(client.getTokenLifetime());
|
token.setExpiresIn(client.getTokenLifetime());
|
||||||
@ -147,6 +190,10 @@ public class OidcTokenManager implements SmartInitializingSingleton {
|
|||||||
claimsSetBuilder.claim("nonce", nonce);
|
claimsSetBuilder.claim("nonce", nonce);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (StringUtils.hasText(sid)) {
|
||||||
|
claimsSetBuilder.claim("sid", sid);
|
||||||
|
}
|
||||||
|
|
||||||
JwtKey jwtKey = jwtKeyManager.getLatest(OIDC_JWT_KEY_NAME, false);
|
JwtKey jwtKey = jwtKeyManager.getLatest(OIDC_JWT_KEY_NAME, false);
|
||||||
|
|
||||||
if (jwtKey == null) {
|
if (jwtKey == null) {
|
||||||
@ -162,7 +209,7 @@ public class OidcTokenManager implements SmartInitializingSingleton {
|
|||||||
|
|
||||||
token.setIdToken(jwt.serialize());
|
token.setIdToken(jwt.serialize());
|
||||||
|
|
||||||
return tokenRepository.save(token);
|
return oidcTokenRepository.save(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -234,15 +281,112 @@ public class OidcTokenManager implements SmartInitializingSingleton {
|
|||||||
* @return the by access token
|
* @return the by access token
|
||||||
*/
|
*/
|
||||||
public OidcToken getByAccessToken(String accessToken) {
|
public OidcToken getByAccessToken(String accessToken) {
|
||||||
return tokenRepository.findOne(qOidcToken.accessToken.eq(accessToken)).orElse(null);
|
return oidcTokenRepository.findOne(qOidcToken.accessToken.eq(accessToken)).orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the jwk set.
|
* Gets the last by refresh token.
|
||||||
*
|
*
|
||||||
* @return the jwk set
|
* @param refreshToken the refresh token
|
||||||
|
* @return the last by refresh token
|
||||||
*/
|
*/
|
||||||
public JWKSet getJwkSet() {
|
public OidcToken getLastByRefreshToken(String refreshToken) {
|
||||||
return jwtKeyManager.getJwkSet(OIDC_JWT_KEY_NAME, false);
|
List<OidcToken> token = getAllByRefreshToken(refreshToken);
|
||||||
|
if (token.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return token.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the all by refresh token.
|
||||||
|
*
|
||||||
|
* @param refreshToken the refresh token
|
||||||
|
* @return the all by refresh token
|
||||||
|
*/
|
||||||
|
public List<OidcToken> getAllByRefreshToken(String refreshToken) {
|
||||||
|
return Lists.newArrayList(oidcTokenRepository.findAll(qOidcToken.refreshToken.eq(refreshToken),
|
||||||
|
Sort.by(Sort.Direction.DESC, "created")));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete.
|
||||||
|
*
|
||||||
|
* @param token the token
|
||||||
|
*/
|
||||||
|
public void delete(OidcToken token) {
|
||||||
|
if (oidcTokenRepository.existsById(token.getId())) {
|
||||||
|
oidcTokenRepository.delete(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete by access token.
|
||||||
|
*
|
||||||
|
* @param accessToken the access token
|
||||||
|
*/
|
||||||
|
public void deleteByAccessToken(String accessToken) {
|
||||||
|
OidcToken token = getByAccessToken(accessToken);
|
||||||
|
if (token != null) {
|
||||||
|
delete(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all by refresh token.
|
||||||
|
*
|
||||||
|
* @param refreshToken the refresh token
|
||||||
|
*/
|
||||||
|
public void deleteAllByRefreshToken(String refreshToken) {
|
||||||
|
for (OidcToken token : oidcTokenRepository.findAll(qOidcToken.refreshToken.eq(refreshToken))) {
|
||||||
|
delete(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all by target.
|
||||||
|
*
|
||||||
|
* @param userId the user id
|
||||||
|
*/
|
||||||
|
public void deleteAllByTarget(Long userId) {
|
||||||
|
for (OidcToken token : oidcTokenRepository.findAll(qOidcToken.userId.eq(userId))) {
|
||||||
|
delete(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cron.
|
||||||
|
*/
|
||||||
|
@Scheduled(cron = "${openid-provider.token.cleanup.cron:0 0 0,12 * * * }")
|
||||||
|
public void cron() {
|
||||||
|
deleteExpired(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete expired.
|
||||||
|
*
|
||||||
|
* @param includeRefreshToken the include refresh token
|
||||||
|
*/
|
||||||
|
public void deleteExpired(boolean includeRefreshToken) {
|
||||||
|
BooleanBuilder predicate = new BooleanBuilder();
|
||||||
|
predicate.and(qOidcToken.expiresIn.gt(0L));
|
||||||
|
if (!includeRefreshToken) {
|
||||||
|
predicate.and(qOidcToken.refreshToken.isEmpty().or(qOidcToken.refreshToken.isNull()));
|
||||||
|
}
|
||||||
|
Pageable pageable = PageRequest.of(0, 500, Sort.by("id"));
|
||||||
|
Page<OidcToken> page;
|
||||||
|
do {
|
||||||
|
page = oidcTokenRepository.findAll(predicate.getValue(), pageable);
|
||||||
|
for (OidcToken oidcToken : page.getContent()) {
|
||||||
|
if (oidcToken.getCreated()
|
||||||
|
.isBefore(Instant.now().minus(oidcToken.getExpiresIn(), ChronoUnit.SECONDS))) {
|
||||||
|
logger.debug(
|
||||||
|
"delete expired OidcToken: " + oidcToken.getId() + " [" + oidcToken.getAccessToken() + "]");
|
||||||
|
delete(oidcToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pageable = page.nextPageable();
|
||||||
|
} while (page.hasNext());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,113 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package de.bstly.we.oidc.businesslogic.exception;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
import de.bstly.we.oidc.businesslogic.model.OidcAuthorizationErrorCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Class InvalidAuthorizationRequestException.
|
||||||
|
*/
|
||||||
|
public class InvalidAuthorizationRequestException extends RuntimeException {
|
||||||
|
/**
|
||||||
|
* default serialVersionUID
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private URI redirectUri;
|
||||||
|
private OidcAuthorizationErrorCode errorCode;
|
||||||
|
private String errorDescription;
|
||||||
|
private String state;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new invalid authorization request exception.
|
||||||
|
*
|
||||||
|
* @param redirectUri the redirect uri
|
||||||
|
* @param errorCode the error code
|
||||||
|
* @param errorDescription the error description
|
||||||
|
* @param state the state
|
||||||
|
*/
|
||||||
|
public InvalidAuthorizationRequestException(URI redirectUri, OidcAuthorizationErrorCode errorCode,
|
||||||
|
String errorDescription, String state) {
|
||||||
|
super(errorDescription);
|
||||||
|
this.redirectUri = redirectUri;
|
||||||
|
this.errorCode = errorCode;
|
||||||
|
this.errorDescription = errorDescription;
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the redirect uri.
|
||||||
|
*
|
||||||
|
* @return the redirect uri
|
||||||
|
*/
|
||||||
|
public URI getRedirectUri() {
|
||||||
|
return redirectUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the redirect uri.
|
||||||
|
*
|
||||||
|
* @param redirectUri the new redirect uri
|
||||||
|
*/
|
||||||
|
public void setRedirectUri(URI redirectUri) {
|
||||||
|
this.redirectUri = redirectUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the error code.
|
||||||
|
*
|
||||||
|
* @return the error code
|
||||||
|
*/
|
||||||
|
public OidcAuthorizationErrorCode getErrorCode() {
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the error code.
|
||||||
|
*
|
||||||
|
* @param errorCode the new error code
|
||||||
|
*/
|
||||||
|
public void setErrorCode(OidcAuthorizationErrorCode errorCode) {
|
||||||
|
this.errorCode = errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the error description.
|
||||||
|
*
|
||||||
|
* @return the error description
|
||||||
|
*/
|
||||||
|
public String getErrorDescription() {
|
||||||
|
return errorDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the error description.
|
||||||
|
*
|
||||||
|
* @param errorDescription the new error description
|
||||||
|
*/
|
||||||
|
public void setErrorDescription(String errorDescription) {
|
||||||
|
this.errorDescription = errorDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the state.
|
||||||
|
*
|
||||||
|
* @return the state
|
||||||
|
*/
|
||||||
|
public String getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the state.
|
||||||
|
*
|
||||||
|
* @param state the new state
|
||||||
|
*/
|
||||||
|
public void setState(String state) {
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package de.bstly.we.oidc.businesslogic.exception;
|
||||||
|
|
||||||
|
import de.bstly.we.oidc.businesslogic.model.OidcTokenErrorCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Class InvalidTokenRequestException.
|
||||||
|
*/
|
||||||
|
public class InvalidTokenRequestException extends RuntimeException {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* default serialVersionUID
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private OidcTokenErrorCode errorCode;
|
||||||
|
private String errorDescription;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new invalid token request exception.
|
||||||
|
*
|
||||||
|
* @param errorCode the error code
|
||||||
|
* @param errorDescription the error description
|
||||||
|
*/
|
||||||
|
public InvalidTokenRequestException(OidcTokenErrorCode errorCode, String errorDescription) {
|
||||||
|
super(errorDescription);
|
||||||
|
this.errorCode = errorCode;
|
||||||
|
this.errorDescription = errorDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the error code.
|
||||||
|
*
|
||||||
|
* @return the error code
|
||||||
|
*/
|
||||||
|
public OidcTokenErrorCode getErrorCode() {
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the error code.
|
||||||
|
*
|
||||||
|
* @param errorCode the new error code
|
||||||
|
*/
|
||||||
|
public void setErrorCode(OidcTokenErrorCode errorCode) {
|
||||||
|
this.errorCode = errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the error description.
|
||||||
|
*
|
||||||
|
* @return the error description
|
||||||
|
*/
|
||||||
|
public String getErrorDescription() {
|
||||||
|
return errorDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the error description.
|
||||||
|
*
|
||||||
|
* @param errorDescription the new error description
|
||||||
|
*/
|
||||||
|
public void setErrorDescription(String errorDescription) {
|
||||||
|
this.errorDescription = errorDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package de.bstly.we.oidc.model;
|
package de.bstly.we.oidc.businesslogic.model;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
@ -25,6 +25,7 @@ public class OidcAuthorizationCode {
|
|||||||
private final Instant expiry;
|
private final Instant expiry;
|
||||||
private final Long userId;
|
private final Long userId;
|
||||||
private final String nonce;
|
private final String nonce;
|
||||||
|
private String springSession;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiates a new oidc authorization code.
|
* Instantiates a new oidc authorization code.
|
||||||
@ -108,4 +109,22 @@ public class OidcAuthorizationCode {
|
|||||||
return nonce;
|
return nonce;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the spring session.
|
||||||
|
*
|
||||||
|
* @return the spring session
|
||||||
|
*/
|
||||||
|
public String getSpringSession() {
|
||||||
|
return springSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the spring session.
|
||||||
|
*
|
||||||
|
* @param springSession the new spring session
|
||||||
|
*/
|
||||||
|
public void setSpringSession(String springSession) {
|
||||||
|
this.springSession = springSession;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package de.bstly.we.oidc.model;
|
package de.bstly.we.oidc.businesslogic.model;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Enum OidcAuthorizationErrorCode.
|
* The Enum OidcAuthorizationErrorCode.
|
@ -1,11 +1,11 @@
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package de.bstly.we.oidc.model;
|
package de.bstly.we.oidc.businesslogic.model;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Enum OidcAuthorizationGrantType.
|
* The Enum OidcAuthorizationGrantType.
|
||||||
*/
|
*/
|
||||||
public enum OidcAuthorizationGrantType {
|
public enum OidcAuthorizationGrantType {
|
||||||
authorization_code, client_credentials
|
authorization_code, client_credentials, refresh_token
|
||||||
}
|
}
|
@ -0,0 +1,282 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package de.bstly.we.oidc.businesslogic.model;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Class OidcAuthorizationRequest.
|
||||||
|
*/
|
||||||
|
public class OidcAuthorizationRequest {
|
||||||
|
|
||||||
|
private String code;
|
||||||
|
private final String client_id;
|
||||||
|
private final String response_type;
|
||||||
|
private final URI redirect_uri;
|
||||||
|
private final String scope;
|
||||||
|
private String state;
|
||||||
|
private String nonce;
|
||||||
|
private String display;
|
||||||
|
private String prompt;
|
||||||
|
private String max_age;
|
||||||
|
private String ui_locales;
|
||||||
|
private String id_token_hint;
|
||||||
|
private String login_hint;
|
||||||
|
private String acr_values;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new oidc authorization request.
|
||||||
|
*
|
||||||
|
* @param code the code
|
||||||
|
* @param client_id the client id
|
||||||
|
* @param response_type the response type
|
||||||
|
* @param redirect_uri the redirect uri
|
||||||
|
* @param scope the scope
|
||||||
|
* @param state the state
|
||||||
|
* @param nonce the nonce
|
||||||
|
* @param display the display
|
||||||
|
* @param prompt the prompt
|
||||||
|
* @param max_age the max age
|
||||||
|
* @param ui_locales the ui locales
|
||||||
|
* @param id_token_hint the id token hint
|
||||||
|
* @param login_hint the login hint
|
||||||
|
* @param acr_values the acr values
|
||||||
|
*/
|
||||||
|
public OidcAuthorizationRequest(String code, String client_id, String response_type, URI redirect_uri, String scope,
|
||||||
|
String state, String nonce, String display, String prompt, String max_age, String ui_locales,
|
||||||
|
String id_token_hint, String login_hint, String acr_values) {
|
||||||
|
super();
|
||||||
|
this.code = code;
|
||||||
|
this.client_id = client_id;
|
||||||
|
this.response_type = response_type;
|
||||||
|
this.redirect_uri = redirect_uri;
|
||||||
|
this.scope = scope;
|
||||||
|
this.state = state;
|
||||||
|
this.nonce = nonce;
|
||||||
|
this.display = display;
|
||||||
|
this.prompt = prompt;
|
||||||
|
this.max_age = max_age;
|
||||||
|
this.ui_locales = ui_locales;
|
||||||
|
this.id_token_hint = id_token_hint;
|
||||||
|
this.login_hint = login_hint;
|
||||||
|
this.acr_values = acr_values;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the code.
|
||||||
|
*
|
||||||
|
* @return the code
|
||||||
|
*/
|
||||||
|
public String getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the code.
|
||||||
|
*
|
||||||
|
* @param code the new code
|
||||||
|
*/
|
||||||
|
public void setCode(String code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the client id.
|
||||||
|
*
|
||||||
|
* @return the client id
|
||||||
|
*/
|
||||||
|
public String getClient_id() {
|
||||||
|
return client_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the response type.
|
||||||
|
*
|
||||||
|
* @return the response type
|
||||||
|
*/
|
||||||
|
public String getResponse_type() {
|
||||||
|
return response_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the redirect uri.
|
||||||
|
*
|
||||||
|
* @return the redirect uri
|
||||||
|
*/
|
||||||
|
public URI getRedirect_uri() {
|
||||||
|
return redirect_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the scope.
|
||||||
|
*
|
||||||
|
* @return the scope
|
||||||
|
*/
|
||||||
|
public String getScope() {
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the state.
|
||||||
|
*
|
||||||
|
* @return the state
|
||||||
|
*/
|
||||||
|
public String getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the state.
|
||||||
|
*
|
||||||
|
* @param state the new state
|
||||||
|
*/
|
||||||
|
public void setState(String state) {
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the display.
|
||||||
|
*
|
||||||
|
* @return the display
|
||||||
|
*/
|
||||||
|
public String getDisplay() {
|
||||||
|
return display;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the display.
|
||||||
|
*
|
||||||
|
* @param display the new display
|
||||||
|
*/
|
||||||
|
public void setDisplay(String display) {
|
||||||
|
this.display = display;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the nonce.
|
||||||
|
*
|
||||||
|
* @return the nonce
|
||||||
|
*/
|
||||||
|
public String getNonce() {
|
||||||
|
return nonce;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the nonce.
|
||||||
|
*
|
||||||
|
* @param nonce the new nonce
|
||||||
|
*/
|
||||||
|
public void setNonce(String nonce) {
|
||||||
|
this.nonce = nonce;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the prompt.
|
||||||
|
*
|
||||||
|
* @return the prompt
|
||||||
|
*/
|
||||||
|
public String getPrompt() {
|
||||||
|
return prompt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the prompt.
|
||||||
|
*
|
||||||
|
* @param prompt the new prompt
|
||||||
|
*/
|
||||||
|
public void setPrompt(String prompt) {
|
||||||
|
this.prompt = prompt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the max age.
|
||||||
|
*
|
||||||
|
* @return the max age
|
||||||
|
*/
|
||||||
|
public String getMax_age() {
|
||||||
|
return max_age;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the max age.
|
||||||
|
*
|
||||||
|
* @param max_age the new max age
|
||||||
|
*/
|
||||||
|
public void setMax_age(String max_age) {
|
||||||
|
this.max_age = max_age;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the ui locales.
|
||||||
|
*
|
||||||
|
* @return the ui locales
|
||||||
|
*/
|
||||||
|
public String getUi_locales() {
|
||||||
|
return ui_locales;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the ui locales.
|
||||||
|
*
|
||||||
|
* @param ui_locales the new ui locales
|
||||||
|
*/
|
||||||
|
public void setUi_locales(String ui_locales) {
|
||||||
|
this.ui_locales = ui_locales;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the id token hint.
|
||||||
|
*
|
||||||
|
* @return the id token hint
|
||||||
|
*/
|
||||||
|
public String getId_token_hint() {
|
||||||
|
return id_token_hint;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the id token hint.
|
||||||
|
*
|
||||||
|
* @param id_token_hint the new id token hint
|
||||||
|
*/
|
||||||
|
public void setId_token_hint(String id_token_hint) {
|
||||||
|
this.id_token_hint = id_token_hint;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the login hint.
|
||||||
|
*
|
||||||
|
* @return the login hint
|
||||||
|
*/
|
||||||
|
public String getLogin_hint() {
|
||||||
|
return login_hint;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the login hint.
|
||||||
|
*
|
||||||
|
* @param login_hint the new login hint
|
||||||
|
*/
|
||||||
|
public void setLogin_hint(String login_hint) {
|
||||||
|
this.login_hint = login_hint;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the acr values.
|
||||||
|
*
|
||||||
|
* @return the acr values
|
||||||
|
*/
|
||||||
|
public String getAcr_values() {
|
||||||
|
return acr_values;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the acr values.
|
||||||
|
*
|
||||||
|
* @param acr_values the new acr values
|
||||||
|
*/
|
||||||
|
public void setAcr_values(String acr_values) {
|
||||||
|
this.acr_values = acr_values;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package de.bstly.we.oidc.businesslogic.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Enum OidcAuthorizationResponseType.
|
||||||
|
*/
|
||||||
|
public enum OidcAuthorizationResponseType {
|
||||||
|
AUTHORIZATION_CODE("code");
|
||||||
|
|
||||||
|
private final String authorizationResponseType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new oidc authorization response type.
|
||||||
|
*
|
||||||
|
* @param authorizationGrantType the authorization grant type
|
||||||
|
*/
|
||||||
|
OidcAuthorizationResponseType(String authorizationGrantType) {
|
||||||
|
this.authorizationResponseType = authorizationGrantType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the authorization response type.
|
||||||
|
*
|
||||||
|
* @return the authorization response type
|
||||||
|
*/
|
||||||
|
public String getAuthorizationResponseType() {
|
||||||
|
return authorizationResponseType;
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package de.bstly.we.oidc.model;
|
package de.bstly.we.oidc.businesslogic.model;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Enum OidcClientAuthenticationMethod.
|
* The Enum OidcClientAuthenticationMethod.
|
@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package de.bstly.we.oidc.model;
|
package de.bstly.we.oidc.businesslogic.model;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Enum OidcTokenErrorCode.
|
* The Enum OidcTokenErrorCode.
|
@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package de.bstly.we.oidc.model;
|
package de.bstly.we.oidc.businesslogic.model;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
|
||||||
@ -14,6 +14,7 @@ public class OidcTokenRequest {
|
|||||||
private final OidcAuthorizationGrantType grant_type;
|
private final OidcAuthorizationGrantType grant_type;
|
||||||
private String client_id;
|
private String client_id;
|
||||||
private String client_secret;
|
private String client_secret;
|
||||||
|
private String refresh_token;
|
||||||
private final URI redirect_uri;
|
private final URI redirect_uri;
|
||||||
private final String scope;
|
private final String scope;
|
||||||
|
|
||||||
@ -24,16 +25,18 @@ public class OidcTokenRequest {
|
|||||||
* @param grant_type the grant type
|
* @param grant_type the grant type
|
||||||
* @param client_id the client id
|
* @param client_id the client id
|
||||||
* @param client_secret the client secret
|
* @param client_secret the client secret
|
||||||
|
* @param refresh_token the refresh token
|
||||||
* @param redirect_uri the redirect uri
|
* @param redirect_uri the redirect uri
|
||||||
* @param scope the scope
|
* @param scope the scope
|
||||||
*/
|
*/
|
||||||
public OidcTokenRequest(String code, OidcAuthorizationGrantType grant_type, String client_id, String client_secret,
|
public OidcTokenRequest(String code, OidcAuthorizationGrantType grant_type, String client_id, String client_secret,
|
||||||
URI redirect_uri, String scope) {
|
String refresh_token, URI redirect_uri, String scope) {
|
||||||
super();
|
super();
|
||||||
this.code = code;
|
this.code = code;
|
||||||
this.grant_type = grant_type;
|
this.grant_type = grant_type;
|
||||||
this.client_id = client_id;
|
this.client_id = client_id;
|
||||||
this.client_secret = client_secret;
|
this.client_secret = client_secret;
|
||||||
|
this.refresh_token = refresh_token;
|
||||||
this.redirect_uri = redirect_uri;
|
this.redirect_uri = redirect_uri;
|
||||||
this.scope = scope;
|
this.scope = scope;
|
||||||
}
|
}
|
||||||
@ -74,6 +77,24 @@ public class OidcTokenRequest {
|
|||||||
this.client_secret = client_secret;
|
this.client_secret = client_secret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the refresh token.
|
||||||
|
*
|
||||||
|
* @return the refresh token
|
||||||
|
*/
|
||||||
|
public String getRefresh_token() {
|
||||||
|
return refresh_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the refresh token.
|
||||||
|
*
|
||||||
|
* @param refresh_token the new refresh token
|
||||||
|
*/
|
||||||
|
public void setRefresh_token(String refresh_token) {
|
||||||
|
this.refresh_token = refresh_token;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the code.
|
* Gets the code.
|
||||||
*
|
*
|
@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package de.bstly.we.oidc.model;
|
package de.bstly.we.oidc.businesslogic.model;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Class OidcTokenResponse.
|
* The Class OidcTokenResponse.
|
@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package de.bstly.we.oidc.businesslogic.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Class OidcTokenRevokeRequest.
|
||||||
|
*/
|
||||||
|
public class OidcTokenRevokeRequest {
|
||||||
|
|
||||||
|
private String token;
|
||||||
|
private String token_type_hint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the token.
|
||||||
|
*
|
||||||
|
* @return the token
|
||||||
|
*/
|
||||||
|
public String getToken() {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the token.
|
||||||
|
*
|
||||||
|
* @param token the new token
|
||||||
|
*/
|
||||||
|
public void setToken(String token) {
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the token type hint.
|
||||||
|
*
|
||||||
|
* @return the token type hint
|
||||||
|
*/
|
||||||
|
public String getToken_type_hint() {
|
||||||
|
return token_type_hint;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the token type hint.
|
||||||
|
*
|
||||||
|
* @param token_type_hint the new token type hint
|
||||||
|
*/
|
||||||
|
public void setToken_type_hint(String token_type_hint) {
|
||||||
|
this.token_type_hint = token_type_hint;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -24,10 +24,10 @@ import com.google.common.collect.Sets;
|
|||||||
import de.bstly.we.controller.BaseController;
|
import de.bstly.we.controller.BaseController;
|
||||||
import de.bstly.we.controller.support.EntityResponseStatusException;
|
import de.bstly.we.controller.support.EntityResponseStatusException;
|
||||||
import de.bstly.we.oidc.businesslogic.OidcClientManager;
|
import de.bstly.we.oidc.businesslogic.OidcClientManager;
|
||||||
|
import de.bstly.we.oidc.businesslogic.model.OidcAuthorizationGrantType;
|
||||||
|
import de.bstly.we.oidc.businesslogic.model.OidcClientAuthenticationMethod;
|
||||||
import de.bstly.we.oidc.controller.model.OidcClientModel;
|
import de.bstly.we.oidc.controller.model.OidcClientModel;
|
||||||
import de.bstly.we.oidc.model.OidcAuthorizationGrantType;
|
|
||||||
import de.bstly.we.oidc.model.OidcClient;
|
import de.bstly.we.oidc.model.OidcClient;
|
||||||
import de.bstly.we.oidc.model.OidcClientAuthenticationMethod;
|
|
||||||
import de.bstly.we.oidc.repository.OidcClientRepository;
|
import de.bstly.we.oidc.repository.OidcClientRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -13,25 +13,31 @@ import javax.servlet.http.HttpServletResponse;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.util.UriComponentsBuilder;
|
||||||
|
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
import de.bstly.we.businesslogic.PermissionManager;
|
import de.bstly.we.businesslogic.PermissionManager;
|
||||||
import de.bstly.we.businesslogic.Permissions;
|
import de.bstly.we.businesslogic.Permissions;
|
||||||
import de.bstly.we.oidc.businesslogic.OidcAuthorizationCodeManager;
|
import de.bstly.we.oidc.businesslogic.OidcAuthorizationManager;
|
||||||
import de.bstly.we.oidc.businesslogic.OidcClientManager;
|
import de.bstly.we.oidc.businesslogic.OidcClientManager;
|
||||||
import de.bstly.we.oidc.model.OidcAuthorizationCode;
|
import de.bstly.we.oidc.businesslogic.exception.InvalidAuthorizationRequestException;
|
||||||
import de.bstly.we.oidc.model.OidcAuthorizationErrorCode;
|
import de.bstly.we.oidc.businesslogic.model.OidcAuthorizationCode;
|
||||||
import de.bstly.we.oidc.model.OidcAuthorizationGrantType;
|
import de.bstly.we.oidc.businesslogic.model.OidcAuthorizationErrorCode;
|
||||||
import de.bstly.we.oidc.model.OidcAuthorizationResponseType;
|
import de.bstly.we.oidc.businesslogic.model.OidcAuthorizationGrantType;
|
||||||
|
import de.bstly.we.oidc.businesslogic.model.OidcAuthorizationRequest;
|
||||||
|
import de.bstly.we.oidc.businesslogic.model.OidcAuthorizationResponseType;
|
||||||
import de.bstly.we.oidc.model.OidcClient;
|
import de.bstly.we.oidc.model.OidcClient;
|
||||||
import de.bstly.we.security.model.LocalUserDetails;
|
import de.bstly.we.security.model.LocalUserDetails;
|
||||||
|
|
||||||
@ -49,7 +55,10 @@ public class OidcAuthorizationController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private OidcClientManager oidcClientManager;
|
private OidcClientManager oidcClientManager;
|
||||||
@Autowired
|
@Autowired
|
||||||
private OidcAuthorizationCodeManager oidcAuthorizationCodeManager;
|
private OidcAuthorizationManager oidcAuthorizationManager;
|
||||||
|
|
||||||
|
@Value("${oidcAuthorizationFormUrl:/oidc/authorize/form}")
|
||||||
|
private String authorizationFormUrl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Authorization request.
|
* Authorization request.
|
||||||
@ -60,36 +69,21 @@ public class OidcAuthorizationController {
|
|||||||
* @param redirectUri the redirect uri
|
* @param redirectUri the redirect uri
|
||||||
* @param state the state
|
* @param state the state
|
||||||
* @param nonce the nonce
|
* @param nonce the nonce
|
||||||
|
* @param prompt the prompt
|
||||||
|
* @param login_hint the login hint
|
||||||
|
* @param code the code
|
||||||
* @param principal the principal
|
* @param principal the principal
|
||||||
* @param request the request
|
* @param request the request
|
||||||
* @param response the response
|
* @param response the response
|
||||||
* @throws IOException Signals that an I/O exception has occurred.
|
* @throws IOException Signals that an I/O exception has occurred.
|
||||||
*/
|
*/
|
||||||
@PreAuthorize("isAuthenticated()")
|
protected void authorizationRequest(String scope, String responseType, String clientId, URI redirectUri,
|
||||||
@GetMapping
|
String state, String nonce, String prompt, String login_hint, String code, LocalUserDetails principal,
|
||||||
void authorizationRequest(
|
HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||||
// for OIDC scope must contain "openid"
|
|
||||||
@RequestParam(name = "scope") String scope,
|
|
||||||
// for now only "code" is available
|
|
||||||
@RequestParam(name = "response_type") String responseType,
|
|
||||||
// client id
|
|
||||||
@RequestParam(name = "client_id") String clientId,
|
|
||||||
// redirect url
|
|
||||||
@RequestParam(name = "redirect_uri") URI redirectUri,
|
|
||||||
// optional state
|
|
||||||
@RequestParam(name = "state", required = false) String state,
|
|
||||||
// optional state
|
|
||||||
@RequestParam(name = "nonce", required = false) String nonce,
|
|
||||||
// authentication details
|
|
||||||
@AuthenticationPrincipal LocalUserDetails principal,
|
|
||||||
// the request
|
|
||||||
HttpServletRequest request,
|
|
||||||
// the response
|
|
||||||
HttpServletResponse response) throws IOException {
|
|
||||||
|
|
||||||
if (!StringUtils.hasText(clientId)) {
|
if (!StringUtils.hasText(clientId)) {
|
||||||
logger.debug("missing client_id");
|
logger.debug("missing client_id");
|
||||||
throw new InvalidAuthorizationRequestError(redirectUri, OidcAuthorizationErrorCode.INVALID_REQUEST,
|
throw new InvalidAuthorizationRequestException(redirectUri, OidcAuthorizationErrorCode.INVALID_REQUEST,
|
||||||
"missing client_id", state);
|
"missing client_id", state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,14 +91,14 @@ public class OidcAuthorizationController {
|
|||||||
|
|
||||||
if (client == null) {
|
if (client == null) {
|
||||||
logger.debug("invalid client_id: " + clientId);
|
logger.debug("invalid client_id: " + clientId);
|
||||||
throw new InvalidAuthorizationRequestError(redirectUri, OidcAuthorizationErrorCode.INVALID_REQUEST,
|
throw new InvalidAuthorizationRequestException(redirectUri, OidcAuthorizationErrorCode.INVALID_REQUEST,
|
||||||
"invalid client_id", state);
|
"invalid client_id", state);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!client.getRedirectUris().contains(redirectUri.toString())) {
|
if (!client.getRedirectUris().contains(redirectUri.toString())) {
|
||||||
logger.debug("invalid redirect_uri: " + redirectUri + " allowed: " + client.getRedirectUris());
|
logger.debug("invalid redirect_uri: " + redirectUri + " allowed: " + client.getRedirectUris());
|
||||||
throw new InvalidAuthorizationRequestError(redirectUri, OidcAuthorizationErrorCode.INVALID_REQUEST,
|
throw new InvalidAuthorizationRequestException(redirectUri, OidcAuthorizationErrorCode.INVALID_REQUEST,
|
||||||
"invalid redirect_uri", state);
|
"invalid redirect_uri", state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,23 +106,22 @@ public class OidcAuthorizationController {
|
|||||||
&& !permissionManager.hasPermission(principal.getUserId(), client.getClientName())
|
&& !permissionManager.hasPermission(principal.getUserId(), client.getClientName())
|
||||||
&& !permissionManager.hasPermission(principal.getUserId(), Permissions.ROLE_ADMIN)) {
|
&& !permissionManager.hasPermission(principal.getUserId(), Permissions.ROLE_ADMIN)) {
|
||||||
logger.debug("user not allowed: " + principal.getUserId() + " - " + client.getClientName());
|
logger.debug("user not allowed: " + principal.getUserId() + " - " + client.getClientName());
|
||||||
throw new InvalidAuthorizationRequestError(redirectUri, OidcAuthorizationErrorCode.ACCESS_DENIED,
|
throw new InvalidAuthorizationRequestException(redirectUri, OidcAuthorizationErrorCode.ACCESS_DENIED,
|
||||||
"user not allowed", state);
|
"user not allowed", state);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!client.getAuthorizationGrantTypes().contains(OidcAuthorizationGrantType.authorization_code)) {
|
if (!client.getAuthorizationGrantTypes().contains(OidcAuthorizationGrantType.authorization_code)) {
|
||||||
logger.debug("authorization grant type not allowed: " + OidcAuthorizationGrantType.authorization_code
|
logger.debug("authorization grant type not allowed: " + OidcAuthorizationGrantType.authorization_code
|
||||||
+ " - " + client.getClientName());
|
+ " - " + client.getClientName());
|
||||||
throw new InvalidAuthorizationRequestError(redirectUri, OidcAuthorizationErrorCode.UNAUTHORIZED_CLIENT,
|
throw new InvalidAuthorizationRequestException(redirectUri,
|
||||||
"authorization grant type not allowed", state);
|
OidcAuthorizationErrorCode.INVALID_REQUEST_OBJECT, "authorization grant type not allowed", state);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!responseType.equals(OidcAuthorizationResponseType.code.toString())) {
|
if (!responseType.equals(OidcAuthorizationResponseType.AUTHORIZATION_CODE.getAuthorizationResponseType())) {
|
||||||
logger.debug("response type not allowed: " + OidcAuthorizationResponseType.code
|
logger.debug("response type not allowed: "
|
||||||
|
+ OidcAuthorizationResponseType.AUTHORIZATION_CODE.getAuthorizationResponseType() + " - "
|
||||||
+ " - " + client.getClientName());
|
+ client.getClientName());
|
||||||
throw new InvalidAuthorizationRequestError(redirectUri,
|
throw new InvalidAuthorizationRequestException(redirectUri,
|
||||||
OidcAuthorizationErrorCode.UNSUPPORTED_RESPONSE_TYPE, "response type not allowed", state);
|
OidcAuthorizationErrorCode.UNSUPPORTED_RESPONSE_TYPE, "response type not allowed", state);
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -137,27 +130,151 @@ public class OidcAuthorizationController {
|
|||||||
|
|
||||||
if (!scopes.contains("openid")) {
|
if (!scopes.contains("openid")) {
|
||||||
logger.debug("missing openid scope: " + scopes + " - " + client.getClientName());
|
logger.debug("missing openid scope: " + scopes + " - " + client.getClientName());
|
||||||
throw new InvalidAuthorizationRequestError(redirectUri, OidcAuthorizationErrorCode.INVALID_SCOPE,
|
throw new InvalidAuthorizationRequestException(redirectUri, OidcAuthorizationErrorCode.INVALID_SCOPE,
|
||||||
"missing openid scope", state);
|
"missing openid scope", state);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OidcAuthorizationCode authorizationCode = oidcAuthorizationCodeManager.create(clientId, redirectUri, scopes,
|
if (!client.getScopes().containsAll(scopes)) {
|
||||||
principal.getUserId(), nonce);
|
throw new InvalidAuthorizationRequestException(redirectUri, OidcAuthorizationErrorCode.INVALID_SCOPE,
|
||||||
|
"some scopes not allowed", state);
|
||||||
|
}
|
||||||
|
UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUri(redirectUri);
|
||||||
|
|
||||||
String uri = redirectUri.toString();
|
Set<String> prompts = Sets.newHashSet();
|
||||||
|
|
||||||
if (StringUtils.hasText(redirectUri.getQuery())) {
|
if (StringUtils.hasText(prompt)) {
|
||||||
uri += "&code=" + authorizationCode.getCode();
|
prompts = Sets.newHashSet(prompt.split(" "));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prompts.contains("none") && prompts.size() > 1) {
|
||||||
|
throw new InvalidAuthorizationRequestException(redirectUri, OidcAuthorizationErrorCode.INVALID_REQUEST,
|
||||||
|
"prompt contains more than 'none'", state);
|
||||||
|
} else if (prompts.contains("none") && client.isAuthorize()
|
||||||
|
&& !oidcAuthorizationManager.isAuthorized(client.getId(), principal.getUserId(), scopes)) {
|
||||||
|
throw new InvalidAuthorizationRequestException(redirectUri, OidcAuthorizationErrorCode.INTERACTION_REQUIRED,
|
||||||
|
"prompt 'consent' required beforehand", state);
|
||||||
|
}
|
||||||
|
|
||||||
|
OidcAuthorizationCode authorizationCode;
|
||||||
|
|
||||||
|
if (StringUtils.hasText(code)) {
|
||||||
|
authorizationCode = oidcAuthorizationManager.getCode(code);
|
||||||
|
if (authorizationCode == null) {
|
||||||
|
throw new InvalidAuthorizationRequestException(redirectUri,
|
||||||
|
OidcAuthorizationErrorCode.INVALID_REQUEST_OBJECT, "invalid code", state);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!authorizationCode.getUserId().equals(principal.getUserId())) {
|
||||||
|
throw new InvalidAuthorizationRequestException(redirectUri, OidcAuthorizationErrorCode.LOGIN_REQUIRED,
|
||||||
|
"code does not match user", state);
|
||||||
|
}
|
||||||
|
|
||||||
|
oidcAuthorizationManager.authorize(client.getId(), principal.getUserId(), scopes);
|
||||||
} else {
|
} else {
|
||||||
uri += "?code=" + authorizationCode.getCode();
|
if (client.isAuthorize()
|
||||||
|
&& !oidcAuthorizationManager.isAuthorized(client.getId(), principal.getUserId(), scopes)
|
||||||
|
|| prompts.contains("consent")) {
|
||||||
|
uriBuilder = UriComponentsBuilder.fromUriString(authorizationFormUrl);
|
||||||
|
uriBuilder.queryParam("client_id", clientId);
|
||||||
|
uriBuilder.queryParam("response_type", responseType);
|
||||||
|
uriBuilder.queryParam("redirect_uri", redirectUri);
|
||||||
|
uriBuilder.queryParam("scope", scope);
|
||||||
|
|
||||||
|
if (StringUtils.hasText(nonce)) {
|
||||||
|
uriBuilder.queryParam("nonce", nonce);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.hasText(prompt)) {
|
||||||
|
uriBuilder.queryParam("prompt", prompt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
authorizationCode = oidcAuthorizationManager.createCode(clientId, redirectUri, scopes,
|
||||||
|
principal.getUserId(), nonce, request.getSession().getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uriBuilder.queryParam("code", authorizationCode.getCode());
|
||||||
|
|
||||||
if (StringUtils.hasText(state)) {
|
if (StringUtils.hasText(state)) {
|
||||||
uri += "&state=" + state;
|
uriBuilder.queryParam("state", state);
|
||||||
}
|
}
|
||||||
|
|
||||||
response.sendRedirect(uri);
|
if (StringUtils.hasText(login_hint)) {
|
||||||
|
uriBuilder.queryParam("login_hint", login_hint);
|
||||||
|
}
|
||||||
|
|
||||||
|
response.sendRedirect(uriBuilder.build().toUriString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authorization get request.
|
||||||
|
*
|
||||||
|
* @param scope the scope
|
||||||
|
* @param responseType the response type
|
||||||
|
* @param clientId the client id
|
||||||
|
* @param redirectUri the redirect uri
|
||||||
|
* @param state the state
|
||||||
|
* @param nonce the nonce
|
||||||
|
* @param prompt the prompt
|
||||||
|
* @param login_hint the login hint
|
||||||
|
* @param principal the principal
|
||||||
|
* @param request the request
|
||||||
|
* @param response the response
|
||||||
|
* @throws IOException Signals that an I/O exception has occurred.
|
||||||
|
*/
|
||||||
|
@PreAuthorize("authentication.authenticated")
|
||||||
|
@GetMapping
|
||||||
|
public void authorizationGetRequest(
|
||||||
|
// for OIDC scope must contain "openid"
|
||||||
|
@RequestParam(name = "scope") String scope,
|
||||||
|
// response type
|
||||||
|
@RequestParam(name = "response_type") String responseType,
|
||||||
|
// client id
|
||||||
|
@RequestParam(name = "client_id") String clientId,
|
||||||
|
// redirect url
|
||||||
|
@RequestParam(name = "redirect_uri") URI redirectUri,
|
||||||
|
// optional state
|
||||||
|
@RequestParam(name = "state", required = false) String state,
|
||||||
|
// optional nonce
|
||||||
|
@RequestParam(name = "nonce", required = false) String nonce,
|
||||||
|
// optional prompt
|
||||||
|
@RequestParam(name = "prompt", required = false) String prompt,
|
||||||
|
// optional login_hint
|
||||||
|
@RequestParam(name = "login_hint", required = false) String login_hint,
|
||||||
|
// authentication details
|
||||||
|
@AuthenticationPrincipal LocalUserDetails principal,
|
||||||
|
// the request
|
||||||
|
HttpServletRequest request,
|
||||||
|
// the response
|
||||||
|
HttpServletResponse response) throws IOException {
|
||||||
|
authorizationRequest(scope, responseType, clientId, redirectUri, state, nonce, prompt, login_hint, null,
|
||||||
|
principal, request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authorization post request.
|
||||||
|
*
|
||||||
|
* @param authorizationRequest the authorization request
|
||||||
|
* @param principal the principal
|
||||||
|
* @param request the request
|
||||||
|
* @param response the response
|
||||||
|
* @throws IOException Signals that an I/O exception has occurred.
|
||||||
|
*/
|
||||||
|
@PostMapping
|
||||||
|
@PreAuthorize("authentication.authenticated")
|
||||||
|
public void authorizationPostRequest(
|
||||||
|
// authorization request
|
||||||
|
@ModelAttribute("authorizationRequest") OidcAuthorizationRequest authorizationRequest,
|
||||||
|
// authentication details
|
||||||
|
@AuthenticationPrincipal LocalUserDetails principal,
|
||||||
|
// the request
|
||||||
|
HttpServletRequest request,
|
||||||
|
// the response
|
||||||
|
HttpServletResponse response) throws IOException {
|
||||||
|
authorizationRequest(authorizationRequest.getScope(), authorizationRequest.getResponse_type(),
|
||||||
|
authorizationRequest.getClient_id(), authorizationRequest.getRedirect_uri(),
|
||||||
|
authorizationRequest.getState(), authorizationRequest.getNonce(), authorizationRequest.getPrompt(),
|
||||||
|
authorizationRequest.getLogin_hint(), authorizationRequest.getCode(), principal, request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -167,126 +284,21 @@ public class OidcAuthorizationController {
|
|||||||
* @param response the response
|
* @param response the response
|
||||||
* @throws IOException Signals that an I/O exception has occurred.
|
* @throws IOException Signals that an I/O exception has occurred.
|
||||||
*/
|
*/
|
||||||
@ExceptionHandler(InvalidAuthorizationRequestError.class)
|
@ExceptionHandler(InvalidAuthorizationRequestException.class)
|
||||||
public void handle(InvalidAuthorizationRequestError exception, HttpServletResponse response) throws IOException {
|
public void handle(InvalidAuthorizationRequestException exception, HttpServletResponse response)
|
||||||
String uri = exception.getRedirectUri().toString();
|
throws IOException {
|
||||||
|
UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUri(exception.getRedirectUri());
|
||||||
|
|
||||||
uri += "?error=" + exception.getErrorCode().getAuthorizationErrorCode();
|
uriBuilder.queryParam("error", exception.getErrorCode().getAuthorizationErrorCode());
|
||||||
|
|
||||||
if (StringUtils.hasText(exception.getErrorDescription())) {
|
if (StringUtils.hasText(exception.getErrorDescription())) {
|
||||||
uri += "&error_description=" + exception.getErrorDescription();
|
uriBuilder.queryParam("error_description", exception.getErrorDescription());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StringUtils.hasText(exception.getState())) {
|
if (StringUtils.hasText(exception.getState())) {
|
||||||
uri += "&state=" + exception.getState();
|
uriBuilder.queryParam("state", exception.getState());
|
||||||
}
|
}
|
||||||
|
|
||||||
response.sendRedirect(uri);
|
response.sendRedirect(uriBuilder.build().toUriString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The Class InvalidAuthorizationRequestError.
|
|
||||||
*/
|
|
||||||
static class InvalidAuthorizationRequestError extends RuntimeException {
|
|
||||||
/**
|
|
||||||
* default serialVersionUID
|
|
||||||
*/
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private URI redirectUri;
|
|
||||||
private OidcAuthorizationErrorCode errorCode;
|
|
||||||
private String errorDescription;
|
|
||||||
private String state;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instantiates a new invalid authorization request error.
|
|
||||||
*
|
|
||||||
* @param redirectUri the redirect uri
|
|
||||||
* @param errorCode the error code
|
|
||||||
* @param errorDescription the error description
|
|
||||||
* @param state the state
|
|
||||||
*/
|
|
||||||
InvalidAuthorizationRequestError(URI redirectUri, OidcAuthorizationErrorCode errorCode, String errorDescription,
|
|
||||||
String state) {
|
|
||||||
super(errorDescription);
|
|
||||||
this.redirectUri = redirectUri;
|
|
||||||
this.errorCode = errorCode;
|
|
||||||
this.errorDescription = errorDescription;
|
|
||||||
this.state = state;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the redirect uri.
|
|
||||||
*
|
|
||||||
* @return the redirect uri
|
|
||||||
*/
|
|
||||||
public URI getRedirectUri() {
|
|
||||||
return redirectUri;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the redirect uri.
|
|
||||||
*
|
|
||||||
* @param redirectUri the new redirect uri
|
|
||||||
*/
|
|
||||||
public void setRedirectUri(URI redirectUri) {
|
|
||||||
this.redirectUri = redirectUri;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the error code.
|
|
||||||
*
|
|
||||||
* @return the error code
|
|
||||||
*/
|
|
||||||
public OidcAuthorizationErrorCode getErrorCode() {
|
|
||||||
return errorCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the error code.
|
|
||||||
*
|
|
||||||
* @param errorCode the new error code
|
|
||||||
*/
|
|
||||||
public void setErrorCode(OidcAuthorizationErrorCode errorCode) {
|
|
||||||
this.errorCode = errorCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the error description.
|
|
||||||
*
|
|
||||||
* @return the error description
|
|
||||||
*/
|
|
||||||
public String getErrorDescription() {
|
|
||||||
return errorDescription;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the error description.
|
|
||||||
*
|
|
||||||
* @param errorDescription the new error description
|
|
||||||
*/
|
|
||||||
public void setErrorDescription(String errorDescription) {
|
|
||||||
this.errorDescription = errorDescription;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the state.
|
|
||||||
*
|
|
||||||
* @return the state
|
|
||||||
*/
|
|
||||||
public String getState() {
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the state.
|
|
||||||
*
|
|
||||||
* @param state the new state
|
|
||||||
*/
|
|
||||||
public void setState(String state) {
|
|
||||||
this.state = state;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,8 @@ import java.net.URISyntaxException;
|
|||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
@ -19,7 +18,8 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
import de.bstly.we.controller.support.EntityResponseStatusException;
|
import de.bstly.we.controller.support.EntityResponseStatusException;
|
||||||
import de.bstly.we.oidc.model.OidcConfiguration;
|
import de.bstly.we.oidc.businesslogic.OidcClientManager;
|
||||||
|
import de.bstly.we.oidc.controller.model.OidcConfiguration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Class OidcDiscoveryController.
|
* The Class OidcDiscoveryController.
|
||||||
@ -28,8 +28,8 @@ import de.bstly.we.oidc.model.OidcConfiguration;
|
|||||||
@RestController
|
@RestController
|
||||||
public class OidcDiscoveryController {
|
public class OidcDiscoveryController {
|
||||||
|
|
||||||
@Value("${oidc.provider.issuer:}")
|
@Autowired
|
||||||
private String oidcIssuer;
|
private OidcClientManager oidcClientManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the configuration.
|
* Gets the configuration.
|
||||||
@ -42,14 +42,7 @@ public class OidcDiscoveryController {
|
|||||||
public OidcConfiguration getConfiguration(HttpServletRequest request, HttpServletResponse response) {
|
public OidcConfiguration getConfiguration(HttpServletRequest request, HttpServletResponse response) {
|
||||||
OidcConfiguration config = new OidcConfiguration();
|
OidcConfiguration config = new OidcConfiguration();
|
||||||
|
|
||||||
String issuer = oidcIssuer;
|
String issuer = oidcClientManager.getIssuer(request);
|
||||||
|
|
||||||
if (!StringUtils.hasText(issuer)) {
|
|
||||||
issuer = request.getScheme() + "://" + request.getServerName();
|
|
||||||
if (request.getServerPort() != 443 && request.getServerPort() != 80) {
|
|
||||||
issuer += ":" + request.getServerPort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
config.setIssuer(issuer);
|
config.setIssuer(issuer);
|
||||||
config.setScopes_supported(Sets.newHashSet("openid"));
|
config.setScopes_supported(Sets.newHashSet("openid"));
|
||||||
|
@ -10,6 +10,7 @@ import org.springframework.web.bind.annotation.GetMapping;
|
|||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import de.bstly.we.jwt.businesslogic.JwtKeyManager;
|
||||||
import de.bstly.we.oidc.businesslogic.OidcTokenManager;
|
import de.bstly.we.oidc.businesslogic.OidcTokenManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -20,7 +21,7 @@ import de.bstly.we.oidc.businesslogic.OidcTokenManager;
|
|||||||
public class OidcJwksController {
|
public class OidcJwksController {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private OidcTokenManager oidcTokenManager;
|
private JwtKeyManager jwtKeyManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the jwks.
|
* Gets the jwks.
|
||||||
@ -29,6 +30,6 @@ public class OidcJwksController {
|
|||||||
*/
|
*/
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public Map<String, Object> getJwks() {
|
public Map<String, Object> getJwks() {
|
||||||
return oidcTokenManager.getJwkSet().toJSONObject();
|
return jwtKeyManager.getJwkSet(OidcTokenManager.OIDC_JWT_KEY_NAME, false).toJSONObject();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,234 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package de.bstly.we.oidc.controller;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.web.authentication.RememberMeServices;
|
||||||
|
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
|
import de.bstly.we.businesslogic.PermissionManager;
|
||||||
|
import de.bstly.we.businesslogic.Permissions;
|
||||||
|
import de.bstly.we.businesslogic.SessionManager;
|
||||||
|
import de.bstly.we.businesslogic.UserManager;
|
||||||
|
import de.bstly.we.controller.BaseController;
|
||||||
|
import de.bstly.we.controller.support.EntityResponseStatusException;
|
||||||
|
import de.bstly.we.model.User;
|
||||||
|
import de.bstly.we.oidc.businesslogic.OidcClientManager;
|
||||||
|
import de.bstly.we.oidc.businesslogic.OidcSessionManager;
|
||||||
|
import de.bstly.we.oidc.controller.model.OidcClientInfo;
|
||||||
|
import de.bstly.we.oidc.model.OidcClient;
|
||||||
|
import de.bstly.we.oidc.model.OidcSession;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Class OidcSessionController.
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/oidc/session")
|
||||||
|
public class OidcSessionController extends BaseController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OidcClientManager oidcClientManager;
|
||||||
|
@Autowired
|
||||||
|
private OidcSessionManager oidcSessionManager;
|
||||||
|
@Autowired
|
||||||
|
private SessionManager sessionManager;
|
||||||
|
@Autowired
|
||||||
|
private RememberMeServices rememberMeServices;
|
||||||
|
@Autowired
|
||||||
|
private UserManager userManager;
|
||||||
|
@Autowired
|
||||||
|
private OidcClientManager oidcRegisteredClientManager;
|
||||||
|
@Autowired
|
||||||
|
private PermissionManager permissionManager;
|
||||||
|
|
||||||
|
@Value("${loginUrl:/login}")
|
||||||
|
private String loginUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the sessions.
|
||||||
|
*
|
||||||
|
* @return the sessions
|
||||||
|
*/
|
||||||
|
@PreAuthorize("authentication.authenticated")
|
||||||
|
@GetMapping
|
||||||
|
public List<OidcClientInfo> getSessions() {
|
||||||
|
Map<Long, OidcClientInfo> clients = Maps.newHashMap();
|
||||||
|
for (OidcSession session : oidcSessionManager.getAllBySubject(getCurrentUserId())) {
|
||||||
|
Long oidcClientId = session.getClientId();
|
||||||
|
OidcClientInfo clientInfo = clients.get(oidcClientId);
|
||||||
|
if (clientInfo == null) {
|
||||||
|
OidcClient client = oidcRegisteredClientManager.get(oidcClientId);
|
||||||
|
if (client == null || !client.isAlwaysPermitted()
|
||||||
|
&& (!permissionManager.hasPermission(getCurrentUserId(), client.getClientName())
|
||||||
|
|| !permissionManager.hasPermission(getCurrentUserId(), Permissions.ROLE_ADMIN))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
clientInfo = oidcRegisteredClientManager.getClientInfo(client, getCurrentUserId());
|
||||||
|
}
|
||||||
|
clientInfo.getSessions().add(session);
|
||||||
|
clients.put(session.getClientId(), clientInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Lists.newArrayList(clients.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the sessions for target.
|
||||||
|
*
|
||||||
|
* @param target the target
|
||||||
|
* @return the sessions for target
|
||||||
|
*/
|
||||||
|
@PreAuthorize("authentication.authenticated")
|
||||||
|
@GetMapping("/{target}")
|
||||||
|
public OidcClientInfo getSessionsForTarget(@PathVariable("target") Long target) {
|
||||||
|
OidcClient client = oidcRegisteredClientManager.get(target);
|
||||||
|
if (client == null || !client.isAlwaysPermitted()
|
||||||
|
&& (!permissionManager.hasPermission(getCurrentUserId(), client.getClientName())
|
||||||
|
|| !permissionManager.hasPermission(getCurrentUserId(), Permissions.ROLE_ADMIN))) {
|
||||||
|
throw new EntityResponseStatusException(HttpStatus.FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
OidcClientInfo clientInfo = oidcRegisteredClientManager.getClientInfo(client, getCurrentUserId());
|
||||||
|
clientInfo.getSessions().addAll(oidcSessionManager.getAllByTargetAndSubject(target, getCurrentUserId()));
|
||||||
|
return clientInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logout session.
|
||||||
|
*
|
||||||
|
* @param redirectUrlParam the redirect url param
|
||||||
|
* @param request the request
|
||||||
|
* @param response the response
|
||||||
|
* @throws IOException Signals that an I/O exception has occurred.
|
||||||
|
*/
|
||||||
|
@PreAuthorize("authentication.authenticated")
|
||||||
|
@RequestMapping(value = "/logout", method = { RequestMethod.GET, RequestMethod.POST })
|
||||||
|
public void logoutSession(@RequestParam("redirect_url") Optional<String> redirectUrlParam,
|
||||||
|
HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||||
|
String issuer = oidcClientManager.getIssuer(request);
|
||||||
|
oidcSessionManager.backchannelLogout(request.getSession().getId(), issuer);
|
||||||
|
internalLogout(redirectUrlParam, request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logout all.
|
||||||
|
*
|
||||||
|
* @param redirectUrlParam the redirect url param
|
||||||
|
* @param request the request
|
||||||
|
* @param response the response
|
||||||
|
* @throws IOException Signals that an I/O exception has occurred.
|
||||||
|
*/
|
||||||
|
@PreAuthorize("authentication.authenticated")
|
||||||
|
@RequestMapping(value = "/logout/all", method = { RequestMethod.GET, RequestMethod.POST })
|
||||||
|
public void logoutAll(@RequestParam("redirect_url") Optional<String> redirectUrlParam, HttpServletRequest request,
|
||||||
|
HttpServletResponse response) throws IOException {
|
||||||
|
String issuer = oidcClientManager.getIssuer(request);
|
||||||
|
oidcSessionManager.backchannelLogout(getCurrentUserId(), issuer);
|
||||||
|
|
||||||
|
User user = userManager.get(getCurrentUserId());
|
||||||
|
sessionManager.deleteSessionsForUser(user);
|
||||||
|
|
||||||
|
internalLogout(redirectUrlParam, request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logout by sid.
|
||||||
|
*
|
||||||
|
* @param sid the sid
|
||||||
|
* @param redirectUrlParam the redirect url param
|
||||||
|
* @param request the request
|
||||||
|
* @param response the response
|
||||||
|
* @throws IOException Signals that an I/O exception has occurred.
|
||||||
|
*/
|
||||||
|
@PreAuthorize("authentication.authenticated")
|
||||||
|
@RequestMapping(value = "/logout/sid/{sid}", method = { RequestMethod.GET, RequestMethod.POST })
|
||||||
|
public void logoutBySid(@PathVariable("sid") String sid,
|
||||||
|
@RequestParam("redirect_url") Optional<String> redirectUrlParam, HttpServletRequest request,
|
||||||
|
HttpServletResponse response) throws IOException {
|
||||||
|
String issuer = oidcClientManager.getIssuer(request);
|
||||||
|
oidcSessionManager.backchannelLogout(sid, issuer);
|
||||||
|
redirect(redirectUrlParam, request, response, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logout all for target.
|
||||||
|
*
|
||||||
|
* @param target the target
|
||||||
|
* @param redirectUrlParam the redirect url param
|
||||||
|
* @param request the request
|
||||||
|
* @param response the response
|
||||||
|
* @throws IOException Signals that an I/O exception has occurred.
|
||||||
|
*/
|
||||||
|
@PreAuthorize("authentication.authenticated")
|
||||||
|
@RequestMapping(value = "/logout/all/{target}", method = { RequestMethod.GET, RequestMethod.POST })
|
||||||
|
public void logoutAllForTarget(@PathVariable("target") Long target,
|
||||||
|
@RequestParam("redirect_url") Optional<String> redirectUrlParam, HttpServletRequest request,
|
||||||
|
HttpServletResponse response) throws IOException {
|
||||||
|
String issuer = oidcClientManager.getIssuer(request);
|
||||||
|
oidcSessionManager.backchannelLogout(target, getCurrentUserId(), issuer);
|
||||||
|
redirect(redirectUrlParam, request, response, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal logout.
|
||||||
|
*
|
||||||
|
* @param redirectUrlParam the redirect url param
|
||||||
|
* @param request the request
|
||||||
|
* @param response the response
|
||||||
|
* @throws IOException Signals that an I/O exception has occurred.
|
||||||
|
*/
|
||||||
|
protected void internalLogout(Optional<String> redirectUrlParam, HttpServletRequest request,
|
||||||
|
HttpServletResponse response) throws IOException {
|
||||||
|
new SecurityContextLogoutHandler().logout(request, response,
|
||||||
|
SecurityContextHolder.getContext().getAuthentication());
|
||||||
|
rememberMeServices.loginFail(request, response);
|
||||||
|
redirect(redirectUrlParam, request, response, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redirect.
|
||||||
|
*
|
||||||
|
* @param redirectUrlParam the redirect url param
|
||||||
|
* @param request the request
|
||||||
|
* @param response the response
|
||||||
|
* @param fallback the fallback
|
||||||
|
* @throws IOException Signals that an I/O exception has occurred.
|
||||||
|
*/
|
||||||
|
protected void redirect(Optional<String> redirectUrlParam, HttpServletRequest request, HttpServletResponse response,
|
||||||
|
boolean fallback) throws IOException {
|
||||||
|
String redirectUrl = redirectUrlParam.orElse(null);
|
||||||
|
|
||||||
|
if (!StringUtils.hasText(redirectUrl) && fallback) {
|
||||||
|
|
||||||
|
redirectUrl = loginUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.hasText(redirectUrl)) {
|
||||||
|
response.sendRedirect(redirectUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -11,15 +11,16 @@ import java.util.Set;
|
|||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.transaction.Transactional;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
@ -32,16 +33,19 @@ import org.springframework.web.server.ResponseStatusException;
|
|||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import com.nimbusds.jose.JOSEException;
|
import com.nimbusds.jose.JOSEException;
|
||||||
|
|
||||||
import de.bstly.we.oidc.businesslogic.OidcAuthorizationCodeManager;
|
import de.bstly.we.oidc.businesslogic.OidcAuthorizationManager;
|
||||||
import de.bstly.we.oidc.businesslogic.OidcClientManager;
|
import de.bstly.we.oidc.businesslogic.OidcClientManager;
|
||||||
|
import de.bstly.we.oidc.businesslogic.OidcSessionManager;
|
||||||
import de.bstly.we.oidc.businesslogic.OidcTokenManager;
|
import de.bstly.we.oidc.businesslogic.OidcTokenManager;
|
||||||
import de.bstly.we.oidc.model.OidcAuthorizationCode;
|
import de.bstly.we.oidc.businesslogic.exception.InvalidTokenRequestException;
|
||||||
|
import de.bstly.we.oidc.businesslogic.model.OidcAuthorizationCode;
|
||||||
|
import de.bstly.we.oidc.businesslogic.model.OidcClientAuthenticationMethod;
|
||||||
|
import de.bstly.we.oidc.businesslogic.model.OidcTokenErrorCode;
|
||||||
|
import de.bstly.we.oidc.businesslogic.model.OidcTokenRequest;
|
||||||
|
import de.bstly.we.oidc.businesslogic.model.OidcTokenResponse;
|
||||||
|
import de.bstly.we.oidc.businesslogic.model.OidcTokenRevokeRequest;
|
||||||
import de.bstly.we.oidc.model.OidcClient;
|
import de.bstly.we.oidc.model.OidcClient;
|
||||||
import de.bstly.we.oidc.model.OidcClientAuthenticationMethod;
|
|
||||||
import de.bstly.we.oidc.model.OidcToken;
|
import de.bstly.we.oidc.model.OidcToken;
|
||||||
import de.bstly.we.oidc.model.OidcTokenErrorCode;
|
|
||||||
import de.bstly.we.oidc.model.OidcTokenRequest;
|
|
||||||
import de.bstly.we.oidc.model.OidcTokenResponse;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Class OidcTokenController.
|
* The Class OidcTokenController.
|
||||||
@ -57,12 +61,11 @@ public class OidcTokenController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private OidcClientManager oidcClientManager;
|
private OidcClientManager oidcClientManager;
|
||||||
@Autowired
|
@Autowired
|
||||||
private OidcAuthorizationCodeManager oidcAuthorizationCodeManager;
|
private OidcAuthorizationManager oidcAuthorizationManager;
|
||||||
@Autowired
|
@Autowired
|
||||||
private OidcTokenManager oidcTokenManager;
|
private OidcTokenManager oidcTokenManager;
|
||||||
|
@Autowired
|
||||||
@Value("${oidc.provider.issuer:}")
|
private OidcSessionManager oidcSessionManager;
|
||||||
private String oidcIssuer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the token.
|
* Gets the token.
|
||||||
@ -74,6 +77,7 @@ public class OidcTokenController {
|
|||||||
* @return the token
|
* @return the token
|
||||||
*/
|
*/
|
||||||
@PostMapping
|
@PostMapping
|
||||||
|
@Transactional
|
||||||
public OidcTokenResponse getToken(
|
public OidcTokenResponse getToken(
|
||||||
// Authorization header for BASIC client authentication method
|
// Authorization header for BASIC client authentication method
|
||||||
@RequestHeader(name = HttpHeaders.AUTHORIZATION, required = false) String authorizationHeader,
|
@RequestHeader(name = HttpHeaders.AUTHORIZATION, required = false) String authorizationHeader,
|
||||||
@ -84,6 +88,8 @@ public class OidcTokenController {
|
|||||||
// the response
|
// the response
|
||||||
HttpServletResponse response) {
|
HttpServletResponse response) {
|
||||||
|
|
||||||
|
String issuer = oidcClientManager.getIssuer(request);
|
||||||
|
|
||||||
response.setHeader(HttpHeaders.CACHE_CONTROL, "no-store");
|
response.setHeader(HttpHeaders.CACHE_CONTROL, "no-store");
|
||||||
response.setHeader(HttpHeaders.PRAGMA, "no-cache");
|
response.setHeader(HttpHeaders.PRAGMA, "no-cache");
|
||||||
|
|
||||||
@ -101,52 +107,61 @@ public class OidcTokenController {
|
|||||||
clientAuthenticationMethod = OidcClientAuthenticationMethod.basic;
|
clientAuthenticationMethod = OidcClientAuthenticationMethod.basic;
|
||||||
} else {
|
} else {
|
||||||
logger.debug("invalid_basic_authentication: " + decoded);
|
logger.debug("invalid_basic_authentication: " + decoded);
|
||||||
throw new InvalidTokenRequestError(OidcTokenErrorCode.INVALID_CLIENT, "invalid_basic_authentication");
|
throw new InvalidTokenRequestException(OidcTokenErrorCode.INVALID_CLIENT,
|
||||||
|
"invalid_basic_authentication");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!StringUtils.hasText(tokenRequest.getClient_id())) {
|
||||||
|
throw new InvalidTokenRequestException(OidcTokenErrorCode.INVALID_REQUEST, "missing_client_id");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!StringUtils.hasText(tokenRequest.getClient_secret())) {
|
||||||
|
throw new InvalidTokenRequestException(OidcTokenErrorCode.INVALID_REQUEST, "missing_client_secret");
|
||||||
|
}
|
||||||
|
|
||||||
OidcClient client = oidcClientManager.getByClientIdAndSecret(tokenRequest.getClient_id(),
|
OidcClient client = oidcClientManager.getByClientIdAndSecret(tokenRequest.getClient_id(),
|
||||||
tokenRequest.getClient_secret());
|
tokenRequest.getClient_secret());
|
||||||
|
|
||||||
if (client == null) {
|
if (client == null) {
|
||||||
logger.debug("client not found: " + tokenRequest.getClient_id());
|
logger.debug("client not found: " + tokenRequest.getClient_id());
|
||||||
throw new InvalidTokenRequestError(OidcTokenErrorCode.INVALID_CLIENT, "invalid_client");
|
throw new InvalidTokenRequestException(OidcTokenErrorCode.INVALID_CLIENT, "invalid_client");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!client.getClientAuthenticationMethods().contains(clientAuthenticationMethod)) {
|
if (!client.getClientAuthenticationMethods().contains(clientAuthenticationMethod)) {
|
||||||
logger.debug("invalid_authentication_method: " + clientAuthenticationMethod);
|
logger.debug("invalid_authentication_method: " + clientAuthenticationMethod);
|
||||||
throw new InvalidTokenRequestError(OidcTokenErrorCode.INVALID_REQUEST, "invalid_authentication_method");
|
throw new InvalidTokenRequestException(OidcTokenErrorCode.INVALID_REQUEST, "invalid_authentication_method");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!client.getAuthorizationGrantTypes().contains(tokenRequest.getGrant_type())) {
|
if (!client.getAuthorizationGrantTypes().contains(tokenRequest.getGrant_type())) {
|
||||||
logger.debug("invalid_grant_type: " + tokenRequest.getGrant_type());
|
logger.debug("invalid_grant_type: " + tokenRequest.getGrant_type());
|
||||||
throw new InvalidTokenRequestError(OidcTokenErrorCode.UNAUTHORIZED_CLIENT, "invalid_grant_type");
|
throw new InvalidTokenRequestException(OidcTokenErrorCode.UNAUTHORIZED_CLIENT, "invalid_grant_type");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tokenRequest.getRedirect_uri() != null
|
if (tokenRequest.getRedirect_uri() != null
|
||||||
&& !client.getRedirectUris().contains(tokenRequest.getRedirect_uri().toString())) {
|
&& !client.getRedirectUris().contains(tokenRequest.getRedirect_uri().toString())) {
|
||||||
logger.debug("invalid redirect_uri: " + tokenRequest.getRedirect_uri().toString() + " allowed: "
|
logger.debug("invalid redirect_uri: " + tokenRequest.getRedirect_uri().toString() + " allowed: "
|
||||||
+ client.getRedirectUris());
|
+ client.getRedirectUris());
|
||||||
throw new InvalidTokenRequestError(OidcTokenErrorCode.INVALID_REQUEST, "invalid_redirect_uri");
|
throw new InvalidTokenRequestException(OidcTokenErrorCode.INVALID_REQUEST, "invalid_redirect_uri");
|
||||||
}
|
}
|
||||||
|
|
||||||
OidcToken token = null;
|
OidcToken token = null;
|
||||||
switch (tokenRequest.getGrant_type()) {
|
switch (tokenRequest.getGrant_type()) {
|
||||||
case authorization_code:
|
case authorization_code:
|
||||||
OidcAuthorizationCode authorizationCode = oidcAuthorizationCodeManager.getByCode(tokenRequest.getCode());
|
OidcAuthorizationCode authorizationCode = oidcAuthorizationManager.getCode(tokenRequest.getCode());
|
||||||
if (authorizationCode == null) {
|
if (authorizationCode == null) {
|
||||||
logger.debug("invalid authorization code: " + tokenRequest.getCode());
|
logger.debug("invalid authorization code: " + tokenRequest.getCode());
|
||||||
throw new InvalidTokenRequestError(OidcTokenErrorCode.INVALID_GRANT, "invalid_authorization_code");
|
throw new InvalidTokenRequestException(OidcTokenErrorCode.INVALID_GRANT, "invalid_authorization_code");
|
||||||
}
|
}
|
||||||
if (Instant.now().isAfter(authorizationCode.getExpiry())) {
|
if (Instant.now().isAfter(authorizationCode.getExpiry())) {
|
||||||
logger.debug("authorization code expired: " + authorizationCode.getExpiry());
|
logger.debug("authorization code expired: " + authorizationCode.getExpiry());
|
||||||
throw new InvalidTokenRequestError(OidcTokenErrorCode.INVALID_GRANT, "invalid_authorization_code");
|
throw new InvalidTokenRequestException(OidcTokenErrorCode.INVALID_GRANT, "invalid_authorization_code");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tokenRequest.getClient_id().equals(authorizationCode.getClientId())) {
|
if (!tokenRequest.getClient_id().equals(authorizationCode.getClientId())) {
|
||||||
logger.debug("invalid client for authorization code, expected: " + authorizationCode.getClientId()
|
logger.debug("invalid client for authorization code, expected: " + authorizationCode.getClientId()
|
||||||
+ " got: " + tokenRequest.getClient_id());
|
+ " got: " + tokenRequest.getClient_id());
|
||||||
throw new InvalidTokenRequestError(OidcTokenErrorCode.INVALID_CLIENT, "invalid_client");
|
throw new InvalidTokenRequestException(OidcTokenErrorCode.INVALID_CLIENT, "invalid_client");
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<String> scopes = StringUtils.hasText(tokenRequest.getScope())
|
Set<String> scopes = StringUtils.hasText(tokenRequest.getScope())
|
||||||
@ -155,46 +170,80 @@ public class OidcTokenController {
|
|||||||
|
|
||||||
if (!scopes.contains("openid") || !client.getScopes().containsAll(scopes)) {
|
if (!scopes.contains("openid") || !client.getScopes().containsAll(scopes)) {
|
||||||
logger.debug("missing openid scope: " + scopes + " - " + client.getClientName());
|
logger.debug("missing openid scope: " + scopes + " - " + client.getClientName());
|
||||||
throw new InvalidTokenRequestError(OidcTokenErrorCode.INVALID_SCOPE, "invalid scopes");
|
throw new InvalidTokenRequestException(OidcTokenErrorCode.INVALID_SCOPE, "invalid scopes");
|
||||||
}
|
|
||||||
|
|
||||||
String issuer = oidcIssuer;
|
|
||||||
|
|
||||||
if (!StringUtils.hasText(issuer)) {
|
|
||||||
issuer = request.getScheme() + "://" + request.getServerName();
|
|
||||||
if (request.getServerPort() != 443 && request.getServerPort() != 80) {
|
|
||||||
issuer += ":" + request.getServerPort();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
String sid = oidcSessionManager.createSid();
|
||||||
token = oidcTokenManager.createTokenWithIdToken(client, authorizationCode.getUserId(),
|
token = oidcTokenManager.createTokenWithIdToken(client, authorizationCode.getUserId(),
|
||||||
authorizationCode.getNonce(), scopes, issuer);
|
authorizationCode.getNonce(), scopes, issuer, sid);
|
||||||
|
oidcSessionManager.createSession(client.getId(), authorizationCode.getUserId(), token.getIdToken(), sid,
|
||||||
|
authorizationCode.getSpringSession());
|
||||||
} catch (JOSEException e) {
|
} catch (JOSEException e) {
|
||||||
logger.error("error creating token", client, authorizationCode);
|
logger.error("error creating token", client, authorizationCode);
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR);
|
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
oidcAuthorizationCodeManager.removeByCode(tokenRequest.getCode());
|
oidcAuthorizationManager.removeCode(tokenRequest.getCode());
|
||||||
break;
|
break;
|
||||||
case client_credentials:
|
case client_credentials:
|
||||||
token = oidcTokenManager.createToken(client, client.getId());
|
token = oidcTokenManager.createToken(client, client.getId(), false);
|
||||||
|
break;
|
||||||
|
case refresh_token:
|
||||||
|
if (!StringUtils.hasText(tokenRequest.getRefresh_token())) {
|
||||||
|
throw new InvalidTokenRequestException(OidcTokenErrorCode.INVALID_GRANT, "invalid_refresh_token");
|
||||||
|
}
|
||||||
|
|
||||||
|
OidcToken refreshToken = oidcTokenManager.getLastByRefreshToken(tokenRequest.getRefresh_token());
|
||||||
|
|
||||||
|
if (refreshToken == null || !(client.getId().equals(refreshToken.getClient()))) {
|
||||||
|
throw new InvalidTokenRequestException(OidcTokenErrorCode.INVALID_GRANT, "invalid_refresh_token");
|
||||||
|
}
|
||||||
|
|
||||||
|
token = oidcTokenManager.createToken(client, client.getId(), tokenRequest.getRefresh_token());
|
||||||
|
token.setRefreshToken(null);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (token == null) {
|
||||||
|
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
OidcTokenResponse tokenResponse = new OidcTokenResponse();
|
OidcTokenResponse tokenResponse = new OidcTokenResponse();
|
||||||
|
|
||||||
tokenResponse.setAccess_token(token.getAccessToken());
|
tokenResponse.setAccess_token(token.getAccessToken());
|
||||||
tokenResponse.setId_token(token.getIdToken());
|
if (StringUtils.hasText(token.getRefreshToken())) {
|
||||||
|
tokenResponse.setRefresh_token(token.getRefreshToken());
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(token.getIdToken())) {
|
||||||
|
tokenResponse.setId_token(token.getIdToken());
|
||||||
|
}
|
||||||
tokenResponse.setToken_type(OidcTokenManager.BEARER_TOKEN_TYPE);
|
tokenResponse.setToken_type(OidcTokenManager.BEARER_TOKEN_TYPE);
|
||||||
tokenResponse.setExpires_in(client.getTokenLifetime());
|
tokenResponse.setExpires_in(token.getExpiresIn());
|
||||||
|
|
||||||
oidcAuthorizationCodeManager.removeByCode(tokenRequest.getCode());
|
|
||||||
|
|
||||||
return tokenResponse;
|
return tokenResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Revoke.
|
||||||
|
*
|
||||||
|
* @param oidcTokenRevokeRequest the oidc token revoke request
|
||||||
|
*/
|
||||||
|
@PreAuthorize("authentication.authenticated")
|
||||||
|
@PostMapping("revoke")
|
||||||
|
@Transactional
|
||||||
|
public void revoke(@ModelAttribute("oidcTokenRevokeRequest") OidcTokenRevokeRequest oidcTokenRevokeRequest) {
|
||||||
|
if (!StringUtils.hasText(oidcTokenRevokeRequest.getToken())) {
|
||||||
|
throw new ResponseStatusException(HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("refresh_token".equals(oidcTokenRevokeRequest.getToken_type_hint())) {
|
||||||
|
oidcTokenManager.deleteAllByRefreshToken(oidcTokenRevokeRequest.getToken());
|
||||||
|
} else if ("access_token".equals(oidcTokenRevokeRequest.getToken_type_hint())
|
||||||
|
|| oidcTokenRevokeRequest.getToken_type_hint() == null) {
|
||||||
|
oidcTokenManager.deleteByAccessToken(oidcTokenRevokeRequest.getToken());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle.
|
* Handle.
|
||||||
*
|
*
|
||||||
@ -203,74 +252,11 @@ public class OidcTokenController {
|
|||||||
* @return the response entity
|
* @return the response entity
|
||||||
* @throws IOException Signals that an I/O exception has occurred.
|
* @throws IOException Signals that an I/O exception has occurred.
|
||||||
*/
|
*/
|
||||||
@ExceptionHandler(InvalidTokenRequestError.class)
|
@ExceptionHandler(InvalidTokenRequestException.class)
|
||||||
public ResponseEntity<String> handle(InvalidTokenRequestError exception, HttpServletResponse response)
|
public ResponseEntity<String> handle(InvalidTokenRequestException exception, HttpServletResponse response)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
// response.sendError(400, "redirect uri mismatch");
|
// response.sendError(400, "redirect uri mismatch");
|
||||||
return ResponseEntity.badRequest().contentType(MediaType.APPLICATION_JSON)
|
return ResponseEntity.badRequest().contentType(MediaType.APPLICATION_JSON)
|
||||||
.body(" {\"error\": \"" + exception.getMessage() + "\"}");
|
.body(" {\"error\": \"" + exception.getMessage() + "\"}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The Class InvalidTokenRequestError.
|
|
||||||
*/
|
|
||||||
static class InvalidTokenRequestError extends RuntimeException {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* default serialVersionUID
|
|
||||||
*/
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private OidcTokenErrorCode errorCode;
|
|
||||||
private String errorDescription;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instantiates a new invalid token request error.
|
|
||||||
*
|
|
||||||
* @param errorCode the error code
|
|
||||||
* @param errorDescription the error description
|
|
||||||
*/
|
|
||||||
InvalidTokenRequestError(OidcTokenErrorCode errorCode, String errorDescription) {
|
|
||||||
super(errorDescription);
|
|
||||||
this.errorCode = errorCode;
|
|
||||||
this.errorDescription = errorDescription;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the error code.
|
|
||||||
*
|
|
||||||
* @return the error code
|
|
||||||
*/
|
|
||||||
public OidcTokenErrorCode getErrorCode() {
|
|
||||||
return errorCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the error code.
|
|
||||||
*
|
|
||||||
* @param errorCode the new error code
|
|
||||||
*/
|
|
||||||
public void setErrorCode(OidcTokenErrorCode errorCode) {
|
|
||||||
this.errorCode = errorCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the error description.
|
|
||||||
*
|
|
||||||
* @return the error description
|
|
||||||
*/
|
|
||||||
public String getErrorDescription() {
|
|
||||||
return errorDescription;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the error description.
|
|
||||||
*
|
|
||||||
* @param errorDescription the new error description
|
|
||||||
*/
|
|
||||||
public void setErrorDescription(String errorDescription) {
|
|
||||||
this.errorDescription = errorDescription;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,266 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package de.bstly.we.oidc.controller.model;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import de.bstly.we.oidc.model.OidcSession;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Class OidcClientInfo.
|
||||||
|
*/
|
||||||
|
public class OidcClientInfo {
|
||||||
|
|
||||||
|
private String clientId;
|
||||||
|
private String name;
|
||||||
|
private String description;
|
||||||
|
private String loginUrl;
|
||||||
|
private String frontchannelLogoutUri;
|
||||||
|
private boolean frontchannelLogoutSessionRequired;
|
||||||
|
private boolean backchannelLogout;
|
||||||
|
private boolean authorize;
|
||||||
|
private Set<String> scopes;
|
||||||
|
private Map<String, Set<String>> claimMapping;
|
||||||
|
private Set<String> authorizedScopes;
|
||||||
|
private Map<String, Object> claims;
|
||||||
|
private List<OidcSession> sessions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the client id.
|
||||||
|
*
|
||||||
|
* @return the client id
|
||||||
|
*/
|
||||||
|
public String getClientId() {
|
||||||
|
return clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the client id.
|
||||||
|
*
|
||||||
|
* @param clientId the new client id
|
||||||
|
*/
|
||||||
|
public void setClientId(String clientId) {
|
||||||
|
this.clientId = clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the name.
|
||||||
|
*
|
||||||
|
* @return the name
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the name.
|
||||||
|
*
|
||||||
|
* @param name the new name
|
||||||
|
*/
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the description.
|
||||||
|
*
|
||||||
|
* @return the description
|
||||||
|
*/
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the description.
|
||||||
|
*
|
||||||
|
* @param description the new description
|
||||||
|
*/
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the login url.
|
||||||
|
*
|
||||||
|
* @return the login url
|
||||||
|
*/
|
||||||
|
public String getLoginUrl() {
|
||||||
|
return loginUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the login url.
|
||||||
|
*
|
||||||
|
* @param loginUrl the new login url
|
||||||
|
*/
|
||||||
|
public void setLoginUrl(String loginUrl) {
|
||||||
|
this.loginUrl = loginUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the frontchannel logout uri.
|
||||||
|
*
|
||||||
|
* @return the frontchannel logout uri
|
||||||
|
*/
|
||||||
|
public String getFrontchannelLogoutUri() {
|
||||||
|
return frontchannelLogoutUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the frontchannel logout uri.
|
||||||
|
*
|
||||||
|
* @param frontchannelLogoutUri the new frontchannel logout uri
|
||||||
|
*/
|
||||||
|
public void setFrontchannelLogoutUri(String frontchannelLogoutUri) {
|
||||||
|
this.frontchannelLogoutUri = frontchannelLogoutUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if is frontchannel logout session required.
|
||||||
|
*
|
||||||
|
* @return true, if is frontchannel logout session required
|
||||||
|
*/
|
||||||
|
public boolean isFrontchannelLogoutSessionRequired() {
|
||||||
|
return frontchannelLogoutSessionRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the frontchannel logout session required.
|
||||||
|
*
|
||||||
|
* @param frontchannelLogoutSessionRequired the new frontchannel logout session
|
||||||
|
* required
|
||||||
|
*/
|
||||||
|
public void setFrontchannelLogoutSessionRequired(boolean frontchannelLogoutSessionRequired) {
|
||||||
|
this.frontchannelLogoutSessionRequired = frontchannelLogoutSessionRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if is backchannel logout.
|
||||||
|
*
|
||||||
|
* @return true, if is backchannel logout
|
||||||
|
*/
|
||||||
|
public boolean isBackchannelLogout() {
|
||||||
|
return backchannelLogout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the backchannel logout.
|
||||||
|
*
|
||||||
|
* @param backchannelLogout the new backchannel logout
|
||||||
|
*/
|
||||||
|
public void setBackchannelLogout(boolean backchannelLogout) {
|
||||||
|
this.backchannelLogout = backchannelLogout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if is authorize.
|
||||||
|
*
|
||||||
|
* @return true, if is authorize
|
||||||
|
*/
|
||||||
|
public boolean isAuthorize() {
|
||||||
|
return authorize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the authorize.
|
||||||
|
*
|
||||||
|
* @param authorize the new authorize
|
||||||
|
*/
|
||||||
|
public void setAuthorize(boolean authorize) {
|
||||||
|
this.authorize = authorize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the scopes.
|
||||||
|
*
|
||||||
|
* @return the scopes
|
||||||
|
*/
|
||||||
|
public Set<String> getScopes() {
|
||||||
|
return scopes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the scopes.
|
||||||
|
*
|
||||||
|
* @param scopes the new scopes
|
||||||
|
*/
|
||||||
|
public void setScopes(Set<String> scopes) {
|
||||||
|
this.scopes = scopes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the claim mapping.
|
||||||
|
*
|
||||||
|
* @return the claim mapping
|
||||||
|
*/
|
||||||
|
public Map<String, Set<String>> getClaimMapping() {
|
||||||
|
return claimMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the claim mapping.
|
||||||
|
*
|
||||||
|
* @param claimMapping the claim mapping
|
||||||
|
*/
|
||||||
|
public void setClaimMapping(Map<String, Set<String>> claimMapping) {
|
||||||
|
this.claimMapping = claimMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the authorized scopes.
|
||||||
|
*
|
||||||
|
* @return the authorized scopes
|
||||||
|
*/
|
||||||
|
public Set<String> getAuthorizedScopes() {
|
||||||
|
return authorizedScopes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the authorized scopes.
|
||||||
|
*
|
||||||
|
* @param authorizedScopes the new authorized scopes
|
||||||
|
*/
|
||||||
|
public void setAuthorizedScopes(Set<String> authorizedScopes) {
|
||||||
|
this.authorizedScopes = authorizedScopes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the claims.
|
||||||
|
*
|
||||||
|
* @return the claims
|
||||||
|
*/
|
||||||
|
public Map<String, Object> getClaims() {
|
||||||
|
return claims;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the claims.
|
||||||
|
*
|
||||||
|
* @param claims the claims
|
||||||
|
*/
|
||||||
|
public void setClaims(Map<String, Object> claims) {
|
||||||
|
this.claims = claims;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the sessions.
|
||||||
|
*
|
||||||
|
* @return the sessions
|
||||||
|
*/
|
||||||
|
public List<OidcSession> getSessions() {
|
||||||
|
return sessions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the sessions.
|
||||||
|
*
|
||||||
|
* @param sessions the new sessions
|
||||||
|
*/
|
||||||
|
public void setSessions(List<OidcSession> sessions) {
|
||||||
|
this.sessions = sessions;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -5,8 +5,8 @@ package de.bstly.we.oidc.controller.model;
|
|||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import de.bstly.we.oidc.model.OidcAuthorizationGrantType;
|
import de.bstly.we.oidc.businesslogic.model.OidcAuthorizationGrantType;
|
||||||
import de.bstly.we.oidc.model.OidcClientAuthenticationMethod;
|
import de.bstly.we.oidc.businesslogic.model.OidcClientAuthenticationMethod;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Class OidcClientModel.
|
* The Class OidcClientModel.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package de.bstly.we.oidc.model;
|
package de.bstly.we.oidc.controller.model;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
@ -0,0 +1,94 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package de.bstly.we.oidc.model;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.persistence.CollectionTable;
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.ElementCollection;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
import org.hibernate.annotations.LazyCollection;
|
||||||
|
import org.hibernate.annotations.LazyCollectionOption;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Class OidcAuthorization.
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(name = "oidc_authorizations")
|
||||||
|
public class OidcAuthorization {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Column(name = "id")
|
||||||
|
private Long id;
|
||||||
|
@Column(name = "client_id")
|
||||||
|
private Long client;
|
||||||
|
@Column(name = "subject")
|
||||||
|
private Long subject;
|
||||||
|
@ElementCollection
|
||||||
|
@LazyCollection(LazyCollectionOption.FALSE)
|
||||||
|
@CollectionTable(name = "oidc_authorizations_scopes")
|
||||||
|
private Set<String> scopes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the client.
|
||||||
|
*
|
||||||
|
* @return the client
|
||||||
|
*/
|
||||||
|
public Long getClient() {
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the client.
|
||||||
|
*
|
||||||
|
* @param client the new client
|
||||||
|
*/
|
||||||
|
public void setClient(Long client) {
|
||||||
|
this.client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the subject.
|
||||||
|
*
|
||||||
|
* @return the subject
|
||||||
|
*/
|
||||||
|
public Long getSubject() {
|
||||||
|
return subject;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the subject.
|
||||||
|
*
|
||||||
|
* @param subject the new subject
|
||||||
|
*/
|
||||||
|
public void setSubject(Long subject) {
|
||||||
|
this.subject = subject;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the scopes.
|
||||||
|
*
|
||||||
|
* @return the scopes
|
||||||
|
*/
|
||||||
|
public Set<String> getScopes() {
|
||||||
|
return scopes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the scopes.
|
||||||
|
*
|
||||||
|
* @param scopes the new scopes
|
||||||
|
*/
|
||||||
|
public void setScopes(Set<String> scopes) {
|
||||||
|
this.scopes = scopes;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,11 +0,0 @@
|
|||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package de.bstly.we.oidc.model;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Enum OidcAuthorizationResponseType.
|
|
||||||
*/
|
|
||||||
public enum OidcAuthorizationResponseType {
|
|
||||||
code
|
|
||||||
}
|
|
@ -19,6 +19,9 @@ import javax.persistence.Table;
|
|||||||
import org.hibernate.annotations.LazyCollection;
|
import org.hibernate.annotations.LazyCollection;
|
||||||
import org.hibernate.annotations.LazyCollectionOption;
|
import org.hibernate.annotations.LazyCollectionOption;
|
||||||
|
|
||||||
|
import de.bstly.we.oidc.businesslogic.model.OidcAuthorizationGrantType;
|
||||||
|
import de.bstly.we.oidc.businesslogic.model.OidcClientAuthenticationMethod;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Class OidcClient.
|
* The Class OidcClient.
|
||||||
*/
|
*/
|
||||||
@ -58,6 +61,16 @@ public class OidcClient {
|
|||||||
private Long tokenLifetime;
|
private Long tokenLifetime;
|
||||||
@Column(name = "login_url", length = 1024)
|
@Column(name = "login_url", length = 1024)
|
||||||
private String loginUrl;
|
private String loginUrl;
|
||||||
|
@Column(name = "frontchannel_logout_uri")
|
||||||
|
private String frontchannelLogoutUri;
|
||||||
|
@Column(name = "frontchannel_logout_session_required", columnDefinition = "boolean default false")
|
||||||
|
private boolean frontchannelLogoutSessionRequired;
|
||||||
|
@Column(name = "backchannel_logout_uri")
|
||||||
|
private String backchannelLogoutUri;
|
||||||
|
@Column(name = "backchannel_logout_session_required", columnDefinition = "boolean default false")
|
||||||
|
private boolean backchannelLogoutSessionRequired;
|
||||||
|
@Column(name = "authorize", columnDefinition = "boolean default false")
|
||||||
|
private boolean authorize;
|
||||||
@Column(name = "always_permitted", columnDefinition = "boolean default false")
|
@Column(name = "always_permitted", columnDefinition = "boolean default false")
|
||||||
private boolean alwaysPermitted;
|
private boolean alwaysPermitted;
|
||||||
@Column(name = "category")
|
@Column(name = "category")
|
||||||
@ -243,6 +256,98 @@ public class OidcClient {
|
|||||||
this.loginUrl = loginUrl;
|
this.loginUrl = loginUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the frontchannel logout uri.
|
||||||
|
*
|
||||||
|
* @return the frontchannel logout uri
|
||||||
|
*/
|
||||||
|
public String getFrontchannelLogoutUri() {
|
||||||
|
return frontchannelLogoutUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the frontchannel logout uri.
|
||||||
|
*
|
||||||
|
* @param frontchannelLogoutUri the new frontchannel logout uri
|
||||||
|
*/
|
||||||
|
public void setFrontchannelLogoutUri(String frontchannelLogoutUri) {
|
||||||
|
this.frontchannelLogoutUri = frontchannelLogoutUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if is frontchannel logout session required.
|
||||||
|
*
|
||||||
|
* @return true, if is frontchannel logout session required
|
||||||
|
*/
|
||||||
|
public boolean isFrontchannelLogoutSessionRequired() {
|
||||||
|
return frontchannelLogoutSessionRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the frontchannel logout session required.
|
||||||
|
*
|
||||||
|
* @param frontchannelLogoutSessionRequired the new frontchannel logout session
|
||||||
|
* required
|
||||||
|
*/
|
||||||
|
public void setFrontchannelLogoutSessionRequired(boolean frontchannelLogoutSessionRequired) {
|
||||||
|
this.frontchannelLogoutSessionRequired = frontchannelLogoutSessionRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the backchannel logout uri.
|
||||||
|
*
|
||||||
|
* @return the backchannel logout uri
|
||||||
|
*/
|
||||||
|
public String getBackchannelLogoutUri() {
|
||||||
|
return backchannelLogoutUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the backchannel logout uri.
|
||||||
|
*
|
||||||
|
* @param backchannelLogoutUri the new backchannel logout uri
|
||||||
|
*/
|
||||||
|
public void setBackchannelLogoutUri(String backchannelLogoutUri) {
|
||||||
|
this.backchannelLogoutUri = backchannelLogoutUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if is backchannel logout session required.
|
||||||
|
*
|
||||||
|
* @return true, if is backchannel logout session required
|
||||||
|
*/
|
||||||
|
public boolean isBackchannelLogoutSessionRequired() {
|
||||||
|
return backchannelLogoutSessionRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the backchannel logout session required.
|
||||||
|
*
|
||||||
|
* @param backchannelLogoutSessionRequired the new backchannel logout session
|
||||||
|
* required
|
||||||
|
*/
|
||||||
|
public void setBackchannelLogoutSessionRequired(boolean backchannelLogoutSessionRequired) {
|
||||||
|
this.backchannelLogoutSessionRequired = backchannelLogoutSessionRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if is authorize.
|
||||||
|
*
|
||||||
|
* @return true, if is authorize
|
||||||
|
*/
|
||||||
|
public boolean isAuthorize() {
|
||||||
|
return authorize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the authorize.
|
||||||
|
*
|
||||||
|
* @param authorize the new authorize
|
||||||
|
*/
|
||||||
|
public void setAuthorize(boolean authorize) {
|
||||||
|
this.authorize = authorize;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if is always permitted.
|
* Checks if is always permitted.
|
||||||
*
|
*
|
||||||
|
143
oidc/src/main/java/de/bstly/we/oidc/model/OidcSession.java
Normal file
143
oidc/src/main/java/de/bstly/we/oidc/model/OidcSession.java
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package de.bstly.we.oidc.model;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Class OidcSession.
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(name = "oidc_sessions")
|
||||||
|
public class OidcSession {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Column(name = "id")
|
||||||
|
private Long id;
|
||||||
|
@Column(name = "client_id")
|
||||||
|
private Long client;
|
||||||
|
@Column(name = "subject")
|
||||||
|
private Long subject;
|
||||||
|
@Column(name = "sid")
|
||||||
|
private String sid;
|
||||||
|
@Column(name = "id_token", length = 4000)
|
||||||
|
private String idToken;
|
||||||
|
@Column(name = "spring_session_id")
|
||||||
|
private String springSession;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the id.
|
||||||
|
*
|
||||||
|
* @return the id
|
||||||
|
*/
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the id.
|
||||||
|
*
|
||||||
|
* @param id the new id
|
||||||
|
*/
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the client id.
|
||||||
|
*
|
||||||
|
* @return the client id
|
||||||
|
*/
|
||||||
|
public Long getClientId() {
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the client id.
|
||||||
|
*
|
||||||
|
* @param clientId the new client id
|
||||||
|
*/
|
||||||
|
public void setClientId(Long clientId) {
|
||||||
|
this.client = clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the subject.
|
||||||
|
*
|
||||||
|
* @return the subject
|
||||||
|
*/
|
||||||
|
public Long getSubject() {
|
||||||
|
return subject;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the subject.
|
||||||
|
*
|
||||||
|
* @param subject the new subject
|
||||||
|
*/
|
||||||
|
public void setSubject(Long subject) {
|
||||||
|
this.subject = subject;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the sid.
|
||||||
|
*
|
||||||
|
* @return the sid
|
||||||
|
*/
|
||||||
|
public String getSid() {
|
||||||
|
return sid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the sid.
|
||||||
|
*
|
||||||
|
* @param sid the new sid
|
||||||
|
*/
|
||||||
|
public void setSid(String sid) {
|
||||||
|
this.sid = sid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the id token.
|
||||||
|
*
|
||||||
|
* @return the id token
|
||||||
|
*/
|
||||||
|
public String getIdToken() {
|
||||||
|
return idToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the id token.
|
||||||
|
*
|
||||||
|
* @param idToken the new id token
|
||||||
|
*/
|
||||||
|
public void setIdToken(String idToken) {
|
||||||
|
this.idToken = idToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the spring session.
|
||||||
|
*
|
||||||
|
* @return the spring session
|
||||||
|
*/
|
||||||
|
public String getSpringSession() {
|
||||||
|
return springSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the spring session.
|
||||||
|
*
|
||||||
|
* @param springSession the new spring session
|
||||||
|
*/
|
||||||
|
public void setSpringSession(String springSession) {
|
||||||
|
this.springSession = springSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -3,6 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
package de.bstly.we.oidc.model;
|
package de.bstly.we.oidc.model;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
@ -29,6 +30,8 @@ public class OidcToken {
|
|||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
@Column(name = "id")
|
@Column(name = "id")
|
||||||
private Long id;
|
private Long id;
|
||||||
|
@Column(name = "created")
|
||||||
|
private Instant created;
|
||||||
@Column(name = "user_id")
|
@Column(name = "user_id")
|
||||||
private Long userId;
|
private Long userId;
|
||||||
@Column(name = "client_id")
|
@Column(name = "client_id")
|
||||||
@ -64,6 +67,24 @@ public class OidcToken {
|
|||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the created.
|
||||||
|
*
|
||||||
|
* @return the created
|
||||||
|
*/
|
||||||
|
public Instant getCreated() {
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the created.
|
||||||
|
*
|
||||||
|
* @param created the new created
|
||||||
|
*/
|
||||||
|
public void setCreated(Instant created) {
|
||||||
|
this.created = created;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the user id.
|
* Gets the user id.
|
||||||
*
|
*
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package de.bstly.we.oidc.repository;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import de.bstly.we.oidc.model.OidcAuthorization;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Interface OidcAuthorizationRepository.
|
||||||
|
*/
|
||||||
|
@Repository
|
||||||
|
public interface OidcAuthorizationRepository
|
||||||
|
extends JpaRepository<OidcAuthorization, Long>, QuerydslPredicateExecutor<OidcAuthorization> {
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package de.bstly.we.oidc.repository;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import de.bstly.we.oidc.model.OidcSession;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Interface OidcSessionRepository.
|
||||||
|
*/
|
||||||
|
@Repository
|
||||||
|
public interface OidcSessionRepository
|
||||||
|
extends JpaRepository<OidcSession, Long>, QuerydslPredicateExecutor<OidcSession> {
|
||||||
|
}
|
@ -14,33 +14,20 @@ import de.bstly.we.partey.model.GameRoomPolicyTypes;
|
|||||||
*/
|
*/
|
||||||
public class MapDetailsData {
|
public class MapDetailsData {
|
||||||
|
|
||||||
private String roomSlug = "";
|
|
||||||
private String mapUrl = "";
|
private String mapUrl = "";
|
||||||
private GameRoomPolicyTypes policy_type = GameRoomPolicyTypes.ANONYMOUS_POLICY;
|
private GameRoomPolicyTypes policy_type = GameRoomPolicyTypes.ANONYMOUS_POLICY;
|
||||||
private List<String> tags = Lists.newArrayList();
|
private List<String> tags = Lists.newArrayList();
|
||||||
private List<CharacterTexture> textures = Lists.newArrayList();
|
|
||||||
private String contactPage = "";
|
|
||||||
private boolean authenticationMandatory;
|
private boolean authenticationMandatory;
|
||||||
private String group = "";
|
private String roomSlug;
|
||||||
|
private String contactPage;
|
||||||
|
private String group;
|
||||||
private String iframeAuthentication;
|
private String iframeAuthentication;
|
||||||
|
private String miniLogo;
|
||||||
/**
|
private String loadingLogo;
|
||||||
* Gets the room slug.
|
private String loginSceneLogo;
|
||||||
*
|
private boolean showPoweredBy = false;
|
||||||
* @return the room slug
|
private String loadingCowebsiteLogo;
|
||||||
*/
|
private boolean canReport = true;
|
||||||
public String getRoomSlug() {
|
|
||||||
return roomSlug;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the room slug.
|
|
||||||
*
|
|
||||||
* @param roomSlug the new room slug
|
|
||||||
*/
|
|
||||||
public void setRoomSlug(String roomSlug) {
|
|
||||||
this.roomSlug = roomSlug;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the map url.
|
* Gets the map url.
|
||||||
@ -97,21 +84,39 @@ public class MapDetailsData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the textures.
|
* Checks if is authentication mandatory.
|
||||||
*
|
*
|
||||||
* @return the textures
|
* @return true, if is authentication mandatory
|
||||||
*/
|
*/
|
||||||
public List<CharacterTexture> getTextures() {
|
public boolean isAuthenticationMandatory() {
|
||||||
return textures;
|
return authenticationMandatory;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the textures.
|
* Sets the authentication mandatory.
|
||||||
*
|
*
|
||||||
* @param textures the new textures
|
* @param authenticationMandatory the new authentication mandatory
|
||||||
*/
|
*/
|
||||||
public void setTextures(List<CharacterTexture> textures) {
|
public void setAuthenticationMandatory(boolean authenticationMandatory) {
|
||||||
this.textures = textures;
|
this.authenticationMandatory = authenticationMandatory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the room slug.
|
||||||
|
*
|
||||||
|
* @return the room slug
|
||||||
|
*/
|
||||||
|
public String getRoomSlug() {
|
||||||
|
return roomSlug;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the room slug.
|
||||||
|
*
|
||||||
|
* @param roomSlug the new room slug
|
||||||
|
*/
|
||||||
|
public void setRoomSlug(String roomSlug) {
|
||||||
|
this.roomSlug = roomSlug;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -132,24 +137,6 @@ public class MapDetailsData {
|
|||||||
this.contactPage = contactPage;
|
this.contactPage = contactPage;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if is authentication mandatory.
|
|
||||||
*
|
|
||||||
* @return true, if is authentication mandatory
|
|
||||||
*/
|
|
||||||
public boolean isAuthenticationMandatory() {
|
|
||||||
return authenticationMandatory;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the authentication mandatory.
|
|
||||||
*
|
|
||||||
* @param authenticationMandatory the new authentication mandatory
|
|
||||||
*/
|
|
||||||
public void setAuthenticationMandatory(boolean authenticationMandatory) {
|
|
||||||
this.authenticationMandatory = authenticationMandatory;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the group.
|
* Gets the group.
|
||||||
*
|
*
|
||||||
@ -186,4 +173,112 @@ public class MapDetailsData {
|
|||||||
this.iframeAuthentication = iframeAuthentication;
|
this.iframeAuthentication = iframeAuthentication;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the mini logo.
|
||||||
|
*
|
||||||
|
* @return the mini logo
|
||||||
|
*/
|
||||||
|
public String getMiniLogo() {
|
||||||
|
return miniLogo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the mini logo.
|
||||||
|
*
|
||||||
|
* @param miniLogo the new mini logo
|
||||||
|
*/
|
||||||
|
public void setMiniLogo(String miniLogo) {
|
||||||
|
this.miniLogo = miniLogo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the loading logo.
|
||||||
|
*
|
||||||
|
* @return the loading logo
|
||||||
|
*/
|
||||||
|
public String getLoadingLogo() {
|
||||||
|
return loadingLogo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the loading logo.
|
||||||
|
*
|
||||||
|
* @param loadingLogo the new loading logo
|
||||||
|
*/
|
||||||
|
public void setLoadingLogo(String loadingLogo) {
|
||||||
|
this.loadingLogo = loadingLogo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the login scene logo.
|
||||||
|
*
|
||||||
|
* @return the login scene logo
|
||||||
|
*/
|
||||||
|
public String getLoginSceneLogo() {
|
||||||
|
return loginSceneLogo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the login scene logo.
|
||||||
|
*
|
||||||
|
* @param loginSceneLogo the new login scene logo
|
||||||
|
*/
|
||||||
|
public void setLoginSceneLogo(String loginSceneLogo) {
|
||||||
|
this.loginSceneLogo = loginSceneLogo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if is show powered by.
|
||||||
|
*
|
||||||
|
* @return true, if is show powered by
|
||||||
|
*/
|
||||||
|
public boolean isShowPoweredBy() {
|
||||||
|
return showPoweredBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the show powered by.
|
||||||
|
*
|
||||||
|
* @param showPoweredBy the new show powered by
|
||||||
|
*/
|
||||||
|
public void setShowPoweredBy(boolean showPoweredBy) {
|
||||||
|
this.showPoweredBy = showPoweredBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the loading cowebsite logo.
|
||||||
|
*
|
||||||
|
* @return the loading cowebsite logo
|
||||||
|
*/
|
||||||
|
public String getLoadingCowebsiteLogo() {
|
||||||
|
return loadingCowebsiteLogo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the loading cowebsite logo.
|
||||||
|
*
|
||||||
|
* @param loadingCowebsiteLogo the new loading cowebsite logo
|
||||||
|
*/
|
||||||
|
public void setLoadingCowebsiteLogo(String loadingCowebsiteLogo) {
|
||||||
|
this.loadingCowebsiteLogo = loadingCowebsiteLogo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if is can report.
|
||||||
|
*
|
||||||
|
* @return true, if is can report
|
||||||
|
*/
|
||||||
|
public boolean isCanReport() {
|
||||||
|
return canReport;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the can report.
|
||||||
|
*
|
||||||
|
* @param canReport the new can report
|
||||||
|
*/
|
||||||
|
public void setCanReport(boolean canReport) {
|
||||||
|
this.canReport = canReport;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -189,6 +189,27 @@ public class ParteyUserTag implements UserData {
|
|||||||
this.tag = tag;
|
this.tag = tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @see java.lang.Object#equals(java.lang.Object)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof ParteyUserTagId) {
|
||||||
|
ParteyUserTagId parteyUserTagId = (ParteyUserTagId) obj;
|
||||||
|
return this.target.equals(parteyUserTagId.getTarget()) && this.tag.equals(parteyUserTagId.getTag());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @see java.lang.Object#hashCode()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return this.target.hashCode() * this.tag.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
2
pom.xml
2
pom.xml
@ -13,7 +13,7 @@
|
|||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<java.version>11</java.version>
|
<java.version>11</java.version>
|
||||||
<log4j2.version>2.17.2</log4j2.version>
|
<log4j2.version>2.17.2</log4j2.version>
|
||||||
<revision>1.8.0-SNAPSHOT</revision>
|
<revision>1.9.1-SNAPSHOT</revision>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
|
Loading…
Reference in New Issue
Block a user