dynreg: filter requested grant types

This commit introduces filtering on requested grant types for
dynamically registered clients.

Since extension on the library could support additional grant types,
here we want to be strict about known grant types that cannot be
requested at dynamic client registration (or update) time, but at the
same time we want to preserve grant types that could have been granted
to a client by an administrator.

So at client registration time the list of requested grant types is
filtered to only allow grant types currently enabled for dynamically
registered clients.

OTOH, at client update time the same filtering is implemented while at
the same time preserving grant types assigned the client in other ways.
pull/1611/head
Andrea Ceccanti 2018-04-12 15:24:52 +02:00
parent 35843a5410
commit f09efec031
1 changed files with 61 additions and 48 deletions

View File

@ -17,6 +17,45 @@
*******************************************************************************/
package org.mitre.openid.connect.web;
import static org.mitre.oauth2.model.RegisteredClientFields.APPLICATION_TYPE;
import static org.mitre.oauth2.model.RegisteredClientFields.CLAIMS_REDIRECT_URIS;
import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_ID;
import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_ID_ISSUED_AT;
import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_NAME;
import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_SECRET;
import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_SECRET_EXPIRES_AT;
import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_URI;
import static org.mitre.oauth2.model.RegisteredClientFields.CONTACTS;
import static org.mitre.oauth2.model.RegisteredClientFields.DEFAULT_ACR_VALUES;
import static org.mitre.oauth2.model.RegisteredClientFields.DEFAULT_MAX_AGE;
import static org.mitre.oauth2.model.RegisteredClientFields.GRANT_TYPES;
import static org.mitre.oauth2.model.RegisteredClientFields.ID_TOKEN_ENCRYPTED_RESPONSE_ALG;
import static org.mitre.oauth2.model.RegisteredClientFields.ID_TOKEN_ENCRYPTED_RESPONSE_ENC;
import static org.mitre.oauth2.model.RegisteredClientFields.ID_TOKEN_SIGNED_RESPONSE_ALG;
import static org.mitre.oauth2.model.RegisteredClientFields.INITIATE_LOGIN_URI;
import static org.mitre.oauth2.model.RegisteredClientFields.JWKS;
import static org.mitre.oauth2.model.RegisteredClientFields.JWKS_URI;
import static org.mitre.oauth2.model.RegisteredClientFields.LOGO_URI;
import static org.mitre.oauth2.model.RegisteredClientFields.POLICY_URI;
import static org.mitre.oauth2.model.RegisteredClientFields.POST_LOGOUT_REDIRECT_URIS;
import static org.mitre.oauth2.model.RegisteredClientFields.REDIRECT_URIS;
import static org.mitre.oauth2.model.RegisteredClientFields.REGISTRATION_ACCESS_TOKEN;
import static org.mitre.oauth2.model.RegisteredClientFields.REGISTRATION_CLIENT_URI;
import static org.mitre.oauth2.model.RegisteredClientFields.REQUEST_OBJECT_SIGNING_ALG;
import static org.mitre.oauth2.model.RegisteredClientFields.REQUEST_URIS;
import static org.mitre.oauth2.model.RegisteredClientFields.REQUIRE_AUTH_TIME;
import static org.mitre.oauth2.model.RegisteredClientFields.RESPONSE_TYPES;
import static org.mitre.oauth2.model.RegisteredClientFields.SCOPE;
import static org.mitre.oauth2.model.RegisteredClientFields.SECTOR_IDENTIFIER_URI;
import static org.mitre.oauth2.model.RegisteredClientFields.SOFTWARE_STATEMENT;
import static org.mitre.oauth2.model.RegisteredClientFields.SUBJECT_TYPE;
import static org.mitre.oauth2.model.RegisteredClientFields.TOKEN_ENDPOINT_AUTH_METHOD;
import static org.mitre.oauth2.model.RegisteredClientFields.TOKEN_ENDPOINT_AUTH_SIGNING_ALG;
import static org.mitre.oauth2.model.RegisteredClientFields.TOS_URI;
import static org.mitre.oauth2.model.RegisteredClientFields.USERINFO_ENCRYPTED_RESPONSE_ALG;
import static org.mitre.oauth2.model.RegisteredClientFields.USERINFO_ENCRYPTED_RESPONSE_ENC;
import static org.mitre.oauth2.model.RegisteredClientFields.USERINFO_SIGNED_RESPONSE_ALG;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.util.Date;
@ -71,45 +110,6 @@ import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jwt.JWTClaimsSet;
import static org.mitre.oauth2.model.RegisteredClientFields.APPLICATION_TYPE;
import static org.mitre.oauth2.model.RegisteredClientFields.CLAIMS_REDIRECT_URIS;
import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_ID;
import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_ID_ISSUED_AT;
import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_NAME;
import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_SECRET;
import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_SECRET_EXPIRES_AT;
import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_URI;
import static org.mitre.oauth2.model.RegisteredClientFields.CONTACTS;
import static org.mitre.oauth2.model.RegisteredClientFields.DEFAULT_ACR_VALUES;
import static org.mitre.oauth2.model.RegisteredClientFields.DEFAULT_MAX_AGE;
import static org.mitre.oauth2.model.RegisteredClientFields.GRANT_TYPES;
import static org.mitre.oauth2.model.RegisteredClientFields.ID_TOKEN_ENCRYPTED_RESPONSE_ALG;
import static org.mitre.oauth2.model.RegisteredClientFields.ID_TOKEN_ENCRYPTED_RESPONSE_ENC;
import static org.mitre.oauth2.model.RegisteredClientFields.ID_TOKEN_SIGNED_RESPONSE_ALG;
import static org.mitre.oauth2.model.RegisteredClientFields.INITIATE_LOGIN_URI;
import static org.mitre.oauth2.model.RegisteredClientFields.JWKS;
import static org.mitre.oauth2.model.RegisteredClientFields.JWKS_URI;
import static org.mitre.oauth2.model.RegisteredClientFields.LOGO_URI;
import static org.mitre.oauth2.model.RegisteredClientFields.POLICY_URI;
import static org.mitre.oauth2.model.RegisteredClientFields.POST_LOGOUT_REDIRECT_URIS;
import static org.mitre.oauth2.model.RegisteredClientFields.REDIRECT_URIS;
import static org.mitre.oauth2.model.RegisteredClientFields.REGISTRATION_ACCESS_TOKEN;
import static org.mitre.oauth2.model.RegisteredClientFields.REGISTRATION_CLIENT_URI;
import static org.mitre.oauth2.model.RegisteredClientFields.REQUEST_OBJECT_SIGNING_ALG;
import static org.mitre.oauth2.model.RegisteredClientFields.REQUEST_URIS;
import static org.mitre.oauth2.model.RegisteredClientFields.REQUIRE_AUTH_TIME;
import static org.mitre.oauth2.model.RegisteredClientFields.RESPONSE_TYPES;
import static org.mitre.oauth2.model.RegisteredClientFields.SCOPE;
import static org.mitre.oauth2.model.RegisteredClientFields.SECTOR_IDENTIFIER_URI;
import static org.mitre.oauth2.model.RegisteredClientFields.SOFTWARE_STATEMENT;
import static org.mitre.oauth2.model.RegisteredClientFields.SUBJECT_TYPE;
import static org.mitre.oauth2.model.RegisteredClientFields.TOKEN_ENDPOINT_AUTH_METHOD;
import static org.mitre.oauth2.model.RegisteredClientFields.TOKEN_ENDPOINT_AUTH_SIGNING_ALG;
import static org.mitre.oauth2.model.RegisteredClientFields.TOS_URI;
import static org.mitre.oauth2.model.RegisteredClientFields.USERINFO_ENCRYPTED_RESPONSE_ALG;
import static org.mitre.oauth2.model.RegisteredClientFields.USERINFO_ENCRYPTED_RESPONSE_ENC;
import static org.mitre.oauth2.model.RegisteredClientFields.USERINFO_SIGNED_RESPONSE_ALG;
@Controller
@RequestMapping(value = DynamicClientRegistrationEndpoint.URL)
public class DynamicClientRegistrationEndpoint {
@ -143,6 +143,10 @@ public class DynamicClientRegistrationEndpoint {
*/
private static final Logger logger = LoggerFactory.getLogger(DynamicClientRegistrationEndpoint.class);
public static final ImmutableSet<String> ALLOWED_GRANT_TYPES = ImmutableSet.of(
"authorization_code", "implicit", "client_credentials", "refresh_token",
"urn:ietf:params:oauth:grant_type:redelegate",
"urn:ietf:params:oauth:grant-type:device_code");
/**
* Create a new Client, issue a client ID, and create a registration access token.
* @param jsonString
@ -175,6 +179,10 @@ public class DynamicClientRegistrationEndpoint {
newClient.setClientId(null);
newClient.setClientSecret(null);
Set<String> requestedGrantTypes = newClient.getGrantTypes();
requestedGrantTypes.retainAll(ALLOWED_GRANT_TYPES);
newClient.setGrantTypes(requestedGrantTypes);
// do validation on the fields
try {
newClient = validateSoftwareStatement(newClient); // need to handle the software statement first because it might override requested values
@ -353,6 +361,13 @@ public class DynamicClientRegistrationEndpoint {
newClient.setCreatedAt(oldClient.getCreatedAt());
newClient.setReuseRefreshToken(oldClient.isReuseRefreshToken());
Set<String> requestedGrantTypes = newClient.getGrantTypes();
requestedGrantTypes.retainAll(ALLOWED_GRANT_TYPES);
newClient.setGrantTypes(requestedGrantTypes);
Set<String> oldClientGrantedGrantTypes = oldClient.getGrantTypes();
oldClientGrantedGrantTypes.removeAll(ALLOWED_GRANT_TYPES);
// do validation on the fields
try {
newClient = validateSoftwareStatement(newClient); // need to handle the software statement first because it might override requested values
@ -370,6 +385,11 @@ public class DynamicClientRegistrationEndpoint {
}
try {
if (!oldClientGrantedGrantTypes.isEmpty()) {
newClient.getGrantTypes().addAll(oldClientGrantedGrantTypes);
}
// save the client
ClientDetailsEntity savedClient = clientService.updateClient(oldClient, newClient);
@ -459,7 +479,8 @@ public class DynamicClientRegistrationEndpoint {
return newClient;
}
private ClientDetailsEntity validateGrantTypes(ClientDetailsEntity newClient) throws ValidationException {
private ClientDetailsEntity validateGrantTypes(ClientDetailsEntity newClient)
throws ValidationException {
// set default grant types if needed
if (newClient.getGrantTypes() == null || newClient.getGrantTypes().isEmpty()) {
if (newClient.getScope().contains("offline_access")) { // client asked for offline access
@ -474,14 +495,6 @@ public class DynamicClientRegistrationEndpoint {
}
}
// filter out unknown grant types
// TODO: make this a pluggable service
Set<String> requestedGrantTypes = new HashSet<>(newClient.getGrantTypes());
requestedGrantTypes.retainAll(
ImmutableSet.of("authorization_code", "implicit",
"password", "client_credentials", "refresh_token",
"urn:ietf:params:oauth:grant_type:redelegate"));
// don't allow "password" grant type for dynamic registration
if (newClient.getGrantTypes().contains("password")) {
// return an error, you can't dynamically register for the password grant