Bladeren bron

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
Jyri-Matti Lähteenmäki 3 jaren geleden
bovenliggende
commit
ca0a6d9777

+ 37 - 3
oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/user/OAuth2UserAuthority.java

@@ -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;
+	}
+
 }

+ 37 - 1
oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/user/OAuth2UserAuthorityTests.java

@@ -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());
+	}
+
 }