Update matching for native apps

- redirectURI matching for Native apps accoridng to
  https://datatracker.ietf.org/doc/html/rfc8252#section-7.3
pull/1580/head
Dominik Frantisek Bucik 2021-06-03 10:59:37 +02:00
parent eed08e0da8
commit d9d70c8177
No known key found for this signature in database
GPG Key ID: 25014C8DB2E7E62D
4 changed files with 44 additions and 23 deletions

View File

@ -20,10 +20,14 @@
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<!-- Appenders -->
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out" />
<appender name="FILE" class="org.apache.log4j.FileAppender">
<param name="file" value="/var/log/oidc/elixir/log.out"/>
<param name="immediateFlush" value="true"/>
<param name="threshold" value="debug"/>
<param name="append" value="true"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p: %c - %m%n" />
<param name="conversionPattern" value="%m%n"/>
</layout>
</appender>
@ -52,7 +56,7 @@
<logger name="org.mitre.data">
<level value="info" />
</logger>
<!-- 3rdparty Loggers -->
<logger name="org.springframework.core">
<level value="info" />
@ -77,7 +81,7 @@
<!-- Root Logger -->
<root>
<priority value="warn" />
<appender-ref ref="console" />
<appender-ref ref="file" />
</root>
</log4j:configuration>

View File

@ -41,7 +41,7 @@
error-page="/error">
<oauth:authorization-code authorization-code-services-ref="defaultOAuth2AuthorizationCodeService"/>
<oauth:implicit />
<oauth:implicit/>
<oauth:refresh-token/>
<oauth:client-credentials/>
<oauth:custom-grant token-granter-ref="chainedTokenGranter" />
@ -57,4 +57,4 @@
<!-- Error page handler. -->
<mvc:view-controller path="/error" view-name="error" />
</beans>
</beans>

View File

