Skip to content

Commit 22d43b9

Browse files
committed
Add delegating OAuth2AuthorizedClientProvider
1 parent c100e62 commit 22d43b9

File tree

2 files changed

+156
-0
lines changed

2 files changed

+156
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright 2002-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.security.oauth2.client;
17+
18+
import org.springframework.lang.Nullable;
19+
import org.springframework.util.Assert;
20+
21+
import java.util.ArrayList;
22+
import java.util.Arrays;
23+
import java.util.Collections;
24+
import java.util.List;
25+
import java.util.Objects;
26+
27+
/**
28+
* An implementation of an {@link OAuth2AuthorizedClientProvider} that simply delegates
29+
* to it's internal {@code List} of {@link OAuth2AuthorizedClientProvider}(s).
30+
* <p>
31+
* Each provider is given a chance to
32+
* {@link OAuth2AuthorizedClientProvider#authorize(OAuth2AuthorizationContext) authorize}
33+
* the {@link OAuth2AuthorizationContext#getClientRegistration() client} in the provided context
34+
* with the first {@code non-null} {@link OAuth2AuthorizedClient} being returned.
35+
*
36+
* @author Joe Grandja
37+
* @since 5.2
38+
* @see OAuth2AuthorizedClientProvider
39+
*/
40+
public final class DelegatingOAuth2AuthorizedClientProvider implements OAuth2AuthorizedClientProvider {
41+
private final List<OAuth2AuthorizedClientProvider> authorizedClientProviders;
42+
43+
/**
44+
* Constructs a {@code DelegatingOAuth2AuthorizedClientProvider} using the provided parameters.
45+
*
46+
* @param authorizedClientProviders a list of {@link OAuth2AuthorizedClientProvider}(s)
47+
*/
48+
public DelegatingOAuth2AuthorizedClientProvider(OAuth2AuthorizedClientProvider... authorizedClientProviders) {
49+
Assert.notEmpty(authorizedClientProviders, "authorizedClientProviders cannot be empty");
50+
this.authorizedClientProviders = Collections.unmodifiableList(Arrays.asList(authorizedClientProviders));
51+
}
52+
53+
/**
54+
* Constructs a {@code DelegatingOAuth2AuthorizedClientProvider} using the provided parameters.
55+
*
56+
* @param authorizedClientProviders a {@code List} of {@link OAuth2AuthorizedClientProvider}(s)
57+
*/
58+
public DelegatingOAuth2AuthorizedClientProvider(List<OAuth2AuthorizedClientProvider> authorizedClientProviders) {
59+
Assert.notEmpty(authorizedClientProviders, "authorizedClientProviders cannot be empty");
60+
this.authorizedClientProviders = Collections.unmodifiableList(new ArrayList<>(authorizedClientProviders));
61+
}
62+
63+
@Override
64+
@Nullable
65+
public OAuth2AuthorizedClient authorize(OAuth2AuthorizationContext context) {
66+
Assert.notNull(context, "context cannot be null");
67+
return this.authorizedClientProviders.stream()
68+
.map(authorizedClientProvider -> authorizedClientProvider.authorize(context))
69+
.filter(Objects::nonNull)
70+
.findFirst()
71+
.orElse(null);
72+
}
73+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright 2002-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.security.oauth2.client;
17+
18+
import org.junit.Test;
19+
import org.springframework.security.authentication.TestingAuthenticationToken;
20+
import org.springframework.security.core.Authentication;
21+
import org.springframework.security.oauth2.client.registration.TestClientRegistrations;
22+
import org.springframework.security.oauth2.core.TestOAuth2AccessTokens;
23+
24+
import java.util.Collections;
25+
26+
import static org.assertj.core.api.Assertions.assertThat;
27+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
28+
import static org.mockito.ArgumentMatchers.any;
29+
import static org.mockito.Mockito.mock;
30+
import static org.mockito.Mockito.when;
31+
32+
/**
33+
* Tests for {@link DelegatingOAuth2AuthorizedClientProvider}.
34+
*
35+
* @author Joe Grandja
36+
*/
37+
public class DelegatingOAuth2AuthorizedClientProviderTests {
38+
39+
@Test
40+
public void constructorWhenProvidersIsEmptyThenThrowIllegalArgumentException() {
41+
assertThatThrownBy(() -> new DelegatingOAuth2AuthorizedClientProvider(new OAuth2AuthorizedClientProvider[0]))
42+
.isInstanceOf(IllegalArgumentException.class);
43+
assertThatThrownBy(() -> new DelegatingOAuth2AuthorizedClientProvider(Collections.emptyList()))
44+
.isInstanceOf(IllegalArgumentException.class);
45+
}
46+
47+
@Test
48+
public void authorizeWhenContextIsNullThenThrowIllegalArgumentException() {
49+
DelegatingOAuth2AuthorizedClientProvider delegate = new DelegatingOAuth2AuthorizedClientProvider(
50+
mock(OAuth2AuthorizedClientProvider.class));
51+
assertThatThrownBy(() -> delegate.authorize(null))
52+
.isInstanceOf(IllegalArgumentException.class)
53+
.hasMessage("context cannot be null");
54+
}
55+
56+
@Test
57+
public void authorizeWhenProviderCanAuthorizeThenReturnAuthorizedClient() {
58+
Authentication principal = new TestingAuthenticationToken("principal", "password");
59+
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(
60+
TestClientRegistrations.clientRegistration().build(), principal.getName(), TestOAuth2AccessTokens.noScopes());
61+
62+
OAuth2AuthorizedClientProvider authorizedClientProvider = mock(OAuth2AuthorizedClientProvider.class);
63+
when(authorizedClientProvider.authorize(any())).thenReturn(authorizedClient);
64+
65+
DelegatingOAuth2AuthorizedClientProvider delegate = new DelegatingOAuth2AuthorizedClientProvider(
66+
mock(OAuth2AuthorizedClientProvider.class), mock(OAuth2AuthorizedClientProvider.class), authorizedClientProvider);
67+
OAuth2AuthorizationContext context = OAuth2AuthorizationContext.reauthorize(authorizedClient).principal(principal).build();
68+
OAuth2AuthorizedClient reauthorizedClient = delegate.authorize(context);
69+
assertThat(reauthorizedClient).isSameAs(authorizedClient);
70+
}
71+
72+
@Test
73+
public void authorizeWhenProviderCantAuthorizeThenReturnNull() {
74+
OAuth2AuthorizationContext context = OAuth2AuthorizationContext
75+
.authorize(TestClientRegistrations.clientRegistration().build())
76+
.principal(new TestingAuthenticationToken("principal", "password"))
77+
.build();
78+
79+
DelegatingOAuth2AuthorizedClientProvider delegate = new DelegatingOAuth2AuthorizedClientProvider(
80+
mock(OAuth2AuthorizedClientProvider.class), mock(OAuth2AuthorizedClientProvider.class));
81+
assertThat(delegate.authorize(context)).isNull();
82+
}
83+
}

0 commit comments

Comments
 (0)