From 84695aa8302572262c08d09fc8c48f97eea29714 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 7 Oct 2016 23:41:59 -0400 Subject: [PATCH] enhancing claims gathering endpoint --- .../service/impl/WebfingerIssuerService.java | 2 +- .../uma/service/ClaimsProcessingService.java | 3 +- .../mitre/uma/service/PermissionService.java | 5 + .../WEB-INF/views/claims_collection.jsp | 75 ++++++++++++ .../service/impl/MITREidDataService_1_2.java | 0 .../impl/DefaultPermissionService.java | 8 ++ .../impl/MatchAllClaimsOnAnyPolicy.java | 10 +- .../token/RequestingPartyTokenGranter.java | 13 ++- .../uma/web/ClaimsCollectionEndpoint.java | 110 +++++++++++++----- .../mitre/uma/web/UmaDiscoveryEndpoint.java | 2 +- 10 files changed, 185 insertions(+), 43 deletions(-) create mode 100644 uma-server-webapp/src/main/webapp/WEB-INF/views/claims_collection.jsp rename {uma-server-webapp => uma-server}/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java (100%) diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/WebfingerIssuerService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/WebfingerIssuerService.java index d4f9d5ab3..283b9c982 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/WebfingerIssuerService.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/WebfingerIssuerService.java @@ -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; diff --git a/openid-connect-common/src/main/java/org/mitre/uma/service/ClaimsProcessingService.java b/openid-connect-common/src/main/java/org/mitre/uma/service/ClaimsProcessingService.java index ff8752d9a..a598604bd 100644 --- a/openid-connect-common/src/main/java/org/mitre/uma/service/ClaimsProcessingService.java +++ b/openid-connect-common/src/main/java/org/mitre/uma/service/ClaimsProcessingService.java @@ -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); } diff --git a/openid-connect-common/src/main/java/org/mitre/uma/service/PermissionService.java b/openid-connect-common/src/main/java/org/mitre/uma/service/PermissionService.java index cd01b6c8f..04ed52939 100644 --- a/openid-connect-common/src/main/java/org/mitre/uma/service/PermissionService.java +++ b/openid-connect-common/src/main/java/org/mitre/uma/service/PermissionService.java @@ -55,4 +55,9 @@ public interface PermissionService { */ public PermissionTicket updateTicket(PermissionTicket ticket); + /** + * @param ticket + */ + public void removeTicket(PermissionTicket ticket); + } diff --git a/uma-server-webapp/src/main/webapp/WEB-INF/views/claims_collection.jsp b/uma-server-webapp/src/main/webapp/WEB-INF/views/claims_collection.jsp new file mode 100644 index 000000000..d2703c21e --- /dev/null +++ b/uma-server-webapp/src/main/webapp/WEB-INF/views/claims_collection.jsp @@ -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"%> + + + +
+
+
+
+ +

The client + + + + + + + + + is requesting access to the resource set + + + + + + + + . +

+ +

This system requires that you identify yourself before the process can continue.

+ +
So far, you have provided the following claims: +
    + +
  • NONE
  • +
    + +
  • + : + () +
  • +
    +
+
+ +

Enter your email address to log in with OpenID Connect

+ +
+
+ +
+
+ + + +
+
+
+
+ +
+ +
+ +
+ +
+
+
+ diff --git a/uma-server-webapp/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java b/uma-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java similarity index 100% rename from uma-server-webapp/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java rename to uma-server/src/main/java/org/mitre/openid/connect/service/impl/MITREidDataService_1_2.java diff --git a/uma-server/src/main/java/org/mitre/uma/service/impl/DefaultPermissionService.java b/uma-server/src/main/java/org/mitre/uma/service/impl/DefaultPermissionService.java index b58e1afaa..84a0881ec 100644 --- a/uma-server/src/main/java/org/mitre/uma/service/impl/DefaultPermissionService.java +++ b/uma-server/src/main/java/org/mitre/uma/service/impl/DefaultPermissionService.java @@ -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); + } + } diff --git a/uma-server/src/main/java/org/mitre/uma/service/impl/MatchAllClaimsOnAnyPolicy.java b/uma-server/src/main/java/org/mitre/uma/service/impl/MatchAllClaimsOnAnyPolicy.java index 19695566a..ac505444f 100644 --- a/uma-server/src/main/java/org/mitre/uma/service/impl/MatchAllClaimsOnAnyPolicy.java +++ b/uma-server/src/main/java/org/mitre/uma/service/impl/MatchAllClaimsOnAnyPolicy.java @@ -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 allUnmatched = new HashSet<>(); - Collection 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 unmatched = checkIndividualClaims(policy.getClaimsRequired(), claimsSupplied); + Collection 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); diff --git a/uma-server/src/main/java/org/mitre/uma/token/RequestingPartyTokenGranter.java b/uma-server/src/main/java/org/mitre/uma/token/RequestingPartyTokenGranter.java index f8a16e884..8a34424c8 100644 --- a/uma-server/src/main/java/org/mitre/uma/token/RequestingPartyTokenGranter.java +++ b/uma-server/src/main/java/org/mitre/uma/token/RequestingPartyTokenGranter.java @@ -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 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()); diff --git a/uma-server/src/main/java/org/mitre/uma/web/ClaimsCollectionEndpoint.java b/uma-server/src/main/java/org/mitre/uma/web/ClaimsCollectionEndpoint.java index bc9e39ebe..7b638e956 100644 --- a/uma-server/src/main/java/org/mitre/uma/web/ClaimsCollectionEndpoint.java +++ b/uma-server/src/main/java/org/mitre/uma/web/ClaimsCollectionEndpoint.java @@ -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 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); } diff --git a/uma-server/src/main/java/org/mitre/uma/web/UmaDiscoveryEndpoint.java b/uma-server/src/main/java/org/mitre/uma/web/UmaDiscoveryEndpoint.java index ab59d660a..e20335ea4 100644 --- a/uma-server/src/main/java/org/mitre/uma/web/UmaDiscoveryEndpoint.java +++ b/uma-server/src/main/java/org/mitre/uma/web/UmaDiscoveryEndpoint.java @@ -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 m = new HashMap<>();