@ -18,19 +18,20 @@
*/
package org.mitre.oauth2.service.impl;
import org.mitre.oauth2.model.ClientDetailsEntity;
import org.mitre.openid.connect.config.ConfigurationPropertiesBean;
import org.mitre.openid.connect.service.BlacklistedSiteService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.common.exceptions.InvalidRequestException;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.common.exceptions.RedirectMismatchException;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.endpoint.DefaultRedirectResolver;
import org.springframework.security.oauth2.provider.endpoint.RedirectResolver;
import org.springframework.stereotype.Component;
import com.google.common.base.Strings;
import org.springframework.util.Assert;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
@ -40,10 +41,11 @@ import org.springframework.web.util.UriComponentsBuilder;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import static org.mitre.oauth2.model.ClientDetailsEntity.AppType.NATIVE;
/**
*
* A redirect resolver that knows how to check against the blacklisted URIs
@ -55,6 +57,8 @@ import java.util.Set;
@Component("blacklistAwareRedirectResolver")
public class BlacklistAwareRedirectResolver implements RedirectResolver {
private static final Logger log = LoggerFactory.getLogger(BlacklistAwareRedirectResolver.class);
@Autowired
private BlacklistedSiteService blacklistService;
@ -119,6 +123,7 @@ public class BlacklistAwareRedirectResolver implements RedirectResolver {
*/
@Override
public String resolveRedirect(String requestedRedirect, ClientDetails client) throws OAuth2Exception {
log.error("!!!!!!!!!!!!!!!!!!!!!!!Resolving redirect!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
Set<String> authorizedGrantTypes = client.getAuthorizedGrantTypes();
if (authorizedGrantTypes.isEmpty()) {
throw new InvalidGrantException("A client must have at least one authorized grant type.");
@ -133,7 +138,9 @@ public class BlacklistAwareRedirectResolver implements RedirectResolver {
throw new InvalidRequestException("At least one redirect_uri must be registered with the client.");
}
String redirect = obtainMatchingRedirect(registeredRedirectUris, requestedRedirect);
ClientDetailsEntity cde = (ClientDetailsEntity) client;
String redirect = obtainMatchingRedirect(registeredRedirectUris, requestedRedirect, cde.getApplicationType());
if (blacklistService.isBlacklisted(redirect)) {
// don't let it go through
@ -154,16 +161,22 @@ public class BlacklistAwareRedirectResolver implements RedirectResolver {
*
* @param requestedRedirect The requested redirect URI.
* @param redirectUri The registered redirect URI.
* @param applicationType
* @return Whether the requested redirect URI "matches" the specified redirect URI.
*/
protected boolean redirectMatches(String requestedRedirect, String redirectUri) {
protected boolean redirectMatches(String requestedRedirect, String redirectUri,
ClientDetailsEntity.AppType applicationType)
{
UriComponents requestedRedirectUri = UriComponentsBuilder.fromUriString(requestedRedirect).build();
UriComponents registeredRedirectUri = UriComponentsBuilder.fromUriString(redirectUri).build();
boolean schemeMatch = isEqual(registeredRedirectUri.getScheme(), requestedRedirectUri.getScheme());
boolean userInfoMatch = isEqual(registeredRedirectUri.getUserInfo(), requestedRedirectUri.getUserInfo());
boolean hostMatch = hostMatches(registeredRedirectUri.getHost(), requestedRedirectUri.getHost());
boolean portMatch = !matchPorts || registeredRedirectUri.getPort() == requestedRedirectUri.getPort();
boolean portMatch = true;
if (!NATIVE.equals(applicationType)) {
portMatch = !matchPorts || registeredRedirectUri.getPort() == requestedRedirectUri.getPort();
}
boolean pathMatch = true;
boolean queryParamMatch = true;
if (strictMatch) {
@ -194,10 +207,13 @@ public class BlacklistAwareRedirectResolver implements RedirectResolver {
*
* @param redirectUris the set of the registered URIs to try and find a match. This cannot be null or empty.
* @param requestedRedirect the URI used as part of the request
* @param applicationType
* @return redirect uri
* @throws RedirectMismatchException if no match was found
*/
private String obtainMatchingRedirect(Set<String> redirectUris, String requestedRedirect) {
private String obtainMatchingRedirect(Set<String> redirectUris, String requestedRedirect,
ClientDetailsEntity.AppType applicationType)
{
Assert.notEmpty(redirectUris, "Redirect URIs cannot be empty");
if (redirectUris.size() == 1 && requestedRedirect == null) {
@ -205,7 +221,7 @@ public class BlacklistAwareRedirectResolver implements RedirectResolver {
}
for (String redirectUri : redirectUris) {
if (requestedRedirect != null && redirectMatches(requestedRedirect, redirectUri)) {
if (requestedRedirect != null && redirectMatches(requestedRedirect, redirectUri, applicationType)) {
// Initialize with the registered redirect-uri
UriComponentsBuilder redirectUriBuilder = UriComponentsBuilder.fromUriString(redirectUri);
UriComponents requestedRedirectUri = UriComponentsBuilder.fromUriString(requestedRedirect).build();
@ -213,7 +229,7 @@ public class BlacklistAwareRedirectResolver implements RedirectResolver {
if (this.matchSubdomains) {
redirectUriBuilder.host(requestedRedirectUri.getHost());
}
if (!this.matchPorts) {
if (!this.matchPorts || NATIVE.equals(applicationType)) {
redirectUriBuilder.port(requestedRedirectUri.getPort());
}
if (!this.strictMatch) {

View File

@ -21,6 +21,7 @@ import static org.mockito.Matchers.anyString;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mitre.oauth2.model.ClientDetailsEntity;
import org.mitre.openid.connect.config.ConfigurationPropertiesBean;
import org.mitre.openid.connect.service.BlacklistedSiteService;
import org.mockito.InjectMocks;
@ -49,7 +50,7 @@ public class TestBlacklistAwareRedirectResolver {
private BlacklistedSiteService blacklistService;
@Mock
private ClientDetails client;
private ClientDetailsEntity client;
@Mock
private ConfigurationPropertiesBean config;
@ -108,12 +109,12 @@ public class TestBlacklistAwareRedirectResolver {
public void testRedirectMatches_default() {
// this is not an exact match
boolean res1 = resolver.redirectMatches(pathUri, goodUri);
boolean res1 = resolver.redirectMatches(pathUri, goodUri, ClientDetailsEntity.AppType.WEB);
assertThat(res1, is(false));
// this is an exact match
boolean res2 = resolver.redirectMatches(goodUri, goodUri);
boolean res2 = resolver.redirectMatches(goodUri, goodUri, ClientDetailsEntity.AppType.WEB);
assertThat(res2, is(true));
@ -126,12 +127,12 @@ public class TestBlacklistAwareRedirectResolver {
resolver.setStrictMatch(false);
// this is not an exact match (but that's OK)
boolean res1 = resolver.redirectMatches(pathUri, goodUri);
boolean res1 = resolver.redirectMatches(pathUri, goodUri, ClientDetailsEntity.AppType.WEB);
assertThat(res1, is(true));
// this is an exact match
boolean res2 = resolver.redirectMatches(goodUri, goodUri);
boolean res2 = resolver.redirectMatches(goodUri, goodUri, ClientDetailsEntity.AppType.WEB);
assertThat(res2, is(true));
@ -140,12 +141,12 @@ public class TestBlacklistAwareRedirectResolver {
@Test
public void testHeartMode() {
// this is not an exact match
boolean res1 = resolver.redirectMatches(pathUri, goodUri);
boolean res1 = resolver.redirectMatches(pathUri, goodUri, ClientDetailsEntity.AppType.WEB);
assertThat(res1, is(false));
// this is an exact match
boolean res2 = resolver.redirectMatches(goodUri, goodUri);
boolean res2 = resolver.redirectMatches(goodUri, goodUri, ClientDetailsEntity.AppType.WEB);
assertThat(res2, is(true));
}