scopes can now be set as "restricted" instead of needing to be set "allowDynReg", closes #747

pull/779/head
Justin Richer 2015-02-17 18:25:52 -05:00
parent 1caf5ef8bc
commit 593fac83cf
16 changed files with 200 additions and 115 deletions

View File

@ -46,8 +46,8 @@ public class SystemScope {
private String value; // scope value private String value; // scope value
private String description; // human-readable description private String description; // human-readable description
private String icon; // class of the icon to display on the auth page private String icon; // class of the icon to display on the auth page
private boolean allowDynReg = false; // can a dynamically registered client ask for this scope?
private boolean defaultScope = false; // is this a default scope for newly-registered clients? private boolean defaultScope = false; // is this a default scope for newly-registered clients?
private boolean restricted = false; // is this scope restricted to admin-only registration access?
private boolean structured = false; // is this a default scope for newly-registered clients? private boolean structured = false; // is this a default scope for newly-registered clients?
private String structuredParamDescription; private String structuredParamDescription;
private String structuredValue; private String structuredValue;
@ -124,20 +124,6 @@ public class SystemScope {
public void setIcon(String icon) { public void setIcon(String icon) {
this.icon = icon; this.icon = icon;
} }
/**
* @return the allowDynReg
*/
@Basic
@Column(name = "allow_dyn_reg")
public boolean isAllowDynReg() {
return allowDynReg;
}
/**
* @param allowDynReg the allowDynReg to set
*/
public void setAllowDynReg(boolean allowDynReg) {
this.allowDynReg = allowDynReg;
}
/** /**
* @return the defaultScope * @return the defaultScope
@ -155,6 +141,22 @@ public class SystemScope {
this.defaultScope = defaultScope; this.defaultScope = defaultScope;
} }
/**
* @return the restricted
*/
@Basic
@Column(name = "restricted")
public boolean isRestricted() {
return restricted;
}
/**
* @param restricted the restricted to set
*/
public void setRestricted(boolean restricted) {
this.restricted = restricted;
}
/** /**
* @return the isStructured status * @return the isStructured status
*/ */
@ -208,14 +210,19 @@ public class SystemScope {
public int hashCode() { public int hashCode() {
final int prime = 31; final int prime = 31;
int result = 1; int result = 1;
result = prime * result + (allowDynReg ? 1231 : 1237);
result = prime * result + (defaultScope ? 1231 : 1237); result = prime * result + (defaultScope ? 1231 : 1237);
result = prime * result + ((description == null) ? 0 : description.hashCode()); result = prime * result
+ ((description == null) ? 0 : description.hashCode());
result = prime * result + ((icon == null) ? 0 : icon.hashCode()); result = prime * result + ((icon == null) ? 0 : icon.hashCode());
result = prime * result + ((id == null) ? 0 : id.hashCode()); result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + (restricted ? 1231 : 1237);
result = prime * result + (structured ? 1231 : 1237); result = prime * result + (structured ? 1231 : 1237);
result = prime * result + ((structuredParamDescription == null) ? 0 : structuredParamDescription.hashCode()); result = prime
result = prime * result + ((structuredValue == null) ? 0 : structuredValue.hashCode()); * result
+ ((structuredParamDescription == null) ? 0
: structuredParamDescription.hashCode());
result = prime * result
+ ((structuredValue == null) ? 0 : structuredValue.hashCode());
result = prime * result + ((value == null) ? 0 : value.hashCode()); result = prime * result + ((value == null) ? 0 : value.hashCode());
return result; return result;
} }
@ -231,13 +238,10 @@ public class SystemScope {
if (obj == null) { if (obj == null) {
return false; return false;
} }
if (!(obj instanceof SystemScope)) { if (getClass() != obj.getClass()) {
return false; return false;
} }
SystemScope other = (SystemScope) obj; SystemScope other = (SystemScope) obj;
if (allowDynReg != other.allowDynReg) {
return false;
}
if (defaultScope != other.defaultScope) { if (defaultScope != other.defaultScope) {
return false; return false;
} }
@ -262,6 +266,9 @@ public class SystemScope {
} else if (!id.equals(other.id)) { } else if (!id.equals(other.id)) {
return false; return false;
} }
if (restricted != other.restricted) {
return false;
}
if (structured != other.structured) { if (structured != other.structured) {
return false; return false;
} }
@ -269,7 +276,8 @@ public class SystemScope {
if (other.structuredParamDescription != null) { if (other.structuredParamDescription != null) {
return false; return false;
} }
} else if (!structuredParamDescription.equals(other.structuredParamDescription)) { } else if (!structuredParamDescription
.equals(other.structuredParamDescription)) {
return false; return false;
} }
if (structuredValue == null) { if (structuredValue == null) {
@ -294,7 +302,11 @@ public class SystemScope {
*/ */
@Override @Override
public String toString() { public String toString() {
return "SystemScope [id=" + id + ", value=" + value + ", description=" + description + ", icon=" + icon + ", allowDynReg=" + allowDynReg + ", defaultScope=" + defaultScope + ", structured=" + structured + ", structuredParamDescription=" + structuredParamDescription + ", structuredValue=" return "SystemScope [id=" + id + ", value=" + value + ", description="
+ description + ", icon=" + icon + ", defaultScope="
+ defaultScope + ", restricted=" + restricted + ", structured="
+ structured + ", structuredParamDescription="
+ structuredParamDescription + ", structuredValue="
+ structuredValue + "]"; + structuredValue + "]";
} }

View File

@ -23,6 +23,8 @@ import java.util.Set;
import org.mitre.oauth2.model.SystemScope; import org.mitre.oauth2.model.SystemScope;
import com.google.common.collect.Sets;
/** /**
* @author jricher * @author jricher
* *
@ -35,6 +37,13 @@ public interface SystemScopeService {
public static final String REGISTRATION_TOKEN_SCOPE = "registration-token"; public static final String REGISTRATION_TOKEN_SCOPE = "registration-token";
public static final String RESOURCE_TOKEN_SCOPE = "resource-token"; public static final String RESOURCE_TOKEN_SCOPE = "resource-token";
public static final Set<SystemScope> reservedScopes =
Sets.newHashSet(
new SystemScope(ID_TOKEN_SCOPE),
new SystemScope(REGISTRATION_TOKEN_SCOPE),
new SystemScope(RESOURCE_TOKEN_SCOPE)
);
public Set<SystemScope> getAll(); public Set<SystemScope> getAll();
/** /**
@ -44,10 +53,25 @@ public interface SystemScopeService {
public Set<SystemScope> getDefaults(); public Set<SystemScope> getDefaults();
/** /**
* Get all scopes that are allowed for dynamic registration on this system * Get all the reserved system scopes. These can't be used
* by clients directly, but are instead tied to special system
* tokens like id tokens and registration access tokens.
*
* @return * @return
*/ */
public Set<SystemScope> getDynReg(); public Set<SystemScope> getReserved();
/**
* Get all the registered scopes that are restricted.
* @return
*/
public Set<SystemScope> getRestricted();
/**
* Get all the registered scopes that aren't restricted.
* @return
*/
public Set<SystemScope> getUnrestricted();
public SystemScope getById(Long id); public SystemScope getById(Long id);
@ -82,9 +106,18 @@ public interface SystemScopeService {
public boolean scopesMatch(Set<String> expected, Set<String> actual); public boolean scopesMatch(Set<String> expected, Set<String> actual);
/** /**
* Remove any system-restricted scopes from the set and return the result. * Remove any system-reserved or registered restricted scopes from the
* set and return the result.
* @param scopes * @param scopes
* @return * @return
*/ */
public Set<String> removeRestrictedScopes(Set<String> scopes); public Set<SystemScope> removeRestrictedAndReservedScopes(Set<SystemScope> scopes);
/**
* Remove any system-reserved scopes from the set and return the result.
* @param scopes
* @return
*/
public Set<SystemScope> removeReservedScopes(Set<SystemScope> scopes);
} }

View File

@ -10,23 +10,23 @@ START TRANSACTION;
-- Insert scope information into the temporary tables. -- Insert scope information into the temporary tables.
-- --
INSERT INTO system_scope_TEMP (scope, description, icon, allow_dyn_reg, default_scope, structured, structured_param_description) VALUES INSERT INTO system_scope_TEMP (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES
('openid', 'log in using your identity', 'user', true, true, false, null), ('openid', 'log in using your identity', 'user', false, true, false, null),
('profile', 'basic profile information', 'list-alt', true, true, false, null), ('profile', 'basic profile information', 'list-alt', false, true, false, null),
('email', 'email address', 'envelope', true, true, false, null), ('email', 'email address', 'envelope', false, true, false, null),
('address', 'physical address', 'home', true, true, false, null), ('address', 'physical address', 'home', false, true, false, null),
('phone', 'telephone number', 'bell', true, true, false, null), ('phone', 'telephone number', 'bell', false, true, false, null),
('offline_access', 'offline access', 'time', true, false, false, null); ('offline_access', 'offline access', 'time', false, false, false, null);
-- --
-- Merge the temporary scopes safely into the database. This is a two-step process to keep scopes from being created on every startup with a persistent store. -- Merge the temporary scopes safely into the database. This is a two-step process to keep scopes from being created on every startup with a persistent store.
-- --
MERGE INTO system_scope MERGE INTO system_scope
USING (SELECT scope, description, icon, allow_dyn_reg, default_scope, structured, structured_param_description FROM system_scope_TEMP) AS vals(scope, description, icon, allow_dyn_reg, default_scope, structured, structured_param_description) USING (SELECT scope, description, icon, restricted, default_scope, structured, structured_param_description FROM system_scope_TEMP) AS vals(scope, description, icon, restricted, default_scope, structured, structured_param_description)
ON vals.scope = system_scope.scope ON vals.scope = system_scope.scope
WHEN NOT MATCHED THEN WHEN NOT MATCHED THEN
INSERT (scope, description, icon, allow_dyn_reg, default_scope, structured, structured_param_description) VALUES(vals.scope, vals.description, vals.icon, vals.allow_dyn_reg, vals.default_scope, vals.structured, vals.structured_param_description); INSERT (scope, description, icon, restricted, default_scope, structured, structured_param_description) VALUES(vals.scope, vals.description, vals.icon, vals.restricted, vals.default_scope, vals.structured, vals.structured_param_description);
COMMIT; COMMIT;

View File

@ -170,7 +170,7 @@ CREATE TABLE IF NOT EXISTS system_scope (
scope VARCHAR(256) NOT NULL, scope VARCHAR(256) NOT NULL,
description VARCHAR(4096), description VARCHAR(4096),
icon VARCHAR(256), icon VARCHAR(256),
allow_dyn_reg BOOLEAN DEFAULT false NOT NULL, restricted BOOLEAN DEFAULT false NOT NULL,
default_scope BOOLEAN DEFAULT false NOT NULL, default_scope BOOLEAN DEFAULT false NOT NULL,
structured BOOLEAN DEFAULT false NOT NULL, structured BOOLEAN DEFAULT false NOT NULL,
structured_param_description VARCHAR(256), structured_param_description VARCHAR(256),

View File

@ -69,7 +69,7 @@ CREATE TEMPORARY TABLE IF NOT EXISTS system_scope_TEMP (
scope VARCHAR(256), scope VARCHAR(256),
description VARCHAR(4096), description VARCHAR(4096),
icon VARCHAR(256), icon VARCHAR(256),
allow_dyn_reg BOOLEAN, restricted BOOLEAN,
default_scope BOOLEAN, default_scope BOOLEAN,
structured BOOLEAN, structured BOOLEAN,
structured_param_description VARCHAR(256) structured_param_description VARCHAR(256)

View File

@ -170,7 +170,7 @@ CREATE TABLE IF NOT EXISTS system_scope (
scope VARCHAR(256) NOT NULL, scope VARCHAR(256) NOT NULL,
description VARCHAR(4096), description VARCHAR(4096),
icon VARCHAR(256), icon VARCHAR(256),
allow_dyn_reg BOOLEAN NOT NULL DEFAULT 0, restricted BOOLEAN NOT NULL DEFAULT 0,
default_scope BOOLEAN NOT NULL DEFAULT 0, default_scope BOOLEAN NOT NULL DEFAULT 0,
structured BOOLEAN NOT NULL DEFAULT 0, structured BOOLEAN NOT NULL DEFAULT 0,
structured_param_description VARCHAR(256), structured_param_description VARCHAR(256),

View File

@ -274,7 +274,7 @@ public class DiscoveryEndpoint {
//end_session_endpoint //end_session_endpoint
m.put("jwks_uri", baseUrl + "jwk"); m.put("jwks_uri", baseUrl + "jwk");
m.put("registration_endpoint", baseUrl + "register"); m.put("registration_endpoint", baseUrl + "register");
m.put("scopes_supported", scopeService.toStrings(scopeService.getDynReg())); // these are the scopes that you can dynamically register for, which is what matters for discovery m.put("scopes_supported", scopeService.toStrings(scopeService.getUnrestricted())); // these are the scopes that you can dynamically register for, which is what matters for discovery
m.put("response_types_supported", Lists.newArrayList("code", "token")); // we don't support these yet: , "id_token", "id_token token")); m.put("response_types_supported", Lists.newArrayList("code", "token")); // we don't support these yet: , "id_token", "id_token token"));
m.put("grant_types_supported", Lists.newArrayList("authorization_code", "implicit", "urn:ietf:params:oauth:grant-type:jwt-bearer", "client_credentials", "urn:ietf:params:oauth:grant_type:redelegate")); m.put("grant_types_supported", Lists.newArrayList("authorization_code", "implicit", "urn:ietf:params:oauth:grant-type:jwt-bearer", "client_credentials", "urn:ietf:params:oauth:grant_type:redelegate"));
//acr_values_supported //acr_values_supported
@ -311,6 +311,7 @@ public class DiscoveryEndpoint {
"email", "email",
"email_verified", "email_verified",
"phone_number", "phone_number",
"phone_number_verified",
"address" "address"
)); ));
m.put("service_documentation", baseUrl + "about"); m.put("service_documentation", baseUrl + "about");

View File

@ -121,8 +121,7 @@ public class DefaultOAuth2ClientDetailsEntityService implements ClientDetailsEnt
checkSectorIdentifierUri(client); checkSectorIdentifierUri(client);
// make sure a client doesn't get any special system scopes ensureNoReservedScopes(client);
client.setScope(scopeService.removeRestrictedScopes(client.getScope()));
ClientDetailsEntity c = clientRepository.saveClient(client); ClientDetailsEntity c = clientRepository.saveClient(client);
@ -131,6 +130,15 @@ public class DefaultOAuth2ClientDetailsEntityService implements ClientDetailsEnt
return c; return c;
} }
private void ensureNoReservedScopes(ClientDetailsEntity client) {
// make sure a client doesn't get any special system scopes
Set<SystemScope> requestedScope = scopeService.fromStrings(client.getScope());
requestedScope = scopeService.removeReservedScopes(requestedScope);
client.setScope(scopeService.toStrings(requestedScope));
}
private void checkSectorIdentifierUri(ClientDetailsEntity client) { private void checkSectorIdentifierUri(ClientDetailsEntity client) {
if (!Strings.isNullOrEmpty(client.getSectorIdentifierUri())) { if (!Strings.isNullOrEmpty(client.getSectorIdentifierUri())) {
try { try {
@ -246,7 +254,7 @@ public class DefaultOAuth2ClientDetailsEntityService implements ClientDetailsEnt
checkSectorIdentifierUri(newClient); checkSectorIdentifierUri(newClient);
// make sure a client doesn't get any special system scopes // make sure a client doesn't get any special system scopes
newClient.setScope(scopeService.removeRestrictedScopes(newClient.getScope())); ensureNoReservedScopes(newClient);
return clientRepository.updateClient(oldClient.getId(), newClient); return clientRepository.updateClient(oldClient.getId(), newClient);
} }

View File

@ -30,6 +30,7 @@ import org.mitre.oauth2.model.AuthenticationHolderEntity;
import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.model.ClientDetailsEntity;
import org.mitre.oauth2.model.OAuth2AccessTokenEntity; import org.mitre.oauth2.model.OAuth2AccessTokenEntity;
import org.mitre.oauth2.model.OAuth2RefreshTokenEntity; import org.mitre.oauth2.model.OAuth2RefreshTokenEntity;
import org.mitre.oauth2.model.SystemScope;
import org.mitre.oauth2.repository.AuthenticationHolderRepository; import org.mitre.oauth2.repository.AuthenticationHolderRepository;
import org.mitre.oauth2.repository.OAuth2TokenRepository; import org.mitre.oauth2.repository.OAuth2TokenRepository;
import org.mitre.oauth2.service.ClientDetailsEntityService; import org.mitre.oauth2.service.ClientDetailsEntityService;
@ -144,10 +145,12 @@ public class DefaultOAuth2ProviderTokenService implements OAuth2TokenEntityServi
// inherit the scope from the auth, but make a new set so it is // inherit the scope from the auth, but make a new set so it is
//not unmodifiable. Unmodifiables don't play nicely with Eclipselink, which //not unmodifiable. Unmodifiables don't play nicely with Eclipselink, which
//wants to use the clone operation. //wants to use the clone operation.
Set<String> scopes = Sets.newHashSet(clientAuth.getScope()); Set<SystemScope> scopes = scopeService.fromStrings(clientAuth.getScope());
// remove any of the special system scopes // remove any of the special system scopes
scopes = scopeService.removeRestrictedScopes(scopes); scopes = scopeService.removeRestrictedAndReservedScopes(scopes);
token.setScope(scopes);
token.setScope(scopeService.toStrings(scopes));
// make it expire if necessary // make it expire if necessary
if (client.getAccessTokenValiditySeconds() != null && client.getAccessTokenValiditySeconds() > 0) { if (client.getAccessTokenValiditySeconds() != null && client.getAccessTokenValiditySeconds() > 0) {
@ -263,19 +266,22 @@ public class DefaultOAuth2ProviderTokenService implements OAuth2TokenEntityServi
OAuth2AccessTokenEntity token = new OAuth2AccessTokenEntity(); OAuth2AccessTokenEntity token = new OAuth2AccessTokenEntity();
// get the stored scopes from the authentication holder's authorization request; these are the scopes associated with the refresh token // get the stored scopes from the authentication holder's authorization request; these are the scopes associated with the refresh token
Set<String> refreshScopes = new HashSet<String>(refreshToken.getAuthenticationHolder().getAuthentication().getOAuth2Request().getScope()); Set<String> refreshScopesRequested = new HashSet<String>(refreshToken.getAuthenticationHolder().getAuthentication().getOAuth2Request().getScope());
Set<SystemScope> refreshScopes = scopeService.fromStrings(refreshScopesRequested);
// remove any of the special system scopes // remove any of the special system scopes
refreshScopes = scopeService.removeRestrictedScopes(refreshScopes); refreshScopes = scopeService.removeRestrictedAndReservedScopes(refreshScopes);
Set<String> scopeRequested = authRequest.getScope() == null ? new HashSet<String>() : new HashSet<String>(authRequest.getScope());
Set<SystemScope> scope = scopeService.fromStrings(scopeRequested);
Set<String> scope = authRequest.getScope() == null ? new HashSet<String>() : new HashSet<String>(authRequest.getScope());
// remove any of the special system scopes // remove any of the special system scopes
scope = scopeService.removeRestrictedScopes(scope); scope = scopeService.removeRestrictedAndReservedScopes(scope);
if (scope != null && !scope.isEmpty()) { if (scope != null && !scope.isEmpty()) {
// ensure a proper subset of scopes // ensure a proper subset of scopes
if (refreshScopes != null && refreshScopes.containsAll(scope)) { if (refreshScopes != null && refreshScopes.containsAll(scope)) {
// set the scope of the new access token if requested // set the scope of the new access token if requested
token.setScope(scope); token.setScope(scopeService.toStrings(scope));
} else { } else {
String errorMsg = "Up-scoping is not allowed."; String errorMsg = "Up-scoping is not allowed.";
logger.error(errorMsg); logger.error(errorMsg);
@ -283,7 +289,7 @@ public class DefaultOAuth2ProviderTokenService implements OAuth2TokenEntityServi
} }
} else { } else {
// otherwise inherit the scope of the refresh token (if it's there -- this can return a null scope set) // otherwise inherit the scope of the refresh token (if it's there -- this can return a null scope set)
token.setScope(refreshScopes); token.setScope(scopeService.toStrings(refreshScopes));
} }
token.setClient(client); token.setClient(client);

View File

@ -56,21 +56,17 @@ public class DefaultSystemScopeService implements SystemScopeService {
} }
}; };
private Predicate<SystemScope> isRestricted = new Predicate<SystemScope>() {
private Predicate<SystemScope> isDynReg = new Predicate<SystemScope>() {
@Override @Override
public boolean apply(SystemScope input) { public boolean apply(SystemScope input) {
return (input != null && input.isAllowDynReg()); return (input != null && input.isRestricted());
} }
}; };
private Predicate<String> isRestricted = new Predicate<String>() { private Predicate<SystemScope> isReserved = new Predicate<SystemScope>() {
@Override @Override
public boolean apply(String input) { public boolean apply(SystemScope input) {
return (input != null && return (input != null && getReserved().contains(input));
!input.equals(ID_TOKEN_SCOPE) &&
!input.equals(REGISTRATION_TOKEN_SCOPE) &&
!input.equals(RESOURCE_TOKEN_SCOPE));
} }
}; };
@ -124,22 +120,6 @@ public class DefaultSystemScopeService implements SystemScopeService {
return repository.getAll(); return repository.getAll();
} }
/* (non-Javadoc)
* @see org.mitre.oauth2.service.SystemScopeService#getDefaults()
*/
@Override
public Set<SystemScope> getDefaults() {
return Sets.filter(getAll(), isDefault);
}
/* (non-Javadoc)
* @see org.mitre.oauth2.service.SystemScopeService#getDynReg()
*/
@Override
public Set<SystemScope> getDynReg() {
return Sets.filter(getAll(), isDynReg);
}
/* (non-Javadoc) /* (non-Javadoc)
* @see org.mitre.oauth2.service.SystemScopeService#getById(java.lang.Long) * @see org.mitre.oauth2.service.SystemScopeService#getById(java.lang.Long)
*/ */
@ -170,7 +150,11 @@ public class DefaultSystemScopeService implements SystemScopeService {
*/ */
@Override @Override
public SystemScope save(SystemScope scope) { public SystemScope save(SystemScope scope) {
if (!isReserved.apply(scope)) { // don't allow saving of reserved scopes
return repository.save(scope); return repository.save(scope);
} else {
return null;
}
} }
/* (non-Javadoc) /* (non-Javadoc)
@ -241,10 +225,34 @@ public class DefaultSystemScopeService implements SystemScopeService {
} }
@Override @Override
public Set<String> removeRestrictedScopes(Set<String> scopes) { public Set<SystemScope> getDefaults() {
return new LinkedHashSet<String>(Collections2.filter(scopes, isRestricted)); return Sets.filter(getAll(), isDefault);
} }
@Override
public Set<SystemScope> getReserved() {
return reservedScopes;
}
@Override
public Set<SystemScope> getRestricted() {
return Sets.filter(getAll(), isRestricted);
}
@Override
public Set<SystemScope> getUnrestricted() {
return Sets.filter(getAll(), Predicates.not(isRestricted));
}
@Override
public Set<SystemScope> removeRestrictedAndReservedScopes(Set<SystemScope> scopes) {
return Sets.filter(scopes, Predicates.not(Predicates.or(isRestricted, isReserved)));
}
@Override
public Set<SystemScope> removeReservedScopes(Set<SystemScope> scopes) {
return Sets.filter(scopes, Predicates.not(isReserved));
}
} }

View File

@ -62,7 +62,7 @@ import com.google.gson.JsonSyntaxException;
@Controller @Controller
@RequestMapping(value = "register") @RequestMapping(value = "register")
public class ClientDynamicRegistrationEndpoint { public class DynamicClientRegistrationEndpoint {
@Autowired @Autowired
private ClientDetailsEntityService clientService; private ClientDetailsEntityService clientService;
@ -85,7 +85,7 @@ public class ClientDynamicRegistrationEndpoint {
@Autowired @Autowired
private OIDCTokenService connectTokenService; private OIDCTokenService connectTokenService;
private static Logger logger = LoggerFactory.getLogger(ClientDynamicRegistrationEndpoint.class); private static Logger logger = LoggerFactory.getLogger(DynamicClientRegistrationEndpoint.class);
/** /**
* Create a new Client, issue a client ID, and create a registration access token. * Create a new Client, issue a client ID, and create a registration access token.
@ -361,14 +361,11 @@ public class ClientDynamicRegistrationEndpoint {
} }
private ClientDetailsEntity validateScopes(ClientDetailsEntity newClient) throws ValidationException { 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 // scopes that the client is asking for
Set<SystemScope> requestedScopes = scopeService.fromStrings(newClient.getScope()); Set<SystemScope> requestedScopes = scopeService.fromStrings(newClient.getScope());
// the scopes that the client can have must be a subset of the dynamically allowed scopes // the scopes that the client can have must be a subset of the dynamically allowed scopes
Set<SystemScope> allowedScopes = Sets.intersection(dynScopes, requestedScopes); Set<SystemScope> allowedScopes = scopeService.removeRestrictedAndReservedScopes(requestedScopes);
// if the client didn't ask for any, give them the defaults // if the client didn't ask for any, give them the defaults
if (allowedScopes == null || allowedScopes.isEmpty()) { if (allowedScopes == null || allowedScopes.isEmpty()) {

View File

@ -201,18 +201,18 @@ public class ProtectedResourceRegistrationEndpoint {
} }
private ClientDetailsEntity validateScopes(ClientDetailsEntity newClient) throws ValidationException { private ClientDetailsEntity validateScopes(ClientDetailsEntity newClient) throws ValidationException {
// note that protected resources can register for any scopes, even ones not used by the sysadmin
// scopes that the client is asking for // scopes that the client is asking for
Set<SystemScope> requestedScopes = scopeService.fromStrings(newClient.getScope()); 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 = scopeService.removeRestrictedAndReservedScopes(requestedScopes);
// if the client didn't ask for any, give them the defaults // if the client didn't ask for any, give them the defaults
if (requestedScopes == null || requestedScopes.isEmpty()) { if (allowedScopes == null || allowedScopes.isEmpty()) {
requestedScopes = scopeService.getDefaults(); allowedScopes = scopeService.getDefaults();
} }
newClient.setScope(scopeService.toStrings(requestedScopes)); newClient.setScope(scopeService.toStrings(allowedScopes));
return newClient; return newClient;
} }

View File

@ -99,7 +99,7 @@ public class TestDefaultOAuth2ClientDetailsEntityService {
} }
}); });
Mockito.when(scopeService.removeRestrictedScopes(Matchers.anySet())).thenAnswer(new Answer<Set<String>>() { Mockito.when(scopeService.removeRestrictedAndReservedScopes(Matchers.anySet())).thenAnswer(new Answer<Set<String>>() {
@Override @Override
public Set<String> answer(InvocationOnMock invocation) throws Throwable { public Set<String> answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments(); Object[] args = invocation.getArguments();

View File

@ -144,7 +144,7 @@ public class TestDefaultOAuth2ProviderTokenService {
Mockito.when(authenticationHolderRepository.save(Matchers.any(AuthenticationHolderEntity.class))).thenReturn(storedAuthHolder); Mockito.when(authenticationHolderRepository.save(Matchers.any(AuthenticationHolderEntity.class))).thenReturn(storedAuthHolder);
Mockito.when(scopeService.removeRestrictedScopes(Matchers.anySet())).then(AdditionalAnswers.returnsFirstArg()); Mockito.when(scopeService.removeRestrictedAndReservedScopes(Matchers.anySet())).then(AdditionalAnswers.returnsFirstArg());
Mockito.when(tokenEnhancer.enhance(Matchers.any(OAuth2AccessTokenEntity.class), Matchers.any(OAuth2Authentication.class))) Mockito.when(tokenEnhancer.enhance(Matchers.any(OAuth2AccessTokenEntity.class), Matchers.any(OAuth2Authentication.class)))
.thenAnswer(new Answer<OAuth2AccessTokenEntity>(){ .thenAnswer(new Answer<OAuth2AccessTokenEntity>(){

View File

@ -21,6 +21,7 @@ import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import java.util.HashSet;
import java.util.Set; import java.util.Set;
import org.junit.Before; import org.junit.Before;
@ -50,7 +51,7 @@ public class TestDefaultSystemScopeService {
private SystemScope defaultScope1; private SystemScope defaultScope1;
private SystemScope defaultScope2; private SystemScope defaultScope2;
private SystemScope dynScope1; private SystemScope dynScope1;
private SystemScope extraScope1; private SystemScope restrictedScope1;
private SystemScope structuredScope1; private SystemScope structuredScope1;
private SystemScope structuredScope1Value; private SystemScope structuredScope1Value;
@ -59,12 +60,14 @@ public class TestDefaultSystemScopeService {
private String defaultScope1String = "defaultScope1"; private String defaultScope1String = "defaultScope1";
private String defaultScope2String = "defaultScope2"; private String defaultScope2String = "defaultScope2";
private String dynScope1String = "dynScope1"; private String dynScope1String = "dynScope1";
private String extraScope1String = "extraScope1"; private String restrictedScope1String = "restrictedScope1";
private String structuredScope1String = "structuredScope1"; private String structuredScope1String = "structuredScope1";
private String structuredValue = "structuredValue"; private String structuredValue = "structuredValue";
private Set<SystemScope> allScopes; private Set<SystemScope> allScopes;
private Set<String> allScopeStrings; private Set<String> allScopeStrings;
private Set<SystemScope> allScopesWithValue;
private Set<String> allScopeStringsWithValue;
@Mock @Mock
private SystemScopeRepository repository; private SystemScopeRepository repository;
@ -80,26 +83,27 @@ public class TestDefaultSystemScopeService {
Mockito.reset(repository); Mockito.reset(repository);
// two default and dynamically registerable scopes // two default and dynamically registerable scopes (unrestricted)
defaultDynScope1 = new SystemScope(defaultDynScope1String); defaultDynScope1 = new SystemScope(defaultDynScope1String);
defaultDynScope2 = new SystemScope(defaultDynScope2String); defaultDynScope2 = new SystemScope(defaultDynScope2String);
defaultDynScope1.setAllowDynReg(true);
defaultDynScope2.setAllowDynReg(true);
defaultDynScope1.setDefaultScope(true); defaultDynScope1.setDefaultScope(true);
defaultDynScope2.setDefaultScope(true); defaultDynScope2.setDefaultScope(true);
// two strictly default scopes (isAllowDynReg false) // two strictly default scopes (restricted)
defaultScope1 = new SystemScope(defaultScope1String); defaultScope1 = new SystemScope(defaultScope1String);
defaultScope2 = new SystemScope(defaultScope2String); defaultScope2 = new SystemScope(defaultScope2String);
defaultScope1.setRestricted(true);
defaultScope2.setRestricted(true);
defaultScope1.setDefaultScope(true); defaultScope1.setDefaultScope(true);
defaultScope2.setDefaultScope(true); defaultScope2.setDefaultScope(true);
// one strictly dynamically registerable scope (isDefault false) // one strictly dynamically registerable scope (isDefault false)
dynScope1 = new SystemScope(dynScope1String); dynScope1 = new SystemScope(dynScope1String);
dynScope1.setAllowDynReg(true);
// extraScope1 : extra scope that is neither (defaults to false/false) // extraScope1 : extra scope that is neither restricted nor default (defaults to false/false)
extraScope1 = new SystemScope(extraScope1String); restrictedScope1 = new SystemScope(restrictedScope1String);
restrictedScope1.setRestricted(true);
// structuredScope1 : structured scope // structuredScope1 : structured scope
structuredScope1 = new SystemScope(structuredScope1String); structuredScope1 = new SystemScope(structuredScope1String);
@ -110,15 +114,18 @@ public class TestDefaultSystemScopeService {
structuredScope1Value.setStructured(true); structuredScope1Value.setStructured(true);
structuredScope1Value.setStructuredValue(structuredValue); structuredScope1Value.setStructuredValue(structuredValue);
allScopes = Sets.newHashSet(defaultDynScope1, defaultDynScope2, defaultScope1, defaultScope2, dynScope1, extraScope1, structuredScope1, structuredScope1Value); allScopes = Sets.newHashSet(defaultDynScope1, defaultDynScope2, defaultScope1, defaultScope2, dynScope1, restrictedScope1, structuredScope1);
allScopeStrings = Sets.newHashSet(defaultDynScope1String, defaultDynScope2String, defaultScope1String, defaultScope2String, dynScope1String, extraScope1String, structuredScope1String, structuredScope1String + ":" + structuredValue); allScopeStrings = Sets.newHashSet(defaultDynScope1String, defaultDynScope2String, defaultScope1String, defaultScope2String, dynScope1String, restrictedScope1String, structuredScope1String);
allScopesWithValue = Sets.newHashSet(defaultDynScope1, defaultDynScope2, defaultScope1, defaultScope2, dynScope1, restrictedScope1, structuredScope1, structuredScope1Value);
allScopeStringsWithValue = Sets.newHashSet(defaultDynScope1String, defaultDynScope2String, defaultScope1String, defaultScope2String, dynScope1String, restrictedScope1String, structuredScope1String, structuredScope1String + ":" + structuredValue);
Mockito.when(repository.getByValue(defaultDynScope1String)).thenReturn(defaultDynScope1); Mockito.when(repository.getByValue(defaultDynScope1String)).thenReturn(defaultDynScope1);
Mockito.when(repository.getByValue(defaultDynScope2String)).thenReturn(defaultDynScope2); Mockito.when(repository.getByValue(defaultDynScope2String)).thenReturn(defaultDynScope2);
Mockito.when(repository.getByValue(defaultScope1String)).thenReturn(defaultScope1); Mockito.when(repository.getByValue(defaultScope1String)).thenReturn(defaultScope1);
Mockito.when(repository.getByValue(defaultScope2String)).thenReturn(defaultScope2); Mockito.when(repository.getByValue(defaultScope2String)).thenReturn(defaultScope2);
Mockito.when(repository.getByValue(dynScope1String)).thenReturn(dynScope1); Mockito.when(repository.getByValue(dynScope1String)).thenReturn(dynScope1);
Mockito.when(repository.getByValue(extraScope1String)).thenReturn(extraScope1); Mockito.when(repository.getByValue(restrictedScope1String)).thenReturn(restrictedScope1);
// we re-use this value so we've got to use thenAnswer instead // we re-use this value so we've got to use thenAnswer instead
Mockito.when(repository.getByValue(structuredScope1String)).thenAnswer(new Answer<SystemScope>() { Mockito.when(repository.getByValue(structuredScope1String)).thenAnswer(new Answer<SystemScope>() {
@Override @Override
@ -148,11 +155,19 @@ public class TestDefaultSystemScopeService {
} }
@Test @Test
public void getDynReg() { public void getUnrestricted() {
Set<SystemScope> dynReg = Sets.newHashSet(defaultDynScope1, defaultDynScope2, dynScope1); Set<SystemScope> unrestricted = Sets.newHashSet(defaultDynScope1, defaultDynScope2, dynScope1, structuredScope1);
assertThat(service.getUnrestricted(), equalTo(unrestricted));
}
@Test
public void getRestricted() {
Set<SystemScope> restricted = Sets.newHashSet(defaultScope1, defaultScope2, restrictedScope1);
assertThat(service.getRestricted(), equalTo(restricted));
assertThat(service.getDynReg(), equalTo(dynReg));
} }
@Test @Test
@ -162,6 +177,8 @@ public class TestDefaultSystemScopeService {
assertThat(service.fromStrings(null), is(nullValue())); assertThat(service.fromStrings(null), is(nullValue()));
assertThat(service.fromStrings(allScopeStrings), equalTo(allScopes)); assertThat(service.fromStrings(allScopeStrings), equalTo(allScopes));
assertThat(service.fromStrings(allScopeStringsWithValue), equalTo(allScopesWithValue));
} }
@Test @Test
@ -171,6 +188,8 @@ public class TestDefaultSystemScopeService {
assertThat(service.toStrings(null), is(nullValue())); assertThat(service.toStrings(null), is(nullValue()));
assertThat(service.toStrings(allScopes), equalTo(allScopeStrings)); assertThat(service.toStrings(allScopes), equalTo(allScopeStrings));
assertThat(service.toStrings(allScopesWithValue), equalTo(allScopeStringsWithValue));
} }
@Test @Test

View File

@ -80,6 +80,7 @@ import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter; import com.google.gson.stream.JsonWriter;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
@SuppressWarnings(value = {"rawtypes", "unchecked"})
public class TestMITREidDataService_1_0 { public class TestMITREidDataService_1_0 {
@Mock @Mock