Treat URLs as String before equals/hashcode

java.net.URL performs DNS lookups whenever its equals/hashCode is
used. Thus attribute values of type java.net.URL need to be converted
to something else before they are used for equals/hashCode.

Closes gh-10673
This commit is contained in:
Jyri-Matti Lähteenmäki 2022-03-25 20:13:25 +02:00 committed by Joe Grandja
parent e28fcbfbbe
commit ca0a6d9777
2 changed files with 74 additions and 4 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,9 +16,11 @@
package org.springframework.security.oauth2.core.user;
import java.net.URL;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;
@ -85,13 +87,37 @@ public class OAuth2UserAuthority implements GrantedAuthority {
if (!this.getAuthority().equals(that.getAuthority())) {
return false;
}
return this.getAttributes().equals(that.getAttributes());
Map<String, Object> thatAttributes = that.getAttributes();
if (getAttributes().size() != thatAttributes.size()) {
return false;
}
for (Map.Entry<String, Object> e : getAttributes().entrySet()) {
String key = e.getKey();
Object value = convertURLIfNecessary(e.getValue());
if (value == null) {
if (!(thatAttributes.get(key) == null && thatAttributes.containsKey(key))) {
return false;
}
}
else {
Object thatValue = convertURLIfNecessary(thatAttributes.get(key));
if (!value.equals(thatValue)) {
return false;
}
}
}
return true;
}
@Override
public int hashCode() {
int result = this.getAuthority().hashCode();
result = 31 * result + this.getAttributes().hashCode();
result = 31 * result;
for (Map.Entry<String, Object> e : getAttributes().entrySet()) {
Object key = e.getKey();
Object value = convertURLIfNecessary(e.getValue());
result += Objects.hashCode(key) ^ Objects.hashCode(value);
}
return result;
}
@ -100,4 +126,12 @@ public class OAuth2UserAuthority implements GrantedAuthority {
return this.getAuthority();
}
/**
* @return {@code URL} converted to a string since {@code URL} shouldn't be used for
* equality/hashCode. For other instances the value is returned as is.
*/
private static Object convertURLIfNecessary(Object value) {
return (value instanceof URL) ? ((URL) value).toExternalForm() : value;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,6 +16,8 @@
package org.springframework.security.oauth2.core.user;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.Map;
@ -35,6 +37,22 @@ public class OAuth2UserAuthorityTests {
private static final Map<String, Object> ATTRIBUTES = Collections.singletonMap("username", "test");
private static final OAuth2UserAuthority AUTHORITY_WITH_OBJECTURL;
private static final OAuth2UserAuthority AUTHORITY_WITH_STRINGURL;
static {
try {
AUTHORITY_WITH_OBJECTURL = new OAuth2UserAuthority(
Collections.singletonMap("someurl", new URL("https://localhost")));
AUTHORITY_WITH_STRINGURL = new OAuth2UserAuthority(
Collections.singletonMap("someurl", "https://localhost"));
}
catch (MalformedURLException ex) {
throw new RuntimeException(ex);
}
}
@Test
public void constructorWhenAuthorityIsNullThenThrowIllegalArgumentException() {
assertThatIllegalArgumentException().isThrownBy(() -> new OAuth2UserAuthority(null, ATTRIBUTES));
@ -58,4 +76,22 @@ public class OAuth2UserAuthorityTests {
assertThat(userAuthority.getAttributes()).isEqualTo(ATTRIBUTES);
}
@Test
public void equalsRegardlessOfUrlType() {
assertThat(AUTHORITY_WITH_OBJECTURL).isEqualTo(AUTHORITY_WITH_OBJECTURL);
assertThat(AUTHORITY_WITH_STRINGURL).isEqualTo(AUTHORITY_WITH_STRINGURL);
assertThat(AUTHORITY_WITH_OBJECTURL).isEqualTo(AUTHORITY_WITH_STRINGURL);
assertThat(AUTHORITY_WITH_STRINGURL).isEqualTo(AUTHORITY_WITH_OBJECTURL);
}
@Test
public void hashCodeIsSameRegardlessOfUrlType() {
assertThat(AUTHORITY_WITH_OBJECTURL.hashCode()).isEqualTo(AUTHORITY_WITH_OBJECTURL.hashCode());
assertThat(AUTHORITY_WITH_STRINGURL.hashCode()).isEqualTo(AUTHORITY_WITH_STRINGURL.hashCode());
assertThat(AUTHORITY_WITH_OBJECTURL.hashCode()).isEqualTo(AUTHORITY_WITH_STRINGURL.hashCode());
assertThat(AUTHORITY_WITH_STRINGURL.hashCode()).isEqualTo(AUTHORITY_WITH_OBJECTURL.hashCode());
}
}