Add implementation for in memory RegisteredClientRepository
Fixes gh-40
This commit is contained in:
parent
f13a4a0a48
commit
8700ff19df
@ -18,3 +18,7 @@ dependencies {
|
|||||||
|
|
||||||
provided 'javax.servlet:javax.servlet-api'
|
provided 'javax.servlet:javax.servlet-api'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jacoco {
|
||||||
|
toolVersion = '0.8.5'
|
||||||
|
}
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal class used for serialization across Spring Security Authorization Server classes.
|
||||||
|
*
|
||||||
|
* @author Anoop Garlapati
|
||||||
|
*/
|
||||||
|
public class Version {
|
||||||
|
/**
|
||||||
|
* Global Serialization value for Spring Security Authorization Server classes.
|
||||||
|
*/
|
||||||
|
public static final long SERIAL_VERSION_UID = "0.0.1".hashCode();
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* 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.client;
|
||||||
|
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link RegisteredClientRepository} that stores {@link RegisteredClient}(s) in-memory.
|
||||||
|
*
|
||||||
|
* @author Anoop Garlapati
|
||||||
|
* @see RegisteredClientRepository
|
||||||
|
* @see RegisteredClient
|
||||||
|
*/
|
||||||
|
public final class InMemoryRegisteredClientRepository implements RegisteredClientRepository {
|
||||||
|
private final Map<String, RegisteredClient> idRegistrationMap;
|
||||||
|
private final Map<String, RegisteredClient> clientIdRegistrationMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an {@code InMemoryRegisteredClientRepository} using the provided parameters.
|
||||||
|
*
|
||||||
|
* @param registrations the client registration(s)
|
||||||
|
*/
|
||||||
|
public InMemoryRegisteredClientRepository(RegisteredClient... registrations) {
|
||||||
|
this(Arrays.asList(registrations));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an {@code InMemoryRegisteredClientRepository} using the provided parameters.
|
||||||
|
*
|
||||||
|
* @param registrations the client registration(s)
|
||||||
|
*/
|
||||||
|
public InMemoryRegisteredClientRepository(List<RegisteredClient> registrations) {
|
||||||
|
Assert.notEmpty(registrations, "registrations cannot be empty");
|
||||||
|
ConcurrentHashMap<String, RegisteredClient> idRegistrationMapResult = new ConcurrentHashMap<>();
|
||||||
|
ConcurrentHashMap<String, RegisteredClient> clientIdRegistrationMapResult = new ConcurrentHashMap<>();
|
||||||
|
for (RegisteredClient registration : registrations) {
|
||||||
|
Assert.notNull(registration, "registration cannot be null");
|
||||||
|
String id = registration.getId();
|
||||||
|
if (idRegistrationMapResult.containsKey(id)) {
|
||||||
|
throw new IllegalArgumentException("Registered client must be unique. " +
|
||||||
|
"Found duplicate identifier: " + id);
|
||||||
|
}
|
||||||
|
String clientId = registration.getClientId();
|
||||||
|
if (clientIdRegistrationMapResult.containsKey(clientId)) {
|
||||||
|
throw new IllegalArgumentException("Registered client must be unique. " +
|
||||||
|
"Found duplicate client identifier: " + clientId);
|
||||||
|
}
|
||||||
|
idRegistrationMapResult.put(id, registration);
|
||||||
|
clientIdRegistrationMapResult.put(clientId, registration);
|
||||||
|
}
|
||||||
|
idRegistrationMap = idRegistrationMapResult;
|
||||||
|
clientIdRegistrationMap = clientIdRegistrationMapResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RegisteredClient findById(String id) {
|
||||||
|
Assert.hasText(id, "id cannot be empty");
|
||||||
|
return this.idRegistrationMap.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RegisteredClient findByClientId(String clientId) {
|
||||||
|
Assert.hasText(clientId, "clientId cannot be empty");
|
||||||
|
return this.clientIdRegistrationMap.get(clientId);
|
||||||
|
}
|
||||||
|
}
|
@ -15,25 +15,370 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.security.oauth2.server.authorization.client;
|
package org.springframework.security.oauth2.server.authorization.client;
|
||||||
|
|
||||||
import org.springframework.security.core.SpringSecurityCoreVersion;
|
|
||||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||||
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
|
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
|
||||||
|
import org.springframework.security.oauth2.server.authorization.Version;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* A representation of a client registration with an OAuth 2.0 Authorization Server.
|
||||||
|
*
|
||||||
* @author Joe Grandja
|
* @author Joe Grandja
|
||||||
|
* @author Anoop Garlapati
|
||||||
|
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-2">Section 2 Client Registration</a>
|
||||||
*/
|
*/
|
||||||
public class RegisteredClient implements Serializable {
|
public class RegisteredClient implements Serializable {
|
||||||
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
|
private static final long serialVersionUID = Version.SERIAL_VERSION_UID;
|
||||||
private String id;
|
private String id;
|
||||||
private String clientId;
|
private String clientId;
|
||||||
private String clientSecret;
|
private String clientSecret;
|
||||||
private Set<ClientAuthenticationMethod> clientAuthenticationMethods = Collections.emptySet();
|
private Set<ClientAuthenticationMethod> clientAuthenticationMethods =
|
||||||
|
Collections.singleton(ClientAuthenticationMethod.BASIC);
|
||||||
private Set<AuthorizationGrantType> authorizationGrantTypes = Collections.emptySet();
|
private Set<AuthorizationGrantType> authorizationGrantTypes = Collections.emptySet();
|
||||||
private Set<String> redirectUris = Collections.emptySet();
|
private Set<String> redirectUris = Collections.emptySet();
|
||||||
private Set<String> scopes = Collections.emptySet();
|
private Set<String> scopes = Collections.emptySet();
|
||||||
|
|
||||||
|
protected RegisteredClient() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the identifier for the registration.
|
||||||
|
*
|
||||||
|
* @return the identifier for the registration
|
||||||
|
*/
|
||||||
|
public String getId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the client identifier.
|
||||||
|
*
|
||||||
|
* @return the client identifier
|
||||||
|
*/
|
||||||
|
public String getClientId() {
|
||||||
|
return this.clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the client secret.
|
||||||
|
*
|
||||||
|
* @return the client secret
|
||||||
|
*/
|
||||||
|
public String getClientSecret() {
|
||||||
|
return this.clientSecret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link ClientAuthenticationMethod authentication method(s)} used
|
||||||
|
* when authenticating the client with the authorization server.
|
||||||
|
*
|
||||||
|
* @return the {@code Set} of {@link ClientAuthenticationMethod authentication method(s)}
|
||||||
|
*/
|
||||||
|
public Set<ClientAuthenticationMethod> getClientAuthenticationMethods() {
|
||||||
|
return this.clientAuthenticationMethods;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link AuthorizationGrantType authorization grant type(s)} that the client may use.
|
||||||
|
*
|
||||||
|
* @return the {@code Set} of {@link AuthorizationGrantType authorization grant type(s)}
|
||||||
|
*/
|
||||||
|
public Set<AuthorizationGrantType> getAuthorizationGrantTypes() {
|
||||||
|
return this.authorizationGrantTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the redirect URI(s) that the client may use in redirect-based flows.
|
||||||
|
*
|
||||||
|
* @return the {@code Set} of redirect URI(s)
|
||||||
|
*/
|
||||||
|
public Set<String> getRedirectUris() {
|
||||||
|
return this.redirectUris;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the scope(s) used by the client.
|
||||||
|
*
|
||||||
|
* @return the {@code Set} of scope(s)
|
||||||
|
*/
|
||||||
|
public Set<String> getScopes() {
|
||||||
|
return this.scopes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "RegisteredClient{" +
|
||||||
|
"id='" + this.id + '\'' +
|
||||||
|
", clientId='" + this.clientId + '\'' +
|
||||||
|
", clientAuthenticationMethods=" + this.clientAuthenticationMethods +
|
||||||
|
", authorizationGrantTypes=" + this.authorizationGrantTypes +
|
||||||
|
", redirectUris=" + this.redirectUris +
|
||||||
|
", scopes=" + this.scopes +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new {@link Builder}, initialized with the provided registration identifier.
|
||||||
|
*
|
||||||
|
* @param id the identifier for the registration
|
||||||
|
* @return the {@link Builder}
|
||||||
|
*/
|
||||||
|
public static Builder withId(String id) {
|
||||||
|
Assert.hasText(id, "id cannot be empty");
|
||||||
|
return new Builder(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new {@link Builder}, initialized with the provided {@link RegisteredClient}.
|
||||||
|
*
|
||||||
|
* @param registeredClient the {@link RegisteredClient} to copy from
|
||||||
|
* @return the {@link Builder}
|
||||||
|
*/
|
||||||
|
public static Builder withRegisteredClient(RegisteredClient registeredClient) {
|
||||||
|
Assert.notNull(registeredClient, "registeredClient cannot be null");
|
||||||
|
return new Builder(registeredClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A builder for {@link RegisteredClient}.
|
||||||
|
*/
|
||||||
|
public static class Builder implements Serializable {
|
||||||
|
private static final long serialVersionUID = Version.SERIAL_VERSION_UID;
|
||||||
|
private String id;
|
||||||
|
private String clientId;
|
||||||
|
private String clientSecret;
|
||||||
|
private Set<ClientAuthenticationMethod> clientAuthenticationMethods =
|
||||||
|
new LinkedHashSet<>(Collections.singletonList(ClientAuthenticationMethod.BASIC));
|
||||||
|
private Set<AuthorizationGrantType> authorizationGrantTypes = new LinkedHashSet<>();
|
||||||
|
private Set<String> redirectUris = new LinkedHashSet<>();
|
||||||
|
private Set<String> scopes = new LinkedHashSet<>();
|
||||||
|
|
||||||
|
protected Builder(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Builder(RegisteredClient registeredClient) {
|
||||||
|
this.id = registeredClient.id;
|
||||||
|
this.clientId = registeredClient.clientId;
|
||||||
|
this.clientSecret = registeredClient.clientSecret;
|
||||||
|
this.clientAuthenticationMethods = registeredClient.clientAuthenticationMethods == null ? null :
|
||||||
|
new HashSet<>(registeredClient.clientAuthenticationMethods);
|
||||||
|
this.authorizationGrantTypes = registeredClient.authorizationGrantTypes == null ? null :
|
||||||
|
new HashSet<>(registeredClient.authorizationGrantTypes);
|
||||||
|
this.redirectUris = registeredClient.redirectUris == null ? null :
|
||||||
|
new HashSet<>(registeredClient.redirectUris);
|
||||||
|
this.scopes = registeredClient.scopes == null ? null : new HashSet<>(registeredClient.scopes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the identifier for the registration.
|
||||||
|
*
|
||||||
|
* @param id the identifier for the registration
|
||||||
|
* @return the {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder id(String id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the client identifier.
|
||||||
|
*
|
||||||
|
* @param clientId the client identifier
|
||||||
|
* @return the {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder clientId(String clientId) {
|
||||||
|
this.clientId = clientId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the client secret.
|
||||||
|
*
|
||||||
|
* @param clientSecret the client secret
|
||||||
|
* @return the {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder clientSecret(String clientSecret) {
|
||||||
|
this.clientSecret = clientSecret;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the {@link ClientAuthenticationMethod authentication method} to the set of
|
||||||
|
* client authentication methods used when authenticating the client with the authorization server.
|
||||||
|
*
|
||||||
|
* @param clientAuthenticationMethod the authentication method
|
||||||
|
* @return the {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder clientAuthenticationMethod(ClientAuthenticationMethod clientAuthenticationMethod) {
|
||||||
|
this.clientAuthenticationMethods.add(clientAuthenticationMethod);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link ClientAuthenticationMethod authentication method(s)} used
|
||||||
|
* when authenticating the client with the authorization server.
|
||||||
|
*
|
||||||
|
* @param clientAuthenticationMethodsConsumer the authentication method(s) {@link Consumer}
|
||||||
|
* @return the {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder clientAuthenticationMethods(
|
||||||
|
Consumer<Set<ClientAuthenticationMethod>> clientAuthenticationMethodsConsumer) {
|
||||||
|
clientAuthenticationMethodsConsumer.accept(this.clientAuthenticationMethods);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the {@link AuthorizationGrantType authorization grant type} to
|
||||||
|
* the set of authorization grant types that the client may use.
|
||||||
|
*
|
||||||
|
* @param authorizationGrantType the authorization grant type
|
||||||
|
* @return the {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder authorizationGrantType(AuthorizationGrantType authorizationGrantType) {
|
||||||
|
this.authorizationGrantTypes.add(authorizationGrantType);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link AuthorizationGrantType authorization grant type(s)} that the client may use.
|
||||||
|
*
|
||||||
|
* @param authorizationGrantTypesConsumer the authorization grant type(s) {@link Consumer}
|
||||||
|
* @return the {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder authorizationGrantTypes(Consumer<Set<AuthorizationGrantType>> authorizationGrantTypesConsumer) {
|
||||||
|
authorizationGrantTypesConsumer.accept(this.authorizationGrantTypes);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the redirect URI to the set of redirect URIs that the client may use in redirect-based flows.
|
||||||
|
*
|
||||||
|
* @param redirectUri the redirect URI to add
|
||||||
|
* @return the {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder redirectUri(String redirectUri) {
|
||||||
|
this.redirectUris.add(redirectUri);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the redirect URI(s) that the client may use in redirect-based flows.
|
||||||
|
*
|
||||||
|
* @param redirectUrisConsumer the redirect URI(s) {@link Consumer}
|
||||||
|
* @return the {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder redirectUris(Consumer<Set<String>> redirectUrisConsumer) {
|
||||||
|
redirectUrisConsumer.accept(this.redirectUris);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the scope to the set of scopes used by the client.
|
||||||
|
*
|
||||||
|
* @param scope the scope to add
|
||||||
|
* @return the {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder scope(String scope) {
|
||||||
|
this.scopes.add(scope);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the scope(s) used by the client.
|
||||||
|
*
|
||||||
|
* @param scopesConsumer the scope(s) {@link Consumer}
|
||||||
|
* @return the {@link Builder}
|
||||||
|
*/
|
||||||
|
public Builder scopes(Consumer<Set<String>> scopesConsumer) {
|
||||||
|
scopesConsumer.accept(this.scopes);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a new {@link RegisteredClient}.
|
||||||
|
*
|
||||||
|
* @return a {@link RegisteredClient}
|
||||||
|
*/
|
||||||
|
public RegisteredClient build() {
|
||||||
|
Assert.notEmpty(this.clientAuthenticationMethods, "clientAuthenticationMethods cannot be empty");
|
||||||
|
Assert.notEmpty(this.authorizationGrantTypes, "authorizationGrantTypes cannot be empty");
|
||||||
|
if (authorizationGrantTypes.contains(AuthorizationGrantType.AUTHORIZATION_CODE)) {
|
||||||
|
Assert.hasText(this.id, "id cannot be empty");
|
||||||
|
Assert.hasText(this.clientId, "clientId cannot be empty");
|
||||||
|
Assert.hasText(this.clientSecret, "clientSecret cannot be empty");
|
||||||
|
Assert.notEmpty(this.redirectUris, "redirectUris cannot be empty");
|
||||||
|
}
|
||||||
|
this.validateScopes();
|
||||||
|
this.validateRedirectUris();
|
||||||
|
return this.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
private RegisteredClient create() {
|
||||||
|
RegisteredClient registeredClient = new RegisteredClient();
|
||||||
|
|
||||||
|
registeredClient.id = this.id;
|
||||||
|
registeredClient.clientId = this.clientId;
|
||||||
|
registeredClient.clientSecret = this.clientSecret;
|
||||||
|
registeredClient.clientAuthenticationMethods =
|
||||||
|
Collections.unmodifiableSet(this.clientAuthenticationMethods);
|
||||||
|
registeredClient.authorizationGrantTypes = Collections.unmodifiableSet(this.authorizationGrantTypes);
|
||||||
|
registeredClient.redirectUris = Collections.unmodifiableSet(this.redirectUris);
|
||||||
|
registeredClient.scopes = Collections.unmodifiableSet(this.scopes);
|
||||||
|
|
||||||
|
return registeredClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateScopes() {
|
||||||
|
if (CollectionUtils.isEmpty(this.scopes)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String scope : this.scopes) {
|
||||||
|
Assert.isTrue(validateScope(scope), "scope \"" + scope + "\" contains invalid characters");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean validateScope(String scope) {
|
||||||
|
return scope == null ||
|
||||||
|
scope.chars().allMatch(c -> withinTheRangeOf(c, 0x21, 0x21) ||
|
||||||
|
withinTheRangeOf(c, 0x23, 0x5B) ||
|
||||||
|
withinTheRangeOf(c, 0x5D, 0x7E));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean withinTheRangeOf(int c, int min, int max) {
|
||||||
|
return c >= min && c <= max;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateRedirectUris() {
|
||||||
|
if (CollectionUtils.isEmpty(this.redirectUris)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String redirectUri : redirectUris) {
|
||||||
|
Assert.isTrue(validateRedirectUri(redirectUri),
|
||||||
|
"redirect_uri \"" + redirectUri + "\" is not a valid redirect URI or contains fragment");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean validateRedirectUri(String redirectUri) {
|
||||||
|
try {
|
||||||
|
URI validRedirectUri = new URI(redirectUri);
|
||||||
|
return validRedirectUri.getFragment() == null;
|
||||||
|
} catch (URISyntaxException ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -16,12 +16,28 @@
|
|||||||
package org.springframework.security.oauth2.server.authorization.client;
|
package org.springframework.security.oauth2.server.authorization.client;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* A repository for OAuth 2.0 {@link RegisteredClient}(s).
|
||||||
|
*
|
||||||
* @author Joe Grandja
|
* @author Joe Grandja
|
||||||
|
* @author Anoop Garlapati
|
||||||
|
* @see RegisteredClient
|
||||||
*/
|
*/
|
||||||
public interface RegisteredClientRepository {
|
public interface RegisteredClientRepository {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the registered client identified by the provided {@code id}, or {@code null} if not found.
|
||||||
|
*
|
||||||
|
* @param id the registration identifier
|
||||||
|
* @return the {@link RegisteredClient} if found, otherwise {@code null}
|
||||||
|
*/
|
||||||
RegisteredClient findById(String id);
|
RegisteredClient findById(String id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the registered client identified by the provided {@code clientId}, or {@code null} if not found.
|
||||||
|
*
|
||||||
|
* @param clientId the client identifier
|
||||||
|
* @return the {@link RegisteredClient} if found, otherwise {@code null}
|
||||||
|
*/
|
||||||
RegisteredClient findByClientId(String clientId);
|
RegisteredClient findByClientId(String clientId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* 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.client;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link InMemoryRegisteredClientRepository}.
|
||||||
|
*
|
||||||
|
* @author Anoop Garlapati
|
||||||
|
*/
|
||||||
|
public class InMemoryRegisteredClientRepositoryTests {
|
||||||
|
private RegisteredClient registration = TestRegisteredClients.registeredClient().build();
|
||||||
|
|
||||||
|
private InMemoryRegisteredClientRepository clients = new InMemoryRegisteredClientRepository(this.registration);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructorVarargsRegisteredClientWhenNullThenThrowIllegalArgumentException() {
|
||||||
|
assertThatThrownBy(() -> {
|
||||||
|
RegisteredClient registration = null;
|
||||||
|
new InMemoryRegisteredClientRepository(registration);
|
||||||
|
}).isInstanceOf(IllegalArgumentException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructorListRegisteredClientWhenNullThenThrowIllegalArgumentException() {
|
||||||
|
assertThatThrownBy(() -> {
|
||||||
|
List<RegisteredClient> registrations = null;
|
||||||
|
new InMemoryRegisteredClientRepository(registrations);
|
||||||
|
}).isInstanceOf(IllegalArgumentException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructorListClientRegistrationWhenEmptyThenThrowIllegalArgumentException() {
|
||||||
|
assertThatThrownBy(() -> {
|
||||||
|
List<RegisteredClient> registrations = Collections.emptyList();
|
||||||
|
new InMemoryRegisteredClientRepository(registrations);
|
||||||
|
}).isInstanceOf(IllegalArgumentException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructorListRegisteredClientWhenDuplicateIdThenThrowIllegalArgumentException() {
|
||||||
|
assertThatThrownBy(() -> {
|
||||||
|
RegisteredClient anotherRegistrationWithSameId = TestRegisteredClients.registeredClient2()
|
||||||
|
.id(this.registration.getId()).build();
|
||||||
|
List<RegisteredClient> registrations = Arrays.asList(this.registration, anotherRegistrationWithSameId);
|
||||||
|
new InMemoryRegisteredClientRepository(registrations);
|
||||||
|
}).isInstanceOf(IllegalArgumentException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructorListRegisteredClientWhenDuplicateClientIdThenThrowIllegalArgumentException() {
|
||||||
|
assertThatThrownBy(() -> {
|
||||||
|
RegisteredClient anotherRegistrationWithSameClientId = TestRegisteredClients.registeredClient2()
|
||||||
|
.clientId(this.registration.getClientId()).build();
|
||||||
|
List<RegisteredClient> registrations = Arrays.asList(this.registration,
|
||||||
|
anotherRegistrationWithSameClientId);
|
||||||
|
new InMemoryRegisteredClientRepository(registrations);
|
||||||
|
}).isInstanceOf(IllegalArgumentException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findByIdWhenFoundThenFound() {
|
||||||
|
String id = this.registration.getId();
|
||||||
|
assertThat(clients.findById(id)).isEqualTo(this.registration);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findByIdWhenNotFoundThenNull() {
|
||||||
|
String missingId = this.registration.getId() + "MISSING";
|
||||||
|
assertThat(clients.findById(missingId)).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findByIdWhenNullThenThrowIllegalArgumentException() {
|
||||||
|
assertThatThrownBy(() -> clients.findById(null)).isInstanceOf(IllegalArgumentException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findByClientIdWhenFoundThenFound() {
|
||||||
|
String clientId = this.registration.getClientId();
|
||||||
|
assertThat(clients.findByClientId(clientId)).isEqualTo(this.registration);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findByClientIdWhenNotFoundThenNull() {
|
||||||
|
String missingClientId = this.registration.getClientId() + "MISSING";
|
||||||
|
assertThat(clients.findByClientId(missingClientId)).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findByClientIdWhenNullThenThrowIllegalArgumentException() {
|
||||||
|
assertThatThrownBy(() -> clients.findByClientId(null)).isInstanceOf(IllegalArgumentException.class);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,410 @@
|
|||||||
|
/*
|
||||||
|
* 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.client;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||||
|
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link RegisteredClient}.
|
||||||
|
*
|
||||||
|
* @author Anoop Garlapati
|
||||||
|
*/
|
||||||
|
public class RegisteredClientTests {
|
||||||
|
private static final String ID = "registration-1";
|
||||||
|
private static final String CLIENT_ID = "client-1";
|
||||||
|
private static final String CLIENT_SECRET = "secret";
|
||||||
|
private static final Set<String> REDIRECT_URIS = Collections.singleton("https://example.com");
|
||||||
|
private static final Set<String> SCOPES = Collections.unmodifiableSet(
|
||||||
|
Stream.of("openid", "profile", "email").collect(Collectors.toSet()));
|
||||||
|
private static final Set<ClientAuthenticationMethod> CLIENT_AUTHENTICATION_METHODS =
|
||||||
|
Collections.singleton(ClientAuthenticationMethod.BASIC);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildWhenAuthorizationGrantTypesIsNotSetThenThrowIllegalArgumentException() {
|
||||||
|
assertThatThrownBy(() ->
|
||||||
|
RegisteredClient.withId(ID)
|
||||||
|
.clientId(CLIENT_ID)
|
||||||
|
.clientSecret(CLIENT_SECRET)
|
||||||
|
.redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS))
|
||||||
|
.scopes(scopes -> scopes.addAll(SCOPES))
|
||||||
|
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
|
||||||
|
.build()
|
||||||
|
).isInstanceOf(IllegalArgumentException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildWhenAuthorizationCodeGrantAllAttributesProvidedThenAllAttributesAreSet() {
|
||||||
|
RegisteredClient registration = RegisteredClient.withId(ID)
|
||||||
|
.clientId(CLIENT_ID)
|
||||||
|
.clientSecret(CLIENT_SECRET)
|
||||||
|
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||||
|
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
|
||||||
|
.redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS))
|
||||||
|
.scopes(scopes -> scopes.addAll(SCOPES))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assertThat(registration.getId()).isEqualTo(ID);
|
||||||
|
assertThat(registration.getClientId()).isEqualTo(CLIENT_ID);
|
||||||
|
assertThat(registration.getClientSecret()).isEqualTo(CLIENT_SECRET);
|
||||||
|
assertThat(registration.getAuthorizationGrantTypes())
|
||||||
|
.isEqualTo(Collections.singleton(AuthorizationGrantType.AUTHORIZATION_CODE));
|
||||||
|
assertThat(registration.getClientAuthenticationMethods()).isEqualTo(CLIENT_AUTHENTICATION_METHODS);
|
||||||
|
assertThat(registration.getRedirectUris()).isEqualTo(REDIRECT_URIS);
|
||||||
|
assertThat(registration.getScopes()).isEqualTo(SCOPES);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildWhenAuthorizationCodeGrantIdIsNullThenThrowIllegalArgumentException() {
|
||||||
|
assertThatThrownBy(() ->
|
||||||
|
RegisteredClient.withId(null)
|
||||||
|
.clientId(CLIENT_ID)
|
||||||
|
.clientSecret(CLIENT_SECRET)
|
||||||
|
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||||
|
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
|
||||||
|
.redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS))
|
||||||
|
.scopes(scopes -> scopes.addAll(SCOPES))
|
||||||
|
.build()
|
||||||
|
).isInstanceOf(IllegalArgumentException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildWhenAuthorizationCodeGrantClientIdIsNullThenThrowIllegalArgumentException() {
|
||||||
|
assertThatThrownBy(() ->
|
||||||
|
RegisteredClient.withId(ID)
|
||||||
|
.clientId(null)
|
||||||
|
.clientSecret(CLIENT_SECRET)
|
||||||
|
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||||
|
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
|
||||||
|
.redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS))
|
||||||
|
.scopes(scopes -> scopes.addAll(SCOPES))
|
||||||
|
.build()
|
||||||
|
).isInstanceOf(IllegalArgumentException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildWhenAuthorizationCodeGrantClientSecretIsNullThenThrowIllegalArgumentException() {
|
||||||
|
assertThatThrownBy(() ->
|
||||||
|
RegisteredClient.withId(ID)
|
||||||
|
.clientId(null)
|
||||||
|
.clientSecret(CLIENT_SECRET)
|
||||||
|
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||||
|
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
|
||||||
|
.redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS))
|
||||||
|
.scopes(scopes -> scopes.addAll(SCOPES))
|
||||||
|
.build()
|
||||||
|
).isInstanceOf(IllegalArgumentException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildWhenAuthorizationCodeGrantRedirectUrisNotProvidedThenThrowIllegalArgumentException() {
|
||||||
|
assertThatThrownBy(() ->
|
||||||
|
RegisteredClient.withId(ID)
|
||||||
|
.clientId(CLIENT_ID)
|
||||||
|
.clientSecret(CLIENT_SECRET)
|
||||||
|
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||||
|
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
|
||||||
|
.scopes(scopes -> scopes.addAll(SCOPES))
|
||||||
|
.build()
|
||||||
|
).isInstanceOf(IllegalArgumentException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildWhenAuthorizationCodeGrantRedirectUrisConsumerClearsSetThenThrowIllegalArgumentException() {
|
||||||
|
assertThatThrownBy(() ->
|
||||||
|
RegisteredClient.withId(ID)
|
||||||
|
.clientId(CLIENT_ID)
|
||||||
|
.clientSecret(CLIENT_SECRET)
|
||||||
|
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||||
|
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
|
||||||
|
.redirectUri("https://example.com")
|
||||||
|
.redirectUris(Set::clear)
|
||||||
|
.scopes(scopes -> scopes.addAll(SCOPES))
|
||||||
|
.build()
|
||||||
|
).isInstanceOf(IllegalArgumentException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildWhenAuthorizationCodeGrantClientAuthenticationMethodNotProvidedThenDefaultToBasic() {
|
||||||
|
RegisteredClient registration = RegisteredClient.withId(ID)
|
||||||
|
.clientId(CLIENT_ID)
|
||||||
|
.clientSecret(CLIENT_SECRET)
|
||||||
|
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||||
|
.redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS))
|
||||||
|
.scopes(scopes -> scopes.addAll(SCOPES))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assertThat(registration.getClientAuthenticationMethods())
|
||||||
|
.isEqualTo(Collections.singleton(ClientAuthenticationMethod.BASIC));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildWhenAuthorizationCodeGrantScopeIsEmptyThenScopeNotRequired() {
|
||||||
|
RegisteredClient.withId(ID)
|
||||||
|
.clientId(CLIENT_ID)
|
||||||
|
.clientSecret(CLIENT_SECRET)
|
||||||
|
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||||
|
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
|
||||||
|
.redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildWhenAuthorizationCodeGrantScopeConsumerIsProvidedThenConsumerAccepted() {
|
||||||
|
RegisteredClient registration = RegisteredClient.withId(ID)
|
||||||
|
.clientId(CLIENT_ID)
|
||||||
|
.clientSecret(CLIENT_SECRET)
|
||||||
|
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||||
|
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
|
||||||
|
.redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS))
|
||||||
|
.scopes(scopes -> scopes.addAll(SCOPES))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assertThat(registration.getScopes()).isEqualTo(SCOPES);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildWhenScopeContainsASpaceThenThrowIllegalArgumentException() {
|
||||||
|
assertThatThrownBy(() ->
|
||||||
|
RegisteredClient.withId(ID)
|
||||||
|
.clientId(CLIENT_ID)
|
||||||
|
.clientSecret(null)
|
||||||
|
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||||
|
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
|
||||||
|
.redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS))
|
||||||
|
.scope("openid profile")
|
||||||
|
.build()
|
||||||
|
).isInstanceOf(IllegalArgumentException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildWhenScopesContainsAnInvalidCharacterThenThrowIllegalArgumentException() {
|
||||||
|
assertThatThrownBy(() ->
|
||||||
|
RegisteredClient.withId(ID)
|
||||||
|
.clientId(CLIENT_ID)
|
||||||
|
.clientSecret(CLIENT_SECRET)
|
||||||
|
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||||
|
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
|
||||||
|
.redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS))
|
||||||
|
.scope("an\"invalid\"scope")
|
||||||
|
.build()
|
||||||
|
).isInstanceOf(IllegalArgumentException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildWhenRedirectUrisContainInvalidUriThenThrowIllegalArgumentException() {
|
||||||
|
assertThatThrownBy(() ->
|
||||||
|
RegisteredClient.withId(ID)
|
||||||
|
.clientId(CLIENT_ID)
|
||||||
|
.clientSecret(CLIENT_SECRET)
|
||||||
|
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||||
|
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
|
||||||
|
.redirectUri("invalid URI")
|
||||||
|
.scopes(scopes -> scopes.addAll(SCOPES))
|
||||||
|
.build()
|
||||||
|
).isInstanceOf(IllegalArgumentException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildWhenRedirectUrisContainUriWithFragmentThenThrowIllegalArgumentException() {
|
||||||
|
assertThatThrownBy(() ->
|
||||||
|
RegisteredClient.withId(ID)
|
||||||
|
.clientId(CLIENT_ID)
|
||||||
|
.clientSecret(CLIENT_SECRET)
|
||||||
|
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||||
|
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
|
||||||
|
.redirectUri("https://example.com/page#fragment")
|
||||||
|
.scopes(scopes -> scopes.addAll(SCOPES))
|
||||||
|
.build()
|
||||||
|
).isInstanceOf(IllegalArgumentException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildWhenTwoAuthorizationGrantTypesAreProvidedThenBothAreRegistered() {
|
||||||
|
RegisteredClient registration = RegisteredClient.withId(ID)
|
||||||
|
.clientId(CLIENT_ID)
|
||||||
|
.clientSecret(CLIENT_SECRET)
|
||||||
|
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||||
|
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
|
||||||
|
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
|
||||||
|
.redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS))
|
||||||
|
.scopes(scopes -> scopes.addAll(SCOPES))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assertThat(registration.getAuthorizationGrantTypes())
|
||||||
|
.containsExactly(AuthorizationGrantType.AUTHORIZATION_CODE, AuthorizationGrantType.CLIENT_CREDENTIALS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildWhenAuthorizationGrantTypesConsumerIsProvidedThenConsumerAccepted() {
|
||||||
|
RegisteredClient registration = RegisteredClient.withId(ID)
|
||||||
|
.clientId(CLIENT_ID)
|
||||||
|
.clientSecret(CLIENT_SECRET)
|
||||||
|
.authorizationGrantTypes(authorizationGrantTypes -> {
|
||||||
|
authorizationGrantTypes.add(AuthorizationGrantType.AUTHORIZATION_CODE);
|
||||||
|
authorizationGrantTypes.add(AuthorizationGrantType.CLIENT_CREDENTIALS);
|
||||||
|
})
|
||||||
|
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
|
||||||
|
.redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS))
|
||||||
|
.scopes(scopes -> scopes.addAll(SCOPES))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assertThat(registration.getAuthorizationGrantTypes())
|
||||||
|
.containsExactly(AuthorizationGrantType.AUTHORIZATION_CODE, AuthorizationGrantType.CLIENT_CREDENTIALS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildWhenAuthorizationGrantTypesConsumerClearsSetThenThrowIllegalArgumentException() {
|
||||||
|
assertThatThrownBy(() -> {
|
||||||
|
RegisteredClient.withId(ID)
|
||||||
|
.clientId(CLIENT_ID)
|
||||||
|
.clientSecret(CLIENT_SECRET)
|
||||||
|
.authorizationGrantTypes(Set::clear)
|
||||||
|
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
|
||||||
|
.redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS))
|
||||||
|
.scopes(scopes -> scopes.addAll(SCOPES))
|
||||||
|
.build();
|
||||||
|
}).isInstanceOf(IllegalArgumentException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildWhenTwoClientAuthenticationMethodsAreProvidedThenBothAreRegistered() {
|
||||||
|
RegisteredClient registration = RegisteredClient.withId(ID)
|
||||||
|
.clientId(CLIENT_ID)
|
||||||
|
.clientSecret(CLIENT_SECRET)
|
||||||
|
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||||
|
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
|
||||||
|
.clientAuthenticationMethod(ClientAuthenticationMethod.POST)
|
||||||
|
.redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS))
|
||||||
|
.scopes(scopes -> scopes.addAll(SCOPES))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assertThat(registration.getClientAuthenticationMethods())
|
||||||
|
.containsExactly(ClientAuthenticationMethod.BASIC, ClientAuthenticationMethod.POST);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildWhenClientAuthenticationMethodsConsumerIsProvidedThenConsumerAccepted() {
|
||||||
|
RegisteredClient registration = RegisteredClient.withId(ID)
|
||||||
|
.clientId(CLIENT_ID)
|
||||||
|
.clientSecret(CLIENT_SECRET)
|
||||||
|
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||||
|
.clientAuthenticationMethods(clientAuthenticationMethods -> {
|
||||||
|
clientAuthenticationMethods.add(ClientAuthenticationMethod.BASIC);
|
||||||
|
clientAuthenticationMethods.add(ClientAuthenticationMethod.POST);
|
||||||
|
})
|
||||||
|
.redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS))
|
||||||
|
.scopes(scopes -> scopes.addAll(SCOPES))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assertThat(registration.getClientAuthenticationMethods())
|
||||||
|
.containsExactly(ClientAuthenticationMethod.BASIC, ClientAuthenticationMethod.POST);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildWhenClientAuthenticationMethodsConsumerClearsSetThenThrowIllegalArgumentException() {
|
||||||
|
assertThatThrownBy(() -> {
|
||||||
|
RegisteredClient.withId(ID)
|
||||||
|
.clientId(CLIENT_ID)
|
||||||
|
.clientSecret(CLIENT_SECRET)
|
||||||
|
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||||
|
.clientAuthenticationMethods(Set::clear)
|
||||||
|
.redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS))
|
||||||
|
.scopes(scopes -> scopes.addAll(SCOPES))
|
||||||
|
.build();
|
||||||
|
}).isInstanceOf(IllegalArgumentException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildWhenOverrideIdThenOverridden() {
|
||||||
|
String overriddenId = "override";
|
||||||
|
RegisteredClient registration = RegisteredClient.withId(ID)
|
||||||
|
.id(overriddenId)
|
||||||
|
.clientId(CLIENT_ID)
|
||||||
|
.clientSecret(CLIENT_SECRET)
|
||||||
|
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||||
|
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
|
||||||
|
.redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS))
|
||||||
|
.scopes(scopes -> scopes.addAll(SCOPES))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assertThat(registration.getId()).isEqualTo(overriddenId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildWhenRegisteredClientProvidedThenMakesACopy() {
|
||||||
|
RegisteredClient registration = TestRegisteredClients.registeredClient().build();
|
||||||
|
RegisteredClient updated = RegisteredClient.withRegisteredClient(registration).build();
|
||||||
|
|
||||||
|
assertThat(registration.getClientAuthenticationMethods()).isEqualTo(updated.getClientAuthenticationMethods());
|
||||||
|
assertThat(registration.getClientAuthenticationMethods()).isNotSameAs(updated.getClientAuthenticationMethods());
|
||||||
|
assertThat(registration.getAuthorizationGrantTypes()).isEqualTo(updated.getAuthorizationGrantTypes());
|
||||||
|
assertThat(registration.getAuthorizationGrantTypes()).isNotSameAs(updated.getAuthorizationGrantTypes());
|
||||||
|
assertThat(registration.getRedirectUris()).isEqualTo(updated.getRedirectUris());
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildWhenClientRegistrationValuesOverriddenThenPropagated() {
|
||||||
|
RegisteredClient registration = TestRegisteredClients.registeredClient().build();
|
||||||
|
String newSecret = "new-secret";
|
||||||
|
String newScope = "new-scope";
|
||||||
|
String newRedirectUri = "https://another-redirect-uri.com";
|
||||||
|
RegisteredClient updated = RegisteredClient.withRegisteredClient(registration)
|
||||||
|
.clientSecret(newSecret)
|
||||||
|
.scopes(scopes -> {
|
||||||
|
scopes.clear();
|
||||||
|
scopes.add(newScope);
|
||||||
|
})
|
||||||
|
.redirectUris(redirectUris -> {
|
||||||
|
redirectUris.clear();
|
||||||
|
redirectUris.add(newRedirectUri);
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assertThat(registration.getClientSecret()).isNotEqualTo(newSecret);
|
||||||
|
assertThat(updated.getClientSecret()).isEqualTo(newSecret);
|
||||||
|
assertThat(registration.getScopes()).doesNotContain(newScope);
|
||||||
|
assertThat(updated.getScopes()).containsExactly(newScope);
|
||||||
|
assertThat(registration.getRedirectUris()).doesNotContain(newRedirectUri);
|
||||||
|
assertThat(updated.getRedirectUris()).containsExactly(newRedirectUri);
|
||||||
|
}
|
||||||
|
}
|
@ -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.client;
|
||||||
|
|
||||||
|
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||||
|
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Anoop Garlapati
|
||||||
|
*/
|
||||||
|
public class TestRegisteredClients {
|
||||||
|
|
||||||
|
public static RegisteredClient.Builder registeredClient() {
|
||||||
|
return RegisteredClient.withId("registration-1")
|
||||||
|
.clientId("client-1")
|
||||||
|
.clientSecret("secret")
|
||||||
|
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||||
|
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
|
||||||
|
.redirectUri("https://example.com")
|
||||||
|
.scope("openid")
|
||||||
|
.scope("profile")
|
||||||
|
.scope("email");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RegisteredClient.Builder registeredClient2() {
|
||||||
|
return RegisteredClient.withId("registration-2")
|
||||||
|
.clientId("client-2")
|
||||||
|
.clientSecret("secret")
|
||||||
|
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||||
|
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
|
||||||
|
.redirectUri("https://example.com")
|
||||||
|
.scope("openid")
|
||||||
|
.scope("profile")
|
||||||
|
.scope("email");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user