extracted validation exception, refactored protected resource registration endpoint to use this format
parent
b7a8bbdddc
commit
52e53ba219
|
@ -0,0 +1,47 @@
|
||||||
|
package org.mitre.openid.connect.exception;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown by utility methods when a client fails to validate. Contains information
|
||||||
|
* to be returned.
|
||||||
|
* @author jricher
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class ValidationException extends Exception {
|
||||||
|
private static final long serialVersionUID = 1820497072989294627L;
|
||||||
|
|
||||||
|
private String error;
|
||||||
|
private String errorDescription;
|
||||||
|
private HttpStatus status;
|
||||||
|
public ValidationException(String error, String errorDescription,
|
||||||
|
HttpStatus status) {
|
||||||
|
this.error = error;
|
||||||
|
this.errorDescription = errorDescription;
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
public String getError() {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
public void setError(String error) {
|
||||||
|
this.error = error;
|
||||||
|
}
|
||||||
|
public String getErrorDescription() {
|
||||||
|
return errorDescription;
|
||||||
|
}
|
||||||
|
public void setErrorDescription(String errorDescription) {
|
||||||
|
this.errorDescription = errorDescription;
|
||||||
|
}
|
||||||
|
public HttpStatus getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
public void setStatus(HttpStatus status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ValidationException [error=" + error + ", errorDescription="
|
||||||
|
+ errorDescription + ", status=" + status + "]";
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,6 +32,7 @@ import org.mitre.oauth2.service.OAuth2TokenEntityService;
|
||||||
import org.mitre.oauth2.service.SystemScopeService;
|
import org.mitre.oauth2.service.SystemScopeService;
|
||||||
import org.mitre.openid.connect.ClientDetailsEntityJsonProcessor;
|
import org.mitre.openid.connect.ClientDetailsEntityJsonProcessor;
|
||||||
import org.mitre.openid.connect.config.ConfigurationPropertiesBean;
|
import org.mitre.openid.connect.config.ConfigurationPropertiesBean;
|
||||||
|
import org.mitre.openid.connect.exception.ValidationException;
|
||||||
import org.mitre.openid.connect.service.BlacklistedSiteService;
|
import org.mitre.openid.connect.service.BlacklistedSiteService;
|
||||||
import org.mitre.openid.connect.service.OIDCTokenService;
|
import org.mitre.openid.connect.service.OIDCTokenService;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -510,41 +511,4 @@ public class ClientDynamicRegistrationEndpoint {
|
||||||
return newClient;
|
return newClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Thrown by utility methods when a client fails to validate. Contains information
|
|
||||||
* to be returned.
|
|
||||||
* @author jricher
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
private class ValidationException extends Exception {
|
|
||||||
private String error;
|
|
||||||
private String errorDescription;
|
|
||||||
private HttpStatus status;
|
|
||||||
public ValidationException(String error, String errorDescription,
|
|
||||||
HttpStatus status) {
|
|
||||||
this.error = error;
|
|
||||||
this.errorDescription = errorDescription;
|
|
||||||
this.status = status;
|
|
||||||
}
|
|
||||||
public String getError() {
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
public void setError(String error) {
|
|
||||||
this.error = error;
|
|
||||||
}
|
|
||||||
public String getErrorDescription() {
|
|
||||||
return errorDescription;
|
|
||||||
}
|
|
||||||
public void setErrorDescription(String errorDescription) {
|
|
||||||
this.errorDescription = errorDescription;
|
|
||||||
}
|
|
||||||
public HttpStatus getStatus() {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
public void setStatus(HttpStatus status) {
|
|
||||||
this.status = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ import org.mitre.oauth2.service.OAuth2TokenEntityService;
|
||||||
import org.mitre.oauth2.service.SystemScopeService;
|
import org.mitre.oauth2.service.SystemScopeService;
|
||||||
import org.mitre.openid.connect.ClientDetailsEntityJsonProcessor;
|
import org.mitre.openid.connect.ClientDetailsEntityJsonProcessor;
|
||||||
import org.mitre.openid.connect.config.ConfigurationPropertiesBean;
|
import org.mitre.openid.connect.config.ConfigurationPropertiesBean;
|
||||||
|
import org.mitre.openid.connect.exception.ValidationException;
|
||||||
import org.mitre.openid.connect.service.BlacklistedSiteService;
|
import org.mitre.openid.connect.service.BlacklistedSiteService;
|
||||||
import org.mitre.openid.connect.service.OIDCTokenService;
|
import org.mitre.openid.connect.service.OIDCTokenService;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -110,40 +111,24 @@ public class ProtectedResourceRegistrationEndpoint {
|
||||||
newClient.setClientId(null);
|
newClient.setClientId(null);
|
||||||
newClient.setClientSecret(null);
|
newClient.setClientSecret(null);
|
||||||
|
|
||||||
// set of scopes that are OK for clients to dynamically register for
|
// do validation on the fields
|
||||||
Set<SystemScope> dynScopes = scopeService.getDynReg();
|
try {
|
||||||
|
newClient = validateScopes(newClient);
|
||||||
// scopes that the client is asking for
|
newClient = validateAuth(newClient);
|
||||||
Set<SystemScope> requestedScopes = scopeService.fromStrings(newClient.getScope());
|
} catch (ValidationException ve) {
|
||||||
|
// validation failed, return an error
|
||||||
// the scopes that the client can have must be a subset of the dynamically allowed scopes
|
m.addAttribute("error", ve.getError());
|
||||||
Set<SystemScope> allowedScopes = Sets.intersection(dynScopes, requestedScopes);
|
m.addAttribute("errorMessage", ve.getErrorDescription());
|
||||||
|
m.addAttribute("code", ve.getStatus());
|
||||||
// if the client didn't ask for any, give them the defaults
|
return "jsonErrorView";
|
||||||
if (allowedScopes == null || allowedScopes.isEmpty()) {
|
|
||||||
allowedScopes = scopeService.getDefaults();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
newClient.setScope(scopeService.toStrings(allowedScopes));
|
|
||||||
|
|
||||||
|
|
||||||
// no grant types are allowed
|
// no grant types are allowed
|
||||||
newClient.setGrantTypes(new HashSet<String>());
|
newClient.setGrantTypes(new HashSet<String>());
|
||||||
newClient.setResponseTypes(new HashSet<String>());
|
newClient.setResponseTypes(new HashSet<String>());
|
||||||
newClient.setRedirectUris(new HashSet<String>());
|
newClient.setRedirectUris(new HashSet<String>());
|
||||||
|
|
||||||
if (newClient.getTokenEndpointAuthMethod() == null) {
|
|
||||||
newClient.setTokenEndpointAuthMethod(AuthMethod.SECRET_BASIC);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newClient.getTokenEndpointAuthMethod() == AuthMethod.SECRET_BASIC ||
|
|
||||||
newClient.getTokenEndpointAuthMethod() == AuthMethod.SECRET_JWT ||
|
|
||||||
newClient.getTokenEndpointAuthMethod() == AuthMethod.SECRET_POST) {
|
|
||||||
|
|
||||||
// we need to generate a secret
|
|
||||||
newClient = clientService.generateClientSecret(newClient);
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't issue tokens to this client
|
// don't issue tokens to this client
|
||||||
newClient.setAccessTokenValiditySeconds(0);
|
newClient.setAccessTokenValiditySeconds(0);
|
||||||
newClient.setIdTokenValiditySeconds(0);
|
newClient.setIdTokenValiditySeconds(0);
|
||||||
|
@ -207,6 +192,26 @@ public class ProtectedResourceRegistrationEndpoint {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ClientDetailsEntity validateScopes(ClientDetailsEntity newClient) throws ValidationException {
|
||||||
|
// set of scopes that are OK for clients to dynamically register for
|
||||||
|
Set<SystemScope> dynScopes = scopeService.getDynReg();
|
||||||
|
|
||||||
|
// scopes that the client is asking for
|
||||||
|
Set<SystemScope> requestedScopes = scopeService.fromStrings(newClient.getScope());
|
||||||
|
|
||||||
|
// the scopes that the client can have must be a subset of the dynamically allowed scopes
|
||||||
|
Set<SystemScope> allowedScopes = Sets.intersection(dynScopes, requestedScopes);
|
||||||
|
|
||||||
|
// if the client didn't ask for any, give them the defaults
|
||||||
|
if (allowedScopes == null || allowedScopes.isEmpty()) {
|
||||||
|
allowedScopes = scopeService.getDefaults();
|
||||||
|
}
|
||||||
|
|
||||||
|
newClient.setScope(scopeService.toStrings(allowedScopes));
|
||||||
|
|
||||||
|
return newClient;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the meta information for a client.
|
* Get the meta information for a client.
|
||||||
* @param clientId
|
* @param clientId
|
||||||
|
@ -284,28 +289,51 @@ public class ProtectedResourceRegistrationEndpoint {
|
||||||
// a client can't ask to update its own client secret to any particular value
|
// a client can't ask to update its own client secret to any particular value
|
||||||
newClient.setClientSecret(oldClient.getClientSecret());
|
newClient.setClientSecret(oldClient.getClientSecret());
|
||||||
|
|
||||||
// we need to copy over all of the local and SECOAUTH fields
|
// no grant types are allowed
|
||||||
newClient.setAccessTokenValiditySeconds(oldClient.getAccessTokenValiditySeconds());
|
newClient.setGrantTypes(new HashSet<String>());
|
||||||
newClient.setIdTokenValiditySeconds(oldClient.getIdTokenValiditySeconds());
|
newClient.setResponseTypes(new HashSet<String>());
|
||||||
newClient.setRefreshTokenValiditySeconds(oldClient.getRefreshTokenValiditySeconds());
|
newClient.setRedirectUris(new HashSet<String>());
|
||||||
newClient.setDynamicallyRegistered(true); // it's still dynamically registered
|
|
||||||
newClient.setAllowIntrospection(oldClient.isAllowIntrospection());
|
|
||||||
newClient.setAuthorities(oldClient.getAuthorities());
|
|
||||||
newClient.setClientDescription(oldClient.getClientDescription());
|
|
||||||
newClient.setCreatedAt(oldClient.getCreatedAt());
|
|
||||||
newClient.setReuseRefreshToken(oldClient.isReuseRefreshToken());
|
|
||||||
|
|
||||||
// set of scopes that are OK for clients to dynamically register for
|
// don't issue tokens to this client
|
||||||
Set<SystemScope> dynScopes = scopeService.getDynReg();
|
newClient.setAccessTokenValiditySeconds(0);
|
||||||
|
newClient.setIdTokenValiditySeconds(0);
|
||||||
|
newClient.setRefreshTokenValiditySeconds(0);
|
||||||
|
|
||||||
// scopes that the client is asking for
|
// clear out unused fields
|
||||||
Set<SystemScope> requestedScopes = scopeService.fromStrings(newClient.getScope());
|
newClient.setDefaultACRvalues(new HashSet<String>());
|
||||||
|
newClient.setDefaultMaxAge(null);
|
||||||
|
newClient.setIdTokenEncryptedResponseAlg(null);
|
||||||
|
newClient.setIdTokenEncryptedResponseEnc(null);
|
||||||
|
newClient.setIdTokenSignedResponseAlg(null);
|
||||||
|
newClient.setInitiateLoginUri(null);
|
||||||
|
newClient.setPostLogoutRedirectUri(null);
|
||||||
|
newClient.setRequestObjectSigningAlg(null);
|
||||||
|
newClient.setRequireAuthTime(null);
|
||||||
|
newClient.setReuseRefreshToken(false);
|
||||||
|
newClient.setSectorIdentifierUri(null);
|
||||||
|
newClient.setSubjectType(null);
|
||||||
|
newClient.setUserInfoEncryptedResponseAlg(null);
|
||||||
|
newClient.setUserInfoEncryptedResponseEnc(null);
|
||||||
|
newClient.setUserInfoSignedResponseAlg(null);
|
||||||
|
|
||||||
// the scopes that the client can have must be a subset of the dynamically allowed scopes
|
// this client has been dynamically registered (obviously)
|
||||||
Set<SystemScope> allowedScopes = Sets.intersection(dynScopes, requestedScopes);
|
newClient.setDynamicallyRegistered(true);
|
||||||
|
|
||||||
|
// this client has access to the introspection endpoint
|
||||||
|
newClient.setAllowIntrospection(true);
|
||||||
|
|
||||||
|
// do validation on the fields
|
||||||
|
try {
|
||||||
|
newClient = validateScopes(newClient);
|
||||||
|
newClient = validateAuth(newClient);
|
||||||
|
} catch (ValidationException ve) {
|
||||||
|
// validation failed, return an error
|
||||||
|
m.addAttribute("error", ve.getError());
|
||||||
|
m.addAttribute("errorMessage", ve.getErrorDescription());
|
||||||
|
m.addAttribute("code", ve.getStatus());
|
||||||
|
return "jsonErrorView";
|
||||||
|
}
|
||||||
|
|
||||||
// make sure that the client doesn't ask for scopes it can't have
|
|
||||||
newClient.setScope(scopeService.toStrings(allowedScopes));
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// save the client
|
// save the client
|
||||||
|
@ -374,4 +402,19 @@ public class ProtectedResourceRegistrationEndpoint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ClientDetailsEntity validateAuth(ClientDetailsEntity newClient) throws ValidationException {
|
||||||
|
if (newClient.getTokenEndpointAuthMethod() == null) {
|
||||||
|
newClient.setTokenEndpointAuthMethod(AuthMethod.SECRET_BASIC);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newClient.getTokenEndpointAuthMethod() == AuthMethod.SECRET_BASIC ||
|
||||||
|
newClient.getTokenEndpointAuthMethod() == AuthMethod.SECRET_JWT ||
|
||||||
|
newClient.getTokenEndpointAuthMethod() == AuthMethod.SECRET_POST) {
|
||||||
|
|
||||||
|
// we need to generate a secret
|
||||||
|
newClient = clientService.generateClientSecret(newClient);
|
||||||
|
}
|
||||||
|
return newClient;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue