Structured Scopes from BB+

pull/516/head
Josh Mandel 2013-08-30 12:09:50 -04:00 committed by Justin Richer
parent 6851224e42
commit b416888b07
9 changed files with 156 additions and 7 deletions

View File

@ -47,7 +47,9 @@ public class SystemScope {
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 structured = false; // is this a default scope for newly-registered clients?
private String structuredParamDescription;
/**
* Make a blank system scope with no value
*/
@ -142,13 +144,41 @@ public class SystemScope {
public boolean isDefaultScope() {
return defaultScope;
}
/**
* @param defaultScope the defaultScope to set
*/
public void setDefaultScope(boolean defaultScope) {
this.defaultScope = defaultScope;
}
/**
* @return the isStructured status
*/
@Basic
@Column(name = "structured")
public boolean isStructured() {
return structured;
}
public void setIsStructured(boolean isStructured) {
this.structured = isStructured;
}
@Basic
@Column(name = "structured_param_description")
public String getStructuredParamDescription() {
return structuredParamDescription;
}
/**
* @param isStructured the isStructured to set
*/
public void setStructuredParamDescription(String d) {
this.structuredParamDescription = d;
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@ -158,6 +188,7 @@ public class SystemScope {
int result = 1;
result = prime * result + (allowDynReg ? 1231 : 1237);
result = prime * result + (defaultScope ? 1231 : 1237);
result = prime * result + (structured ? 1231 : 1237);
result = prime * result + ((description == null) ? 0 : description.hashCode());
result = prime * result + ((icon == null) ? 0 : icon.hashCode());
result = prime * result + ((id == null) ? 0 : id.hashCode());
@ -186,6 +217,9 @@ public class SystemScope {
if (defaultScope != other.defaultScope) {
return false;
}
if (structured != other.structured) {
return false;
}
if (description == null) {
if (other.description != null) {
return false;
@ -219,7 +253,7 @@ public class SystemScope {
@Override
public String toString() {
return "SystemScope [value=" + value + ", description=" + description + ", icon=" + icon + ", allowDynReg=" + allowDynReg + ", defaultScope=" + defaultScope + "]";
return "SystemScope [value=" + value + ", description=" + description + ", icon=" + icon + ", allowDynReg=" + allowDynReg + ", defaultScope=" + defaultScope + ", isStructured=" + structured + "]";
}
}

View File

@ -19,8 +19,11 @@
*/
package org.mitre.oauth2.service.impl;
import java.util.HashMap;
import java.util.Map;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.logging.Logger;
import org.mitre.oauth2.model.SystemScope;
import org.mitre.oauth2.repository.SystemScopeRepository;
@ -31,7 +34,9 @@ import org.springframework.stereotype.Service;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.base.Splitter;
import com.google.common.collect.Collections2;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
/**
@ -62,6 +67,7 @@ public class DefaultSystemScopeService implements SystemScopeService {
private Function<String, SystemScope> stringToSystemScope = new Function<String, SystemScope>() {
@Override
public SystemScope apply(String input) {
input = baseScopeString(input);
if (input == null) {
return null;
} else {
@ -169,6 +175,53 @@ public class DefaultSystemScopeService implements SystemScopeService {
}
}
private String[] scopeParts(String value){
return Iterables.toArray(
Splitter.on(":").split(value),
String.class);
}
@Override
public String baseScopeString(String value) {
SystemScope s = toStructuredScope(value);
if (s != null) {
return s.getValue();
}
return value;
}
@Override
public SystemScope toStructuredScope(String value) {
String[] scopeParts = scopeParts(value);
String baseScope = value;
if (scopeParts.length == 2) {
baseScope = scopeParts[0];
}
SystemScope s = repository.getByValue(baseScope);
if (s != null && s.isStructured()) {
return s;
}
return null;
}
@Override
public Map<String, String> structuredScopeParameters(Set<String> scopes) {
HashMap<String, String> ret = new HashMap<String, String>();
for (String s : scopes){
SystemScope structured = toStructuredScope(s);
if (structured != null){
String[] scopeParts = scopeParts(s);
if (scopeParts.length == 2){
ret.put(scopeParts[0], scopeParts[1]);
}
}
}
return ret;
}
}

View File

@ -22,9 +22,11 @@ import java.util.Set;
import org.mitre.oauth2.model.ClientDetailsEntity;
import org.mitre.oauth2.model.OAuth2AccessTokenEntity;
import org.mitre.oauth2.model.SystemScope;
import org.mitre.oauth2.model.OAuth2RefreshTokenEntity;
import org.mitre.oauth2.service.ClientDetailsEntityService;
import org.mitre.oauth2.service.OAuth2TokenEntityService;
import org.mitre.oauth2.service.SystemScopeService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@ -48,6 +50,9 @@ public class IntrospectionEndpoint {
@Autowired
private ClientDetailsEntityService clientService;
@Autowired
private SystemScopeService scopeService;
private static Logger logger = LoggerFactory.getLogger(IntrospectionEndpoint.class);
public IntrospectionEndpoint() {
@ -116,11 +121,24 @@ public class IntrospectionEndpoint {
// if it's the same client that the token was issued to, or it at least has all the scopes the token was issued with
if (authClient.getClientId().equals(tokenClient.getClientId()) || authClient.getScope().containsAll(scopes)) {
// if it's a valid token, we'll print out information on it
model.addAttribute("entity", token);
return "tokenIntrospection";
} else {
boolean scopesConsistent = true;
for (String ts : scopes){
if (!authClient.getScope().contains(scopeService.baseScopeString(ts))){
scopesConsistent = false;
break;
}
}
if (scopesConsistent) {
model.addAttribute("entity", token);
return "tokenIntrospection";
}
logger.error("Verify failed; client tried to introspect a token of an incorrect scope");
model.addAttribute("code", HttpStatus.FORBIDDEN);
return "httpCodeView";

View File

@ -19,6 +19,7 @@
*/
package org.mitre.oauth2.web;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
@ -110,7 +111,7 @@ public class OAuthConfirmationController {
model.put("redirect_uri", redirect_uri);
Set<SystemScope> scopes = scopeService.fromStrings(clientAuth.getScope());
Set<SystemScope> sortedScopes = new LinkedHashSet<SystemScope>(scopes.size());
Set<SystemScope> systemScopes = scopeService.getAll();
@ -122,7 +123,10 @@ public class OAuthConfirmationController {
}
sortedScopes.addAll(Sets.difference(scopes, systemScopes));
Map<String, String> proposedParams = scopeService.structuredScopeParameters(clientAuth.getScope());
model.put("proposedParams", proposedParams);
model.put("scopes", sortedScopes);
return "approve";

View File

@ -19,6 +19,7 @@ package org.mitre.openid.connect;
import java.text.ParseException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -28,11 +29,13 @@ import org.mitre.jwt.signer.service.JwtSigningAndValidationService;
import org.mitre.jwt.signer.service.impl.JWKSetSigningAndValidationServiceCacheService;
import org.mitre.oauth2.model.ClientDetailsEntity;
import org.mitre.oauth2.service.ClientDetailsEntityService;
import org.mitre.oauth2.service.SystemScopeService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.common.exceptions.InvalidScopeException;
import org.springframework.security.oauth2.common.util.OAuth2Utils;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.ClientDetails;
@ -40,7 +43,10 @@ import org.springframework.security.oauth2.provider.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.stereotype.Component;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.nimbusds.jose.util.JSONObjectUtils;
import com.nimbusds.jwt.SignedJWT;
@ -54,6 +60,9 @@ public class ConnectOAuth2RequestFactory extends DefaultOAuth2RequestFactory {
@Autowired
private JWKSetSigningAndValidationServiceCacheService validators;
@Autowired
private SystemScopeService systemScopes;
/**
* Constructor with arguments
*

View File

@ -22,6 +22,8 @@ import java.util.Date;
import java.util.Map;
import java.util.Set;
import org.mitre.oauth2.model.SystemScope;
import org.mitre.oauth2.service.SystemScopeService;
import javax.servlet.http.HttpSession;
import org.mitre.openid.connect.model.ApprovedSite;
@ -39,7 +41,9 @@ import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
/**
@ -69,6 +73,8 @@ public class TofuUserApprovalHandler implements UserApprovalHandler {
@Autowired
private ClientDetailsService clientDetailsService;
@Autowired
private SystemScopeService systemScopes;
/**
* Check if the user has already stored a positive approval decision for this site; or if the
@ -195,11 +201,21 @@ public class TofuUserApprovalHandler implements UserApprovalHandler {
//registered allowed scopes.
String scope = approvalParams.get(key);
String baseScope = systemScopes.baseScopeString(scope);
SystemScope structured = systemScopes.toStructuredScope(scope);
//Make sure this scope is allowed for the given client
if (client.getScope().contains(scope)) {
allowedScopes.add(scope);
if (client.getScope().contains(baseScope)) {
// If it's structured, assign the user-specified parameter
if (structured != null){
String paramValue = approvalParams.get("scopeparam_" + scope);
allowedScopes.add(scope + ":"+paramValue);
// .. and if it's unstructured, we're all set
} else {
allowedScopes.add(scope);
}
}
}
}

View File

@ -165,6 +165,8 @@ CREATE TABLE IF NOT EXISTS system_scope (
icon VARCHAR(256),
allow_dyn_reg BOOLEAN NOT NULL DEFAULT false,
default_scope BOOLEAN NOT NULL DEFAULT false,
structured BOOLEAN NOT NULL DEFAULT false,
structured_param_description VARCHAR(256),
UNIQUE (scope)
);

View File

@ -92,7 +92,11 @@
${ scope.value }
</c:otherwise>
</c:choose>
</label>
<c:if test="${ scope.structured }">
<input name="scopeparam_${ scope.value }" type="text" value="${proposedParams[scope.value]}" placeholder="${scope.structuredParamDescription}">
</c:if>
</c:forEach>

View File

@ -142,6 +142,15 @@
</div>
</div>
<div class="control-group" id="isStructured">
<div class="controls">
<label class="checkbox">
<input type="checkbox" <%=isStructured ? 'checked' : '' %>> is a structured scope
</label>
<p class="help-block">Is the scope structured with structured values like <code>base:extension</code>?</p>
</div>
</div>
<div class="well well-small">
<button class="btn btn-small btn-save btn-success"><i class="icon-ok-circle icon-white"></i> Save</button> &nbsp;
<button class="btn btn-small btn-cancel"><i class="icon-ban-circle"></i> Cancel</button>