TofuUserApprovalHandler.java

  1. /*******************************************************************************
  2.  * Copyright 2017 The MIT Internet Trust Consortium
  3.  *
  4.  * Portions copyright 2011-2013 The MITRE Corporation
  5.  *
  6.  * Licensed under the Apache License, Version 2.0 (the "License");
  7.  * you may not use this file except in compliance with the License.
  8.  * You may obtain a copy of the License at
  9.  *
  10.  *   http://www.apache.org/licenses/LICENSE-2.0
  11.  *
  12.  * Unless required by applicable law or agreed to in writing, software
  13.  * distributed under the License is distributed on an "AS IS" BASIS,
  14.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15.  * See the License for the specific language governing permissions and
  16.  * limitations under the License.
  17.  *******************************************************************************/
  18. package org.mitre.openid.connect.token;

  19. import static org.mitre.openid.connect.request.ConnectRequestParameters.APPROVED_SITE;
  20. import static org.mitre.openid.connect.request.ConnectRequestParameters.PROMPT;
  21. import static org.mitre.openid.connect.request.ConnectRequestParameters.PROMPT_CONSENT;
  22. import static org.mitre.openid.connect.request.ConnectRequestParameters.PROMPT_SEPARATOR;

  23. import java.util.Calendar;
  24. import java.util.Collection;
  25. import java.util.Date;
  26. import java.util.HashMap;
  27. import java.util.List;
  28. import java.util.Map;
  29. import java.util.Set;

  30. import javax.servlet.http.HttpSession;

  31. import org.mitre.oauth2.service.SystemScopeService;
  32. import org.mitre.openid.connect.model.ApprovedSite;
  33. import org.mitre.openid.connect.model.WhitelistedSite;
  34. import org.mitre.openid.connect.service.ApprovedSiteService;
  35. import org.mitre.openid.connect.service.WhitelistedSiteService;
  36. import org.mitre.openid.connect.web.AuthenticationTimeStamper;
  37. import org.springframework.beans.factory.annotation.Autowired;
  38. import org.springframework.security.core.Authentication;
  39. import org.springframework.security.oauth2.provider.AuthorizationRequest;
  40. import org.springframework.security.oauth2.provider.ClientDetails;
  41. import org.springframework.security.oauth2.provider.ClientDetailsService;
  42. import org.springframework.security.oauth2.provider.approval.UserApprovalHandler;
  43. import org.springframework.stereotype.Component;
  44. import org.springframework.web.context.request.RequestContextHolder;
  45. import org.springframework.web.context.request.ServletRequestAttributes;

  46. import com.google.common.base.Splitter;
  47. import com.google.common.base.Strings;
  48. import com.google.common.collect.Sets;

  49. /**
  50.  * Custom User Approval Handler implementation which uses a concept of a whitelist,
  51.  * blacklist, and greylist.
  52.  *
  53.  * Blacklisted sites will be caught and handled before this
  54.  * point.
  55.  *
  56.  * Whitelisted sites will be automatically approved, and an ApprovedSite entry will
  57.  * be created for the site the first time a given user access it.
  58.  *
  59.  * All other sites fall into the greylist - the user will be presented with the user
  60.  * approval page upon their first visit
  61.  * @author aanganes
  62.  *
  63.  */
  64. @Component("tofuUserApprovalHandler")
  65. public class TofuUserApprovalHandler implements UserApprovalHandler {

  66.     @Autowired
  67.     private ApprovedSiteService approvedSiteService;

  68.     @Autowired
  69.     private WhitelistedSiteService whitelistedSiteService;

  70.     @Autowired
  71.     private ClientDetailsService clientDetailsService;

  72.     @Autowired
  73.     private SystemScopeService systemScopes;

  74.     /**
  75.      * Check if the user has already stored a positive approval decision for this site; or if the
  76.      * site is whitelisted, approve it automatically.
  77.      *
  78.      * Otherwise, return false so that the user will see the approval page and can make their own decision.
  79.      *
  80.      * @param authorizationRequest  the incoming authorization request
  81.      * @param userAuthentication    the Principal representing the currently-logged-in user
  82.      *
  83.      * @return                      true if the site is approved, false otherwise
  84.      */
  85.     @Override
  86.     public boolean isApproved(AuthorizationRequest authorizationRequest, Authentication userAuthentication) {

  87.         // if this request is already approved, pass that info through
  88.         // (this flag may be set by updateBeforeApproval, which can also do funny things with scopes, etc)
  89.         if (authorizationRequest.isApproved()) {
  90.             return true;
  91.         } else {
  92.             // if not, check to see if the user has approved it
  93.             // TODO: make parameter name configurable?
  94.             return Boolean.parseBoolean(authorizationRequest.getApprovalParameters().get("user_oauth_approval"));
  95.         }

  96.     }

  97.     /**
  98.      * Check if the user has already stored a positive approval decision for this site; or if the
  99.      * site is whitelisted, approve it automatically.
  100.      *
  101.      * Otherwise the user will be directed to the approval page and can make their own decision.
  102.      *
  103.      * @param authorizationRequest  the incoming authorization request
  104.      * @param userAuthentication    the Principal representing the currently-logged-in user
  105.      *
  106.      * @return                      the updated AuthorizationRequest
  107.      */
  108.     @Override
  109.     public AuthorizationRequest checkForPreApproval(AuthorizationRequest authorizationRequest, Authentication userAuthentication) {

  110.         //First, check database to see if the user identified by the userAuthentication has stored an approval decision

  111.         String userId = userAuthentication.getName();
  112.         String clientId = authorizationRequest.getClientId();

  113.         //lookup ApprovedSites by userId and clientId
  114.         boolean alreadyApproved = false;

  115.         // find out if we're supposed to force a prompt on the user or not
  116.         String prompt = (String) authorizationRequest.getExtensions().get(PROMPT);
  117.         List<String> prompts = Splitter.on(PROMPT_SEPARATOR).splitToList(Strings.nullToEmpty(prompt));
  118.         if (!prompts.contains(PROMPT_CONSENT)) {
  119.             // if the prompt parameter is set to "consent" then we can't use approved sites or whitelisted sites
  120.             // otherwise, we need to check them below

  121.             Collection<ApprovedSite> aps = approvedSiteService.getByClientIdAndUserId(clientId, userId);
  122.             for (ApprovedSite ap : aps) {

  123.                 if (!ap.isExpired()) {

  124.                     // if we find one that fits...
  125.                     if (systemScopes.scopesMatch(ap.getAllowedScopes(), authorizationRequest.getScope())) {

  126.                         //We have a match; update the access date on the AP entry and return true.
  127.                         ap.setAccessDate(new Date());
  128.                         approvedSiteService.save(ap);

  129.                         String apId = ap.getId().toString();
  130.                         authorizationRequest.getExtensions().put(APPROVED_SITE, apId);
  131.                         authorizationRequest.setApproved(true);
  132.                         alreadyApproved = true;

  133.                         setAuthTime(authorizationRequest);
  134.                     }
  135.                 }
  136.             }

  137.             if (!alreadyApproved) {
  138.                 WhitelistedSite ws = whitelistedSiteService.getByClientId(clientId);
  139.                 if (ws != null && systemScopes.scopesMatch(ws.getAllowedScopes(), authorizationRequest.getScope())) {
  140.                     authorizationRequest.setApproved(true);

  141.                     setAuthTime(authorizationRequest);
  142.                 }
  143.             }
  144.         }

  145.         return authorizationRequest;

  146.     }


  147.     @Override
  148.     public AuthorizationRequest updateAfterApproval(AuthorizationRequest authorizationRequest, Authentication userAuthentication) {

  149.         String userId = userAuthentication.getName();
  150.         String clientId = authorizationRequest.getClientId();
  151.         ClientDetails client = clientDetailsService.loadClientByClientId(clientId);

  152.         // This must be re-parsed here because SECOAUTH forces us to call things in a strange order
  153.         if (Boolean.parseBoolean(authorizationRequest.getApprovalParameters().get("user_oauth_approval"))) {

  154.             authorizationRequest.setApproved(true);

  155.             // process scopes from user input
  156.             Set<String> allowedScopes = Sets.newHashSet();
  157.             Map<String,String> approvalParams = authorizationRequest.getApprovalParameters();

  158.             Set<String> keys = approvalParams.keySet();

  159.             for (String key : keys) {
  160.                 if (key.startsWith("scope_")) {
  161.                     //This is a scope parameter from the approval page. The value sent back should
  162.                     //be the scope string. Check to make sure it is contained in the client's
  163.                     //registered allowed scopes.

  164.                     String scope = approvalParams.get(key);
  165.                     Set<String> approveSet = Sets.newHashSet(scope);

  166.                     //Make sure this scope is allowed for the given client
  167.                     if (systemScopes.scopesMatch(client.getScope(), approveSet)) {

  168.                         allowedScopes.add(scope);
  169.                     }

  170.                 }
  171.             }

  172.             // inject the user-allowed scopes into the auth request
  173.             authorizationRequest.setScope(allowedScopes);

  174.             //Only store an ApprovedSite if the user has checked "remember this decision":
  175.             String remember = authorizationRequest.getApprovalParameters().get("remember");
  176.             if (!Strings.isNullOrEmpty(remember) && !remember.equals("none")) {

  177.                 Date timeout = null;
  178.                 if (remember.equals("one-hour")) {
  179.                     // set the timeout to one hour from now
  180.                     Calendar cal = Calendar.getInstance();
  181.                     cal.add(Calendar.HOUR, 1);
  182.                     timeout = cal.getTime();
  183.                 }

  184.                 ApprovedSite newSite = approvedSiteService.createApprovedSite(clientId, userId, timeout, allowedScopes);
  185.                 String newSiteId = newSite.getId().toString();
  186.                 authorizationRequest.getExtensions().put(APPROVED_SITE, newSiteId);
  187.             }

  188.             setAuthTime(authorizationRequest);


  189.         }

  190.         return authorizationRequest;
  191.     }

  192.     /**
  193.      * Get the auth time out of the current session and add it to the
  194.      * auth request in the extensions map.
  195.      *
  196.      * @param authorizationRequest
  197.      */
  198.     private void setAuthTime(AuthorizationRequest authorizationRequest) {
  199.         // Get the session auth time, if we have it, and store it in the request
  200.         ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
  201.         if (attr != null) {
  202.             HttpSession session = attr.getRequest().getSession();
  203.             if (session != null) {
  204.                 Date authTime = (Date) session.getAttribute(AuthenticationTimeStamper.AUTH_TIMESTAMP);
  205.                 if (authTime != null) {
  206.                     String authTimeString = Long.toString(authTime.getTime());
  207.                     authorizationRequest.getExtensions().put(AuthenticationTimeStamper.AUTH_TIMESTAMP, authTimeString);
  208.                 }
  209.             }
  210.         }
  211.     }

  212.     @Override
  213.     public Map<String, Object> getUserApprovalRequest(AuthorizationRequest authorizationRequest,
  214.             Authentication userAuthentication) {
  215.         Map<String, Object> model = new HashMap<>();
  216.         // In case of a redirect we might want the request parameters to be included
  217.         model.putAll(authorizationRequest.getRequestParameters());
  218.         return model;
  219.     }

  220. }