Skip to content

Commit 0732682

Browse files
author
Dave Syer
committed
SECOAUTH-171: Consolidate TokenGranter logic
1 parent 414f932 commit 0732682

File tree

13 files changed

+226
-335
lines changed

13 files changed

+226
-335
lines changed

samples/oauth2/sparklr/src/test/java/org/springframework/security/oauth2/provider/ServerRunning.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
* @author Dave Syer
6161
*
6262
*/
63+
@SuppressWarnings("deprecation")
6364
public class ServerRunning extends TestWatchman {
6465

6566
private static Log logger = LogFactory.getLog(ServerRunning.class);

samples/oauth2/sparklr/src/test/java/org/springframework/security/oauth2/provider/TestAuthorizationCodeProvider.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ public void testInvalidScopeProvided() throws Exception {
455455

456456
URI uri = serverRunning.buildUri("/sparklr2/oauth/authorize").queryParam("response_type", "code")
457457
.queryParam("state", "mystateid").queryParam("client_id", "my-client-with-registered-redirect")
458-
.queryParam("scope", "read").build();
458+
.queryParam("scope", "nonexistent").build();
459459
String location = null;
460460
try {
461461
userAgent.getPage(uri.toString());
@@ -514,7 +514,6 @@ else if ("state".equals(token)) {
514514
MultiValueMap<String, String> formData = new LinkedMultiValueMap<String, String>();
515515
formData.add("grant_type", "authorization_code");
516516
formData.add("client_id", "my-client-with-registered-redirect");
517-
formData.add("scope", "read");
518517
formData.add("code", code);
519518

520519
ResponseEntity<String> response = serverRunning.postForString("/sparklr2/oauth/token", formData);

samples/oauth2/sparklr/src/test/java/org/springframework/security/oauth2/provider/TestNativeApplicationProvider.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,8 @@ public void testSecretProvidedInHeader() throws Exception {
149149
public void testInvalidGrantType() throws Exception {
150150

151151
MultiValueMap<String, String> formData = new LinkedMultiValueMap<String, String>();
152-
formData.add("grant_type", "authorization_code");
153-
formData.add("client_id", "my-trusted-client");
152+
formData.add("grant_type", "password");
153+
formData.add("client_id", "my-untrusted-client-with-registered-redirect");
154154
formData.add("username", "marissa");
155155
formData.add("password", "koala");
156156
ResponseEntity<String> response = serverRunning.postForString("/sparklr2/oauth/token", formData);
@@ -165,7 +165,7 @@ public void testInvalidGrantType() throws Exception {
165165
try {
166166
throw serializationService.deserializeJsonError(new ByteArrayInputStream(response.getBody().getBytes()));
167167
} catch (OAuth2Exception e) {
168-
assertEquals("invalid_request", e.getOAuth2ErrorCode());
168+
assertEquals("invalid_grant", e.getOAuth2ErrorCode());
169169
}
170170
}
171171

spring-security-oauth2/src/main/java/org/springframework/security/oauth2/config/AuthorizationServerBeanDefinitionParser.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ protected AbstractBeanDefinition parseEndpointAndReturnFilter(Element element, P
4848
String clientDetailsRef = element.getAttribute("client-details-service-ref");
4949
String tokenEndpointUrl = element.getAttribute("token-endpoint-url");
5050
String authorizationEndpointUrl = element.getAttribute("authorization-endpoint-url");
51-
String defaultGrantType = element.getAttribute("default-grant-type");
5251
String tokenGranterRef = element.getAttribute("token-granter-ref");
5352
String redirectStrategyRef = element.getAttribute("redirect-strategy-ref");
5453

@@ -192,9 +191,6 @@ protected AbstractBeanDefinition parseEndpointAndReturnFilter(Element element, P
192191

193192
// configure the token endpoint
194193
BeanDefinitionBuilder tokenEndpointBean = BeanDefinitionBuilder.rootBeanDefinition(TokenEndpoint.class);
195-
if (StringUtils.hasText(defaultGrantType)) {
196-
tokenEndpointBean.addPropertyValue("defaultGrantType", defaultGrantType);
197-
}
198194
tokenEndpointBean.addPropertyReference("tokenGranter", tokenGranterRef);
199195
parserContext.getRegistry().registerBeanDefinition("tokenEndpoint", tokenEndpointBean.getBeanDefinition());
200196

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2006-2011 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5+
* the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*/
13+
package org.springframework.security.oauth2.provider;
14+
15+
import java.util.Map;
16+
import java.util.Set;
17+
18+
import org.springframework.security.authentication.encoding.PasswordEncoder;
19+
import org.springframework.security.oauth2.common.OAuth2AccessToken;
20+
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
21+
22+
/**
23+
* @author Dave Syer
24+
*
25+
*/
26+
public abstract class AbstractTokenGranter implements TokenGranter {
27+
28+
private final AuthorizationServerTokenServices tokenServices;
29+
30+
private final ClientCredentialsChecker clientCredentialsChecker;
31+
32+
private final String grantType;
33+
34+
protected AbstractTokenGranter(AuthorizationServerTokenServices tokenServices,
35+
ClientDetailsService clientDetailsService, String grantType) {
36+
this.grantType = grantType;
37+
this.clientCredentialsChecker = new ClientCredentialsChecker(clientDetailsService);
38+
this.tokenServices = tokenServices;
39+
}
40+
41+
public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
42+
clientCredentialsChecker.setPasswordEncoder(passwordEncoder);
43+
}
44+
45+
public OAuth2AccessToken grant(String grantType, Map<String, String> parameters, String clientId,
46+
String clientSecret, Set<String> scopes) {
47+
48+
if (!this.grantType.equals(grantType)) {
49+
return null;
50+
}
51+
52+
ClientToken clientToken = clientCredentialsChecker
53+
.validateCredentials(grantType, clientId, clientSecret, scopes);
54+
55+
return tokenServices.createAccessToken(getOAuth2Authentication(parameters, clientToken));
56+
57+
}
58+
59+
protected abstract OAuth2Authentication getOAuth2Authentication(Map<String, String> parameters,
60+
ClientToken clientToken);
61+
62+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright 2006-2011 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5+
* the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*/
13+
package org.springframework.security.oauth2.provider;
14+
15+
import java.util.HashSet;
16+
import java.util.List;
17+
import java.util.Set;
18+
19+
import org.springframework.security.authentication.encoding.PasswordEncoder;
20+
import org.springframework.security.authentication.encoding.PlaintextPasswordEncoder;
21+
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
22+
import org.springframework.security.oauth2.common.exceptions.InvalidScopeException;
23+
import org.springframework.security.oauth2.common.exceptions.UnauthorizedClientException;
24+
25+
/**
26+
* @author Dave Syer
27+
*
28+
*/
29+
public class ClientCredentialsChecker {
30+
31+
private final ClientDetailsService clientDetailsService;
32+
33+
private PasswordEncoder passwordEncoder = new PlaintextPasswordEncoder();
34+
35+
public ClientCredentialsChecker(ClientDetailsService clientDetailsService) {
36+
this.clientDetailsService = clientDetailsService;
37+
}
38+
39+
public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
40+
this.passwordEncoder = passwordEncoder;
41+
}
42+
43+
public ClientToken validateCredentials(String grantType, String clientId, String clientSecret) {
44+
return this.validateCredentials(grantType, clientId, clientSecret, null);
45+
}
46+
47+
public ClientToken validateCredentials(String grantType, String clientId, String clientSecret, Set<String> scopes) {
48+
49+
ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
50+
validateGrantType(grantType, clientDetails);
51+
if (scopes != null) {
52+
validateScope(clientDetails, scopes);
53+
}
54+
validateClient(clientDetails, clientSecret);
55+
56+
return new ClientToken(clientId, new HashSet<String>(clientDetails.getResourceIds()), clientSecret, scopes,
57+
clientDetails.getAuthorities());
58+
59+
}
60+
61+
private void validateClient(ClientDetails clientDetails, String clientSecret) {
62+
if (clientDetails.isSecretRequired()) {
63+
String assertedSecret = clientSecret;
64+
if (assertedSecret == null) {
65+
throw new UnauthorizedClientException("Client secret is required but not provided.");
66+
}
67+
else {
68+
Object salt = null;
69+
if (clientDetails instanceof SaltedClientSecret) {
70+
salt = ((SaltedClientSecret) clientDetails).getSalt();
71+
}
72+
73+
if (!passwordEncoder.isPasswordValid(clientDetails.getClientSecret(), assertedSecret, salt)) {
74+
throw new UnauthorizedClientException("Invalid client secret.");
75+
}
76+
}
77+
}
78+
}
79+
80+
private void validateScope(ClientDetails clientDetails, Set<String> scopes) {
81+
82+
if (clientDetails.isScoped()) {
83+
if (scopes.isEmpty()) {
84+
throw new InvalidScopeException("Invalid scope (none)");
85+
}
86+
List<String> validScope = clientDetails.getScope();
87+
for (String scope : scopes) {
88+
if (!validScope.contains(scope)) {
89+
throw new InvalidScopeException("Invalid scope: " + scope);
90+
}
91+
}
92+
}
93+
94+
}
95+
96+
private void validateGrantType(String grantType, ClientDetails clientDetails) {
97+
List<String> authorizedGrantTypes = clientDetails.getAuthorizedGrantTypes();
98+
if (authorizedGrantTypes != null && !authorizedGrantTypes.isEmpty()
99+
&& !authorizedGrantTypes.contains(grantType)) {
100+
throw new InvalidGrantException("Unauthorized grant type: " + grantType);
101+
}
102+
}
103+
104+
}

spring-security-oauth2/src/main/java/org/springframework/security/oauth2/provider/client/ClientCredentialsTokenGranter.java

Lines changed: 8 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -16,88 +16,30 @@
1616

1717
package org.springframework.security.oauth2.provider.client;
1818

19-
import java.util.HashSet;
20-
import java.util.List;
2119
import java.util.Map;
22-
import java.util.Set;
2320

24-
import org.springframework.security.authentication.encoding.PasswordEncoder;
25-
import org.springframework.security.authentication.encoding.PlaintextPasswordEncoder;
26-
import org.springframework.security.oauth2.common.OAuth2AccessToken;
27-
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
28-
import org.springframework.security.oauth2.common.exceptions.InvalidScopeException;
29-
import org.springframework.security.oauth2.common.exceptions.UnauthorizedClientException;
30-
import org.springframework.security.oauth2.provider.ClientDetails;
21+
import org.springframework.security.oauth2.provider.AbstractTokenGranter;
3122
import org.springframework.security.oauth2.provider.ClientDetailsService;
3223
import org.springframework.security.oauth2.provider.ClientToken;
3324
import org.springframework.security.oauth2.provider.OAuth2Authentication;
34-
import org.springframework.security.oauth2.provider.SaltedClientSecret;
35-
import org.springframework.security.oauth2.provider.TokenGranter;
3625
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
3726

3827
/**
3928
* @author Dave Syer
4029
*
4130
*/
42-
public class ClientCredentialsTokenGranter implements TokenGranter {
31+
public class ClientCredentialsTokenGranter extends AbstractTokenGranter {
4332

4433
private static final String GRANT_TYPE = "client_credentials";
45-
private final AuthorizationServerTokenServices tokenServices;
46-
private final ClientDetailsService clientDetailsService;
47-
private PasswordEncoder passwordEncoder = new PlaintextPasswordEncoder();
4834

49-
public ClientCredentialsTokenGranter(AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService) {
50-
this.tokenServices = tokenServices;
51-
this.clientDetailsService = clientDetailsService;
35+
public ClientCredentialsTokenGranter(AuthorizationServerTokenServices tokenServices,
36+
ClientDetailsService clientDetailsService) {
37+
super(tokenServices, clientDetailsService, GRANT_TYPE);
5238
}
5339

54-
public OAuth2AccessToken grant(String grantType, Map<String, String> parameters, String clientId,
55-
String clientSecret, Set<String> authorizationScope) {
56-
57-
if (!GRANT_TYPE.equals(grantType)) {
58-
return null;
59-
}
60-
61-
// TODO: move this out to a filter?
62-
ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
63-
if (clientDetails.isSecretRequired()) {
64-
String assertedSecret = clientSecret;
65-
if (assertedSecret == null) {
66-
throw new UnauthorizedClientException("Client secret is required but not provided.");
67-
} else {
68-
Object salt = null;
69-
if (clientDetails instanceof SaltedClientSecret) {
70-
salt = ((SaltedClientSecret) clientDetails).getSalt();
71-
}
72-
73-
if (!passwordEncoder.isPasswordValid(clientDetails.getClientSecret(), assertedSecret, salt)) {
74-
throw new UnauthorizedClientException("Invalid client secret.");
75-
}
76-
}
77-
}
78-
79-
if (clientDetails.isScoped()) {
80-
if (authorizationScope.isEmpty()) {
81-
throw new InvalidScopeException("Invalid scope (none)");
82-
}
83-
List<String> validScope = clientDetails.getScope();
84-
for (String scope : authorizationScope) {
85-
if (!validScope.contains(scope)) {
86-
throw new InvalidScopeException("Invalid scope: " + scope);
87-
}
88-
}
89-
}
90-
91-
List<String> authorizedGrantTypes = clientDetails.getAuthorizedGrantTypes();
92-
if (authorizedGrantTypes != null && !authorizedGrantTypes.isEmpty()
93-
&& !authorizedGrantTypes.contains(grantType)) {
94-
throw new InvalidGrantException("Unauthorized grant type: " + grantType);
95-
}
96-
97-
ClientToken clientAuth = new ClientToken(clientId, new HashSet<String>(
98-
clientDetails.getResourceIds()), clientSecret, authorizationScope, clientDetails.getAuthorities());
99-
return tokenServices.createAccessToken(new OAuth2Authentication(clientAuth, null));
100-
40+
@Override
41+
protected OAuth2Authentication getOAuth2Authentication(Map<String, String> parameters, ClientToken clientToken) {
42+
return new OAuth2Authentication(clientToken, null);
10143
}
10244

10345
}

0 commit comments

Comments
 (0)