Structured Scopes from BB+
parent
6851224e42
commit
b416888b07
|
@ -47,6 +47,8 @@ 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,6 +144,7 @@ public class SystemScope {
|
|||
public boolean isDefaultScope() {
|
||||
return defaultScope;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param defaultScope the defaultScope to set
|
||||
*/
|
||||
|
@ -149,6 +152,33 @@ public class SystemScope {
|
|||
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 + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
|
@ -123,6 +124,9 @@ 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";
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
);
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
<button class="btn btn-small btn-cancel"><i class="icon-ban-circle"></i> Cancel</button>
|
||||
|
|
Loading…
Reference in New Issue