Repository: SAP/cloud-security-xsuaa-integration (java)

Issue:
Potential Memory Leak in JwtValidatorBuilder
Hi,
We noticed that using the `JwtValidatorBuilder` in a non-efficient way leads to a memory leak. We were using it like this:

```java
JwtValidatorBuilder.getInstance(getXsuaaConfiguration()).build();
```

Where `getXsuaaConfiguration()` would load the XSUAA configuration which meant creating a new instance for it.

We were not aware that the `JwtValidatorBuilder.getInstance` method stores all builder instances in a map where the configuration object serves as key. In our case this is a problem because the configuration object is always a different instance and there is no equals implemented on it. That means when `getInstance` [performs the lookup](https://github.com/SAP/cloud-security-xsuaa-integration/blob/8164c7ca041f57b40713a681ec19d231539f2031/java-security/src/main/java/com/sap/cloud/security/token/validation/validators/JwtValidatorBuilder.java#L50) to see if there is already a builder registered it will only find one if the XSUAA configuration object is exactly the same instance as the one in the map.

To us it wasn't obvious that the instances would be stored. It may be worthwhile to make this part of the code more resilient against such usage. E.g. implement `equals` on the configuration or limit the map in size.

We now avoid the problem by making sure we use the same instances of configuration objects in all parts of our code. So I just wanted to let you know because this might be a problem others run into as well.

Pull request:
Implement hashCode and equals for OAuth2ServiceConfigurationImpl
This PR will fix #307.

Patch:
diff --git a/java-security/src/main/java/com/sap/cloud/security/config/OAuth2ServiceConfigurationBuilder.java b/java-security/src/main/java/com/sap/cloud/security/config/OAuth2ServiceConfigurationBuilder.java
index a3a920c4..45d0f1b5 100644
--- a/java-security/src/main/java/com/sap/cloud/security/config/OAuth2ServiceConfigurationBuilder.java
+++ b/java-security/src/main/java/com/sap/cloud/security/config/OAuth2ServiceConfigurationBuilder.java
@@ -6,6 +6,7 @@ import javax.annotation.Nonnull;
 import java.net.URI;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
 
 import static com.sap.cloud.security.config.cf.CFConstants.*;
 
@@ -98,44 +99,72 @@ public class OAuth2ServiceConfigurationBuilder {
 	 * @return the oauth2 service configuration.
 	 */
 	public OAuth2ServiceConfiguration build() {
-		return new OAuth2ServiceConfiguration() {
-
-			@Override
-			public String getClientId() {
-				return properties.get(CLIENT_ID);
-			}
-
-			@Override
-			public String getClientSecret() {
-				return properties.get(CLIENT_SECRET);
-			}
-
-			@Override
-			public URI getUrl() {
-				return URI.create(properties.get(URL));
-			}
-
-			@Override
-			public String getProperty(String name) {
-				return properties.get(name);
-			}
-
-			@Override
-			public boolean hasProperty(String name) {
-				return properties.containsKey(name);
-			}
-
-			@Override
-			public Service getService() {
-				return service;
-			}
-
-			@Override
-			public boolean isLegacyMode() {
-				return runInLegacyMode;
-			}
-		};
-
+		return new OAuth2ServiceConfigurationImpl(properties, service, runInLegacyMode);
 	}
 
+	private class OAuth2ServiceConfigurationImpl implements OAuth2ServiceConfiguration {
+
+		private final Map<String, String> properties;
+		private final boolean runInLegacyMode;
+		private final Service service;
+
+		private OAuth2ServiceConfigurationImpl(@Nonnull Map<String, String> properties,
+				@Nonnull Service service, boolean runInLegacyMode) {
+			this.properties = properties;
+			this.service = service;
+			this.runInLegacyMode = runInLegacyMode;
+		}
+
+		@Override
+		public String getClientId() {
+			return properties.get(CLIENT_ID);
+		}
+
+		@Override
+		public String getClientSecret() {
+			return properties.get(CLIENT_SECRET);
+		}
+
+		@Override
+		public URI getUrl() {
+			return URI.create(properties.get(URL));
+		}
+
+		@Override
+		public String getProperty(String name) {
+			return properties.get(name);
+		}
+
+		@Override
+		public boolean hasProperty(String name) {
+			return properties.containsKey(name);
+		}
+
+		@Override
+		public Service getService() {
+			return service;
+		}
+
+		@Override
+		public boolean isLegacyMode() {
+			return runInLegacyMode;
+		}
+
+		@Override
+		public boolean equals(Object o) {
+			if (this == o)
+				return true;
+			if (o == null || getClass() != o.getClass())
+				return false;
+			OAuth2ServiceConfigurationImpl that = (OAuth2ServiceConfigurationImpl) o;
+			return runInLegacyMode == that.runInLegacyMode &&
+					properties.equals(that.properties) &&
+					service == that.service;
+		}
+
+		@Override
+		public int hashCode() {
+			return Objects.hash(properties, runInLegacyMode, service);
+		}
+	}
 }
\ No newline at end of file


Test patch:
diff --git a/java-security/src/test/java/com/sap/cloud/security/token/validation/validators/JwtValidatorBuilderTest.java b/java-security/src/test/java/com/sap/cloud/security/token/validation/validators/JwtValidatorBuilderTest.java
index d63bd67e..75a99cda 100644
--- a/java-security/src/test/java/com/sap/cloud/security/token/validation/validators/JwtValidatorBuilderTest.java
+++ b/java-security/src/test/java/com/sap/cloud/security/token/validation/validators/JwtValidatorBuilderTest.java
@@ -36,7 +36,9 @@ public class JwtValidatorBuilderTest {
 		OAuth2ServiceConfiguration configuration = xsuaaConfigBuilder.build();
 		JwtValidatorBuilder builder_1 = JwtValidatorBuilder.getInstance(configuration);
 		JwtValidatorBuilder builder_2 = JwtValidatorBuilder.getInstance(configuration);
+		JwtValidatorBuilder builder_3 = JwtValidatorBuilder.getInstance(xsuaaConfigBuilder.build());
 		assertThat(builder_1).isSameAs(builder_2);
+		assertThat(builder_1).isSameAs(builder_3);
 	}
 
 	@Test