Browse Source

Add client configuration settings

Closes gh-117
Joe Grandja 5 years ago
parent
commit
c3b254579c

+ 50 - 0
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/RegisteredClient.java

@@ -18,6 +18,8 @@ package org.springframework.security.oauth2.server.authorization.client;
 import org.springframework.security.core.SpringSecurityCoreVersion2;
 import org.springframework.security.oauth2.core.AuthorizationGrantType;
 import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
+import org.springframework.security.oauth2.server.authorization.config.ClientSettings;
+import org.springframework.security.oauth2.server.authorization.config.TokenSettings;
 import org.springframework.util.Assert;
 import org.springframework.util.CollectionUtils;
 
@@ -46,6 +48,8 @@ public class RegisteredClient implements Serializable {
 	private Set<AuthorizationGrantType> authorizationGrantTypes;
 	private Set<String> redirectUris;
 	private Set<String> scopes;
+	private ClientSettings clientSettings;
+	private TokenSettings tokenSettings;
 
 	protected RegisteredClient() {
 	}
@@ -114,6 +118,24 @@ public class RegisteredClient implements Serializable {
 		return this.scopes;
 	}
 
+	/**
+	 * Returns the {@link ClientSettings client configuration settings}.
+	 *
+	 * @return the {@link ClientSettings}
+	 */
+	public ClientSettings getClientSettings() {
+		return this.clientSettings;
+	}
+
+	/**
+	 * Returns the {@link TokenSettings token configuration settings}.
+	 *
+	 * @return the {@link TokenSettings}
+	 */
+	public TokenSettings getTokenSettings() {
+		return this.tokenSettings;
+	}
+
 	@Override
 	public String toString() {
 		return "RegisteredClient{" +
@@ -160,6 +182,8 @@ public class RegisteredClient implements Serializable {
 		private Set<AuthorizationGrantType> authorizationGrantTypes = new LinkedHashSet<>();
 		private Set<String> redirectUris = new LinkedHashSet<>();
 		private Set<String> scopes = new LinkedHashSet<>();
+		private ClientSettings clientSettings;
+		private TokenSettings tokenSettings;
 
 		protected Builder(String id) {
 			this.id = id;
@@ -181,6 +205,8 @@ public class RegisteredClient implements Serializable {
 			if (!CollectionUtils.isEmpty(registeredClient.scopes)) {
 				this.scopes.addAll(registeredClient.scopes);
 			}
+			this.clientSettings = new ClientSettings(registeredClient.clientSettings.settings());
+			this.tokenSettings = new TokenSettings(registeredClient.tokenSettings.settings());
 		}
 
 		/**
@@ -310,6 +336,28 @@ public class RegisteredClient implements Serializable {
 			return this;
 		}
 
+		/**
+		 * Sets the {@link ClientSettings client configuration settings}.
+		 *
+		 * @param clientSettings the client configuration settings
+		 * @return the {@link Builder}
+		 */
+		public Builder clientSettings(ClientSettings clientSettings) {
+			this.clientSettings = clientSettings;
+			return this;
+		}
+
+		/**
+		 * Sets the {@link TokenSettings token configuration settings}.
+		 *
+		 * @param tokenSettings the token configuration settings
+		 * @return the {@link Builder}
+		 */
+		public Builder tokenSettings(TokenSettings tokenSettings) {
+			this.tokenSettings = tokenSettings;
+			return this;
+		}
+
 		/**
 		 * Builds a new {@link RegisteredClient}.
 		 *
@@ -341,6 +389,8 @@ public class RegisteredClient implements Serializable {
 			registeredClient.authorizationGrantTypes = Collections.unmodifiableSet(this.authorizationGrantTypes);
 			registeredClient.redirectUris = Collections.unmodifiableSet(this.redirectUris);
 			registeredClient.scopes = Collections.unmodifiableSet(this.scopes);
+			registeredClient.clientSettings = this.clientSettings != null ? this.clientSettings : new ClientSettings();
+			registeredClient.tokenSettings = this.tokenSettings != null ? this.tokenSettings : new TokenSettings();
 
 			return registeredClient;
 		}

+ 75 - 0
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/ClientSettings.java

@@ -0,0 +1,75 @@
+/*
+ * Copyright 2020 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.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.security.oauth2.server.authorization.config;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A facility for client configuration settings.
+ *
+ * @author Joe Grandja
+ * @since 0.0.2
+ * @see Settings
+ */
+public class ClientSettings extends Settings {
+	private static final String CLIENT_SETTING_BASE = "spring.security.oauth2.authorization-server.client.";
+	public static final String REQUIRE_PROOF_KEY = CLIENT_SETTING_BASE.concat("require-proof-key");
+
+	/**
+	 * Constructs a {@code ClientSettings}.
+	 */
+	public ClientSettings() {
+		this(defaultSettings());
+	}
+
+	/**
+	 * Constructs a {@code ClientSettings} using the provided parameters.
+	 *
+	 * @param settings the initial settings
+	 */
+	public ClientSettings(Map<String, Object> settings) {
+		super(settings);
+	}
+
+	/**
+	 * Returns {@code true} if the client is required to provide a proof key challenge and verifier
+	 * when performing the Authorization Code Grant flow. The default is {@code false}.
+	 *
+	 * @return {@code true} if the client is required to provide a proof key challenge and verifier, {@code false} otherwise
+	 */
+	public boolean requireProofKey() {
+		return setting(REQUIRE_PROOF_KEY);
+	}
+
+	/**
+	 * Set to {@code true} if the client is required to provide a proof key challenge and verifier
+	 * when performing the Authorization Code Grant flow.
+	 *
+	 * @param requireProofKey {@code true} if the client is required to provide a proof key challenge and verifier, {@code false} otherwise
+	 * @return the {@link ClientSettings}
+	 */
+	public ClientSettings requireProofKey(boolean requireProofKey) {
+		setting(REQUIRE_PROOF_KEY, requireProofKey);
+		return this;
+	}
+
+	protected static Map<String, Object> defaultSettings() {
+		Map<String, Object> settings = new HashMap<>();
+		settings.put(REQUIRE_PROOF_KEY, false);
+		return settings;
+	}
+}

+ 100 - 0
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/Settings.java

@@ -0,0 +1,100 @@
+/*
+ * Copyright 2020 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.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.security.oauth2.server.authorization.config;
+
+import org.springframework.security.core.SpringSecurityCoreVersion2;
+import org.springframework.util.Assert;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+
+/**
+ * A facility for configuration settings.
+ *
+ * @author Joe Grandja
+ * @since 0.0.2
+ */
+public class Settings implements Serializable {
+	private static final long serialVersionUID = SpringSecurityCoreVersion2.SERIAL_VERSION_UID;
+	private final Map<String, Object> settings;
+
+	/**
+	 * Constructs a {@code Settings}.
+	 */
+	public Settings() {
+		this.settings = new HashMap<>();
+	}
+
+	/**
+	 * Constructs a {@code Settings} using the provided parameters.
+	 *
+	 * @param settings the initial settings
+	 */
+	public Settings(Map<String, Object> settings) {
+		Assert.notNull(settings, "settings cannot be null");
+		this.settings = new HashMap<>(settings);
+	}
+
+	/**
+	 * Returns a configuration setting.
+	 *
+	 * @param name the name of the setting
+	 * @param <T> the type of the setting
+	 * @return the value of the setting, or {@code null} if not available
+	 */
+	@SuppressWarnings("unchecked")
+	public <T> T setting(String name) {
+		Assert.hasText(name, "name cannot be empty");
+		return (T) this.settings.get(name);
+	}
+
+	/**
+	 * Sets a configuration setting.
+	 *
+	 * @param name the name of the setting
+	 * @param value the value of the setting
+	 * @return the {@link Settings}
+	 */
+	public Settings setting(String name, Object value) {
+		Assert.hasText(name, "name cannot be empty");
+		Assert.notNull(value, "value cannot be null");
+		this.settings.put(name, value);
+		return this;
+	}
+
+	/**
+	 * Returns a {@code Map} of the configuration settings.
+	 *
+	 * @return a {@code Map} of the configuration settings
+	 */
+	public Map<String, Object> settings() {
+		return this.settings;
+	}
+
+	/**
+	 * A {@code Consumer} of the configuration settings {@code Map}
+	 * allowing the ability to add, replace, or remove.
+	 *
+	 * @param settingsConsumer a {@link Consumer} of the configuration settings {@code Map}
+	 * @return the {@link Settings}
+	 */
+	public Settings settings(Consumer<Map<String, Object>> settingsConsumer) {
+		settingsConsumer.accept(this.settings);
+		return this;
+	}
+}

+ 74 - 0
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/TokenSettings.java

@@ -0,0 +1,74 @@
+/*
+ * Copyright 2020 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.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.security.oauth2.server.authorization.config;
+
+import java.time.Duration;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A facility for token configuration settings.
+ *
+ * @author Joe Grandja
+ * @since 0.0.2
+ * @see Settings
+ */
+public class TokenSettings extends Settings {
+	private static final String TOKEN_SETTING_BASE = "spring.security.oauth2.authorization-server.token.";
+	public static final String ACCESS_TOKEN_TIME_TO_LIVE = TOKEN_SETTING_BASE.concat("access-token-time-to-live");
+
+	/**
+	 * Constructs a {@code TokenSettings}.
+	 */
+	public TokenSettings() {
+		this(defaultSettings());
+	}
+
+	/**
+	 * Constructs a {@code TokenSettings} using the provided parameters.
+	 *
+	 * @param settings the initial settings
+	 */
+	public TokenSettings(Map<String, Object> settings) {
+		super(settings);
+	}
+
+	/**
+	 * Returns the time-to-live for an access token. The default is 5 minutes.
+	 *
+	 * @return the time-to-live for an access token
+	 */
+	public Duration accessTokenTimeToLive() {
+		return setting(ACCESS_TOKEN_TIME_TO_LIVE);
+	}
+
+	/**
+	 * Set the time-to-live for an access token.
+	 *
+	 * @param accessTokenTimeToLive the time-to-live for an access token
+	 * @return the {@link TokenSettings}
+	 */
+	public TokenSettings accessTokenTimeToLive(Duration accessTokenTimeToLive) {
+		setting(ACCESS_TOKEN_TIME_TO_LIVE, accessTokenTimeToLive);
+		return this;
+	}
+
+	protected static Map<String, Object> defaultSettings() {
+		Map<String, Object> settings = new HashMap<>();
+		settings.put(ACCESS_TOKEN_TIME_TO_LIVE, Duration.ofMinutes(5));
+		return settings;
+	}
+}

+ 7 - 14
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/RegisteredClientTests.java

@@ -322,6 +322,9 @@ public class RegisteredClientTests {
 		RegisteredClient registration = TestRegisteredClients.registeredClient().build();
 		RegisteredClient updated = RegisteredClient.withRegisteredClient(registration).build();
 
+		assertThat(registration.getId()).isEqualTo(updated.getId());
+		assertThat(registration.getClientId()).isEqualTo(updated.getClientId());
+		assertThat(registration.getClientSecret()).isEqualTo(updated.getClientSecret());
 		assertThat(registration.getClientAuthenticationMethods()).isEqualTo(updated.getClientAuthenticationMethods());
 		assertThat(registration.getClientAuthenticationMethods()).isNotSameAs(updated.getClientAuthenticationMethods());
 		assertThat(registration.getAuthorizationGrantTypes()).isEqualTo(updated.getAuthorizationGrantTypes());
@@ -330,20 +333,10 @@ public class RegisteredClientTests {
 		assertThat(registration.getRedirectUris()).isNotSameAs(updated.getRedirectUris());
 		assertThat(registration.getScopes()).isEqualTo(updated.getScopes());
 		assertThat(registration.getScopes()).isNotSameAs(updated.getScopes());
-	}
-
-	@Test
-	public void buildWhenRegisteredClientProvidedThenEachPropertyMatches() {
-		RegisteredClient registration = TestRegisteredClients.registeredClient().build();
-		RegisteredClient updated = RegisteredClient.withRegisteredClient(registration).build();
-
-		assertThat(registration.getId()).isEqualTo(updated.getId());
-		assertThat(registration.getClientId()).isEqualTo(updated.getClientId());
-		assertThat(registration.getClientSecret()).isEqualTo(updated.getClientSecret());
-		assertThat(registration.getClientAuthenticationMethods()).isEqualTo(updated.getClientAuthenticationMethods());
-		assertThat(registration.getAuthorizationGrantTypes()).isEqualTo(updated.getAuthorizationGrantTypes());
-		assertThat(registration.getRedirectUris()).isEqualTo(updated.getRedirectUris());
-		assertThat(registration.getScopes()).isEqualTo(updated.getScopes());
+		assertThat(registration.getClientSettings().settings()).isEqualTo(updated.getClientSettings().settings());
+		assertThat(registration.getClientSettings()).isNotSameAs(updated.getClientSettings());
+		assertThat(registration.getTokenSettings().settings()).isEqualTo(updated.getTokenSettings().settings());
+		assertThat(registration.getTokenSettings()).isNotSameAs(updated.getTokenSettings());
 	}
 
 	@Test

+ 49 - 0
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/ClientSettingsTests.java

@@ -0,0 +1,49 @@
+/*
+ * Copyright 2020 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.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.security.oauth2.server.authorization.config;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+/**
+ * Tests for {@link ClientSettings}.
+ *
+ * @author Joe Grandja
+ */
+public class ClientSettingsTests {
+
+	@Test
+	public void constructorWhenDefaultThenDefaultsAreSet() {
+		ClientSettings clientSettings = new ClientSettings();
+		assertThat(clientSettings.settings()).hasSize(1);
+		assertThat(clientSettings.requireProofKey()).isFalse();
+	}
+
+	@Test
+	public void constructorWhenNullThenThrowIllegalArgumentException() {
+		assertThatThrownBy(() -> new ClientSettings(null))
+				.isInstanceOf(IllegalArgumentException.class)
+				.hasMessage("settings cannot be null");
+	}
+
+	@Test
+	public void requireProofKeyWhenTrueThenSet() {
+		ClientSettings clientSettings = new ClientSettings().requireProofKey(true);
+		assertThat(clientSettings.requireProofKey()).isTrue();
+	}
+}

+ 78 - 0
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/SettingsTests.java

@@ -0,0 +1,78 @@
+/*
+ * Copyright 2020 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.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.security.oauth2.server.authorization.config;
+
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.assertj.core.api.Assertions.entry;
+
+/**
+ * Tests for {@link Settings}.
+ *
+ * @author Joe Grandja
+ */
+public class SettingsTests {
+
+	@Test
+	public void constructorWhenNullThenThrowIllegalArgumentException() {
+		assertThatThrownBy(() -> new Settings(null))
+				.isInstanceOf(IllegalArgumentException.class)
+				.hasMessage("settings cannot be null");
+	}
+
+	@Test
+	public void constructorWhenSettingsProvidedThenSettingsAreSet() {
+		Map<String, Object> initialSettings = new HashMap<>();
+		initialSettings.put("setting1", "value1");
+		initialSettings.put("setting2", "value2");
+
+		Settings settings = new Settings(initialSettings)
+				.setting("setting3", "value3")
+				.settings(s -> s.put("setting4", "value4"));
+
+		assertThat(settings.settings()).contains(
+				entry("setting1", "value1"),
+				entry("setting2", "value2"),
+				entry("setting3", "value3"),
+				entry("setting4", "value4"));
+	}
+
+	@Test
+	public void getSettingWhenNameNullThenThrowIllegalArgumentException() {
+		assertThatThrownBy(() -> new Settings().setting(null))
+				.isInstanceOf(IllegalArgumentException.class)
+				.hasMessage("name cannot be empty");
+	}
+
+	@Test
+	public void setSettingWhenNameNullThenThrowIllegalArgumentException() {
+		assertThatThrownBy(() -> new Settings().setting(null, "value"))
+				.isInstanceOf(IllegalArgumentException.class)
+				.hasMessage("name cannot be empty");
+	}
+
+	@Test
+	public void setSettingWhenValueNullThenThrowIllegalArgumentException() {
+		assertThatThrownBy(() -> new Settings().setting("setting", null))
+				.isInstanceOf(IllegalArgumentException.class)
+				.hasMessage("value cannot be null");
+	}
+}

+ 52 - 0
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/TokenSettingsTests.java

@@ -0,0 +1,52 @@
+/*
+ * Copyright 2020 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.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.security.oauth2.server.authorization.config;
+
+import org.junit.Test;
+
+import java.time.Duration;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+/**
+ * Tests for {@link TokenSettings}.
+ *
+ * @author Joe Grandja
+ */
+public class TokenSettingsTests {
+
+	@Test
+	public void constructorWhenDefaultThenDefaultsAreSet() {
+		TokenSettings tokenSettings = new TokenSettings();
+		assertThat(tokenSettings.settings()).hasSize(1);
+		assertThat(tokenSettings.accessTokenTimeToLive()).isEqualTo(Duration.ofMinutes(5));
+	}
+
+	@Test
+	public void constructorWhenNullThenThrowIllegalArgumentException() {
+		assertThatThrownBy(() -> new TokenSettings(null))
+				.isInstanceOf(IllegalArgumentException.class)
+				.hasMessage("settings cannot be null");
+	}
+
+	@Test
+	public void accessTokenTimeToLiveWhenProvidedThenSet() {
+		Duration accessTokenTimeToLive = Duration.ofMinutes(10);
+		TokenSettings tokenSettings = new TokenSettings().accessTokenTimeToLive(accessTokenTimeToLive);
+		assertThat(tokenSettings.accessTokenTimeToLive()).isEqualTo(accessTokenTimeToLive);
+	}
+}