enhancing claims gathering endpoint
parent
f8372666e6
commit
84695aa830
|
@ -115,7 +115,7 @@ public class WebfingerIssuerService implements IssuerService {
|
|||
throw new AuthenticationServiceException("Issuer was in blacklist: " + lr.issuer);
|
||||
}
|
||||
|
||||
return new IssuerServiceResponse(lr.issuer, lr.loginHint, null);
|
||||
return new IssuerServiceResponse(lr.issuer, lr.loginHint, request.getParameter("target_link_uri"));
|
||||
} catch (UncheckedExecutionException | ExecutionException e) {
|
||||
logger.warn("Issue fetching issuer for user input: " + identifier + ": " + e.getMessage());
|
||||
return null;
|
||||
|
|
|
@ -19,7 +19,6 @@ package org.mitre.uma.service;
|
|||
|
||||
import org.mitre.uma.model.ClaimProcessingResult;
|
||||
import org.mitre.uma.model.PermissionTicket;
|
||||
import org.mitre.uma.model.PersistedClaimsToken;
|
||||
import org.mitre.uma.model.ResourceSet;
|
||||
|
||||
/**
|
||||
|
@ -41,6 +40,6 @@ public interface ClaimsProcessingService {
|
|||
* @param ticket the supplied claims to test
|
||||
* @return the result of the claims processing action
|
||||
*/
|
||||
public ClaimProcessingResult claimsAreSatisfied(ResourceSet rs, PermissionTicket ticket, PersistedClaimsToken pct);
|
||||
public ClaimProcessingResult claimsAreSatisfied(ResourceSet rs, PermissionTicket ticket);
|
||||
|
||||
}
|
||||
|
|
|
@ -55,4 +55,9 @@ public interface PermissionService {
|
|||
*/
|
||||
public PermissionTicket updateTicket(PermissionTicket ticket);
|
||||
|
||||
/**
|
||||
* @param ticket
|
||||
*/
|
||||
public void removeTicket(PermissionTicket ticket);
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
<%@page import="org.springframework.http.HttpStatus"%>
|
||||
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
|
||||
<%@ taglib prefix="o" tagdir="/WEB-INF/tags"%>
|
||||
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
|
||||
<%@ taglib prefix="security" uri="http://www.springframework.org/security/tags"%>
|
||||
<%@page import="org.springframework.security.oauth2.common.exceptions.OAuth2Exception"%>
|
||||
<spring:message code="claims.title" var="title"/>
|
||||
<o:header title="${title}" />
|
||||
<o:topbar pageName="Claims" />
|
||||
<div class="container-fluid main">
|
||||
<div class="row-fluid">
|
||||
<div class="offset1 span10">
|
||||
<div class="hero-unit">
|
||||
|
||||
<h1>The client
|
||||
<c:choose>
|
||||
<c:when test="${empty client.clientName}">
|
||||
<em><c:out value="${client.clientId}" /></em>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<em><c:out value="${client.clientName}" /></em>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
is requesting access to the resource set
|
||||
<c:choose>
|
||||
<c:when test="${empty resourceSet.name}">
|
||||
<em><c:out value="${resourceSet.id}" /></em>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<em><c:out value="${resourceSet.name}" /></em>
|
||||
</c:otherwise>
|
||||
</c:choose>.
|
||||
</h1>
|
||||
|
||||
<p>This system requires that you identify yourself before the process can continue.</p>
|
||||
|
||||
<div>So far, you have provided the following claims:
|
||||
<ul>
|
||||
<c:if test="${empty claims}">
|
||||
<li><b>NONE</b></li>
|
||||
</c:if>
|
||||
<c:forEach items="${ claims }" var="claim">
|
||||
<li>
|
||||
<b><c:out value="${claim.name}" /></b>: <i><c:out value="${claim.value}" /></i>
|
||||
<small>(<c:out value="${claim.issuer}" />)</small>
|
||||
</li>
|
||||
</c:forEach>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<p>Enter your email address to log in with OpenID Connect</p>
|
||||
|
||||
<div class="well">
|
||||
<div class="row-fluid">
|
||||
|
||||
<div class="span8">
|
||||
<form action="openid_connect_login" method="get">
|
||||
<input type="text" class="input-xxlarge" name="identifier" id="identifier" />
|
||||
<input type="hidden" name="target_link_uri" value="rqp_claims/collect" />
|
||||
<input type="submit" value="Log In" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form action="rqp_claims" method="POST">
|
||||
<input type="submit" value="Return to Client" class="btn" />
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<o:footer />
|
|
@ -94,6 +94,14 @@ public class DefaultPermissionService implements PermissionService {
|
|||
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.mitre.uma.service.PermissionService#removeTicket(org.mitre.uma.model.PermissionTicket)
|
||||
*/
|
||||
@Override
|
||||
public void removeTicket(PermissionTicket ticket) {
|
||||
repository.remove(ticket);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ import java.util.HashSet;
|
|||
import org.mitre.uma.model.Claim;
|
||||
import org.mitre.uma.model.ClaimProcessingResult;
|
||||
import org.mitre.uma.model.PermissionTicket;
|
||||
import org.mitre.uma.model.PersistedClaimsToken;
|
||||
import org.mitre.uma.model.Policy;
|
||||
import org.mitre.uma.model.ResourceSet;
|
||||
import org.mitre.uma.service.ClaimsProcessingService;
|
||||
|
@ -43,15 +42,10 @@ public class MatchAllClaimsOnAnyPolicy implements ClaimsProcessingService {
|
|||
* @see org.mitre.uma.service.ClaimsProcessingService#claimsAreSatisfied(java.util.Collection, java.util.Collection)
|
||||
*/
|
||||
@Override
|
||||
public ClaimProcessingResult claimsAreSatisfied(ResourceSet rs, PermissionTicket ticket, PersistedClaimsToken pct) {
|
||||
public ClaimProcessingResult claimsAreSatisfied(ResourceSet rs, PermissionTicket ticket) {
|
||||
Collection<Claim> allUnmatched = new HashSet<>();
|
||||
Collection<Claim> claimsSupplied = new HashSet<>(ticket.getClaimsSupplied()); // copy the claims out of the ticket
|
||||
if (pct != null && pct.getClaimsSupplied() != null) {
|
||||
// add the claims from the PCT if available
|
||||
claimsSupplied.addAll(pct.getClaimsSupplied());
|
||||
}
|
||||
for (Policy policy : rs.getPolicies()) {
|
||||
Collection<Claim> unmatched = checkIndividualClaims(policy.getClaimsRequired(), claimsSupplied);
|
||||
Collection<Claim> unmatched = checkIndividualClaims(policy.getClaimsRequired(), ticket.getClaimsSupplied());
|
||||
if (unmatched.isEmpty()) {
|
||||
// we found something that's satisfied the claims, let's go with it!
|
||||
return new ClaimProcessingResult(policy);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
package org.mitre.uma.token;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
@ -153,8 +154,15 @@ public class RequestingPartyTokenGranter extends AbstractTokenGranter {
|
|||
throw new NotAuthorizedException();
|
||||
} else {
|
||||
// claims weren't empty or missing, we need to check against what we have
|
||||
|
||||
if (pct != null && pct.getClaimsSupplied() != null) {
|
||||
// add the claims from the PCT if available
|
||||
Collection<Claim> claimsSupplied = new HashSet<>(ticket.getClaimsSupplied()); // copy the claims out of the ticket
|
||||
claimsSupplied.addAll(pct.getClaimsSupplied());
|
||||
ticket.setClaimsSupplied(claimsSupplied);
|
||||
}
|
||||
|
||||
ClaimProcessingResult result = claimsProcessingService.claimsAreSatisfied(rs, ticket, pct);
|
||||
ClaimProcessingResult result = claimsProcessingService.claimsAreSatisfied(rs, ticket);
|
||||
|
||||
|
||||
if (result.isSatisfied()) {
|
||||
|
@ -244,6 +252,9 @@ public class RequestingPartyTokenGranter extends AbstractTokenGranter {
|
|||
tokenService.revokeAccessToken(incomingRpt);
|
||||
}
|
||||
|
||||
// remove our ticket because it shouldn't be used again
|
||||
permissionService.removeTicket(ticket);
|
||||
|
||||
// create a PCT
|
||||
PersistedClaimsToken newPct = new PersistedClaimsToken();
|
||||
newPct.setClientId(client.getClientId());
|
||||
|
|
|
@ -19,6 +19,8 @@ package org.mitre.uma.web;
|
|||
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.mitre.oauth2.model.ClientDetailsEntity;
|
||||
import org.mitre.oauth2.service.ClientDetailsEntityService;
|
||||
import org.mitre.openid.connect.model.OIDCAuthenticationToken;
|
||||
|
@ -26,6 +28,8 @@ import org.mitre.openid.connect.model.UserInfo;
|
|||
import org.mitre.openid.connect.view.HttpCodeView;
|
||||
import org.mitre.uma.model.Claim;
|
||||
import org.mitre.uma.model.PermissionTicket;
|
||||
import org.mitre.uma.model.ResourceSet;
|
||||
import org.mitre.uma.service.ClaimsProcessingService;
|
||||
import org.mitre.uma.service.PermissionService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -53,7 +57,6 @@ import com.google.gson.JsonPrimitive;
|
|||
*
|
||||
*/
|
||||
@Controller
|
||||
@PreAuthorize("hasRole('ROLE_EXTERNAL_USER')")
|
||||
@RequestMapping("/" + ClaimsCollectionEndpoint.URL)
|
||||
public class ClaimsCollectionEndpoint {
|
||||
// Logger for this class
|
||||
|
@ -61,17 +64,26 @@ public class ClaimsCollectionEndpoint {
|
|||
|
||||
public static final String URL = "rqp_claims";
|
||||
|
||||
// variables for session storage
|
||||
private static final String TICKET = URL + "-ticket";
|
||||
private static final String CLIENT = URL + "-client";
|
||||
private static final String REDIRECT_URI = URL + "-redirect_uri";
|
||||
private static final String STATE = URL + "-state";
|
||||
|
||||
@Autowired
|
||||
private ClientDetailsEntityService clientService;
|
||||
|
||||
@Autowired
|
||||
private PermissionService permissionService;
|
||||
|
||||
@Autowired
|
||||
private ClaimsProcessingService claimsProcessingService;
|
||||
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET)
|
||||
public String collectClaims(@RequestParam("client_id") String clientId, @RequestParam(value = "redirect_uri", required = false) String redirectUri,
|
||||
public String startClaimsCollection(@RequestParam("client_id") String clientId, @RequestParam(value = "redirect_uri", required = false) String redirectUri,
|
||||
@RequestParam("ticket") String ticketValue, @RequestParam(value = "state", required = false) String state,
|
||||
Model m, OIDCAuthenticationToken auth) {
|
||||
Model m, OIDCAuthenticationToken auth, HttpSession session) {
|
||||
|
||||
|
||||
ClientDetailsEntity client = clientService.loadClientByClientId(clientId);
|
||||
|
@ -79,42 +91,80 @@ public class ClaimsCollectionEndpoint {
|
|||
PermissionTicket ticket = permissionService.getByTicket(ticketValue);
|
||||
|
||||
if (client == null || ticket == null) {
|
||||
// couldn't find the client or the ticket, we bail here
|
||||
logger.info("Client or ticket not found: " + clientId + " :: " + ticketValue);
|
||||
m.addAttribute(HttpCodeView.CODE, HttpStatus.NOT_FOUND);
|
||||
return HttpCodeView.VIEWNAME;
|
||||
}
|
||||
|
||||
|
||||
// stash the client and ticket on the session and send the user to the claims endpoint
|
||||
session.setAttribute(TICKET, ticket);
|
||||
session.setAttribute(CLIENT, client);
|
||||
session.setAttribute(REDIRECT_URI, redirectUri);
|
||||
|
||||
return "redirect:" + URL + "/collect";
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/collect", method = RequestMethod.GET)
|
||||
public String collectClaims(Model m, OIDCAuthenticationToken auth, HttpSession session) {
|
||||
|
||||
ClientDetailsEntity client = (ClientDetailsEntity) session.getAttribute(CLIENT);
|
||||
PermissionTicket ticket = (PermissionTicket) session.getAttribute(TICKET);
|
||||
|
||||
// we've got a client and ticket, let's attach the claims that we have from the token and userinfo
|
||||
|
||||
// subject
|
||||
Set<Claim> claimsSupplied = Sets.newHashSet(ticket.getClaimsSupplied());
|
||||
|
||||
String issuer = auth.getIssuer();
|
||||
UserInfo userInfo = auth.getUserInfo();
|
||||
|
||||
claimsSupplied.add(mkClaim(issuer, "sub", new JsonPrimitive(auth.getSub())));
|
||||
if (userInfo.getEmail() != null) {
|
||||
claimsSupplied.add(mkClaim(issuer, "email", new JsonPrimitive(userInfo.getEmail())));
|
||||
}
|
||||
if (userInfo.getEmailVerified() != null) {
|
||||
claimsSupplied.add(mkClaim(issuer, "email_verified", new JsonPrimitive(userInfo.getEmailVerified())));
|
||||
}
|
||||
if (userInfo.getPhoneNumber() != null) {
|
||||
claimsSupplied.add(mkClaim(issuer, "phone_number", new JsonPrimitive(auth.getUserInfo().getPhoneNumber())));
|
||||
}
|
||||
if (userInfo.getPhoneNumberVerified() != null) {
|
||||
claimsSupplied.add(mkClaim(issuer, "phone_number_verified", new JsonPrimitive(auth.getUserInfo().getPhoneNumberVerified())));
|
||||
}
|
||||
if (userInfo.getPreferredUsername() != null) {
|
||||
claimsSupplied.add(mkClaim(issuer, "preferred_username", new JsonPrimitive(auth.getUserInfo().getPreferredUsername())));
|
||||
}
|
||||
if (userInfo.getProfile() != null) {
|
||||
claimsSupplied.add(mkClaim(issuer, "profile", new JsonPrimitive(auth.getUserInfo().getProfile())));
|
||||
}
|
||||
|
||||
ticket.setClaimsSupplied(claimsSupplied);
|
||||
if (auth != null) {
|
||||
|
||||
PermissionTicket updatedTicket = permissionService.updateTicket(ticket);
|
||||
String issuer = auth.getIssuer();
|
||||
UserInfo userInfo = auth.getUserInfo();
|
||||
|
||||
claimsSupplied.add(mkClaim(issuer, "sub", new JsonPrimitive(auth.getSub())));
|
||||
if (userInfo.getEmail() != null) {
|
||||
claimsSupplied.add(mkClaim(issuer, "email", new JsonPrimitive(userInfo.getEmail())));
|
||||
}
|
||||
if (userInfo.getEmailVerified() != null) {
|
||||
claimsSupplied.add(mkClaim(issuer, "email_verified", new JsonPrimitive(userInfo.getEmailVerified())));
|
||||
}
|
||||
if (userInfo.getPhoneNumber() != null) {
|
||||
claimsSupplied.add(mkClaim(issuer, "phone_number", new JsonPrimitive(auth.getUserInfo().getPhoneNumber())));
|
||||
}
|
||||
if (userInfo.getPhoneNumberVerified() != null) {
|
||||
claimsSupplied.add(mkClaim(issuer, "phone_number_verified", new JsonPrimitive(auth.getUserInfo().getPhoneNumberVerified())));
|
||||
}
|
||||
if (userInfo.getPreferredUsername() != null) {
|
||||
claimsSupplied.add(mkClaim(issuer, "preferred_username", new JsonPrimitive(auth.getUserInfo().getPreferredUsername())));
|
||||
}
|
||||
if (userInfo.getProfile() != null) {
|
||||
claimsSupplied.add(mkClaim(issuer, "profile", new JsonPrimitive(auth.getUserInfo().getProfile())));
|
||||
}
|
||||
|
||||
ticket.setClaimsSupplied(claimsSupplied);
|
||||
|
||||
ticket = permissionService.updateTicket(ticket);
|
||||
|
||||
session.setAttribute(TICKET, ticket);
|
||||
}
|
||||
|
||||
ResourceSet resourceSet = ticket.getPermission().getResourceSet();
|
||||
|
||||
m.addAttribute("claims", ticket.getClaimsSupplied());
|
||||
m.addAttribute("resourceSet", resourceSet);
|
||||
m.addAttribute("client", client);
|
||||
m.addAttribute("claimsResult", claimsProcessingService.claimsAreSatisfied(resourceSet, ticket));
|
||||
|
||||
return "claims_collection";
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.POST)
|
||||
public String returnToClient(Model m, OIDCAuthenticationToken auth, HttpSession session) {
|
||||
|
||||
ClientDetailsEntity client = (ClientDetailsEntity) session.getAttribute(CLIENT);
|
||||
PermissionTicket ticket = (PermissionTicket) session.getAttribute(TICKET);
|
||||
String redirectUri = (String) session.getAttribute(REDIRECT_URI);
|
||||
String state = (String) session.getAttribute(STATE);
|
||||
|
||||
if (Strings.isNullOrEmpty(redirectUri)) {
|
||||
if (client.getClaimsRedirectUris().size() == 1) {
|
||||
|
@ -131,7 +181,7 @@ public class ClaimsCollectionEndpoint {
|
|||
|
||||
UriComponentsBuilder template = UriComponentsBuilder.fromUriString(redirectUri);
|
||||
template.queryParam("authorization_state", "claims_submitted");
|
||||
template.queryParam("ticket", updatedTicket.getTicket());
|
||||
template.queryParam("ticket", ticket.getTicket());
|
||||
if (!Strings.isNullOrEmpty(state)) {
|
||||
template.queryParam("state", state);
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ public class UmaDiscoveryEndpoint {
|
|||
@Autowired
|
||||
private ConfigurationPropertiesBean config;
|
||||
|
||||
@RequestMapping(".well-known/multiparty-delegation-configuration")
|
||||
@RequestMapping(".well-known/uma2-configuration")
|
||||
public String umaConfiguration(Model model) {
|
||||
|
||||
Map<String, Object> m = new HashMap<>();
|
||||
|
|
Loading…
Reference in New Issue