@Test
  public void verifyValidServiceTicketWithValidPgtAndProxyHandlerFailing() throws Exception {
    final TicketGrantingTicket tId =
        getCentralAuthenticationService()
            .createTicketGrantingTicket(TestUtils.getCredentialsWithSameUsernameAndPassword());
    final ServiceTicket sId =
        getCentralAuthenticationService().grantServiceTicket(tId.getId(), TestUtils.getService());

    final MockHttpServletRequest request = new MockHttpServletRequest();
    request.addParameter("service", TestUtils.getService().getId());
    request.addParameter("ticket", sId.getId());
    request.addParameter("pgtUrl", "https://www.github.com");

    this.serviceValidateController.setProxyHandler(
        new ProxyHandler() {
          @Override
          public String handle(
              final Credential credential, final TicketGrantingTicket proxyGrantingTicketId) {
            return null;
          }

          @Override
          public boolean canHandle(final Credential credential) {
            return true;
          }
        });

    final ModelAndView modelAndView =
        this.serviceValidateController.handleRequestInternal(
            request, new MockHttpServletResponse());
    assertEquals(
        ServiceValidateController.DEFAULT_SERVICE_FAILURE_VIEW_NAME, modelAndView.getViewName());
    assertNull(modelAndView.getModel().get("pgtIou"));
  }
示例#2
0
 private void internalProxyTest(final String proxyUrl) throws MalformedURLException {
   final Credential proxyCredential =
       new HttpBasedServiceCredential(
           new URL(proxyUrl), TestUtils.getRegisteredService("https://.+"));
   final TicketGrantingTicket expectedTGT =
       new MockTicketGrantingTicket(TGT_ID, proxyCredential, this.principalAttributes);
   expectedTGT.grantServiceTicket(ST_ID, null, null, false, true);
   assertEquals(expectedTGT, transcoder.decode(transcoder.encode(expectedTGT)));
 }
示例#3
0
 @Test
 public void verifyEncodeDecodeTGTWithUnmodifiableMap() throws Exception {
   final Credential userPassCredential = new UsernamePasswordCredential(USERNAME, PASSWORD);
   final TicketGrantingTicket expectedTGT =
       new MockTicketGrantingTicket(
           TGT_ID, userPassCredential, Collections.unmodifiableMap(this.principalAttributes));
   expectedTGT.grantServiceTicket(ST_ID, null, null, false, true);
   assertEquals(expectedTGT, transcoder.decode(transcoder.encode(expectedTGT)));
 }
示例#4
0
 @Test
 public void verifyEncodeDecodeTGTWithListOrderedMap() throws Exception {
   final Credential userPassCredential = new UsernamePasswordCredential(USERNAME, PASSWORD);
   @SuppressWarnings("unchecked")
   final TicketGrantingTicket expectedTGT =
       new MockTicketGrantingTicket(TGT_ID, userPassCredential, this.principalAttributes);
   expectedTGT.grantServiceTicket(ST_ID, null, null, false, true);
   assertEquals(expectedTGT, transcoder.decode(transcoder.encode(expectedTGT)));
 }
示例#5
0
 @Test
 public void verifyEncodeDecodeTGTWithSingletonMap() throws Exception {
   final Map<String, Object> newAttributes =
       Collections.singletonMap(NICKNAME_KEY, (Object) NICKNAME_VALUE);
   final Credential userPassCredential = new UsernamePasswordCredential(USERNAME, PASSWORD);
   final TicketGrantingTicket expectedTGT =
       new MockTicketGrantingTicket(TGT_ID, userPassCredential, newAttributes);
   expectedTGT.grantServiceTicket(ST_ID, null, null, false, true);
   assertEquals(expectedTGT, transcoder.decode(transcoder.encode(expectedTGT)));
 }
示例#6
0
 @Test
 public void verifyEncodeDecodeTGTWithUnmodifiableSet() throws Exception {
   final Map<String, Object> newAttributes = new HashMap<>();
   final Set<String> values = new HashSet<>();
   values.add(NICKNAME_VALUE);
   newAttributes.put(NICKNAME_KEY, Collections.unmodifiableSet(values));
   final Credential userPassCredential = new UsernamePasswordCredential(USERNAME, PASSWORD);
   final TicketGrantingTicket expectedTGT =
       new MockTicketGrantingTicket(TGT_ID, userPassCredential, newAttributes);
   expectedTGT.grantServiceTicket(ST_ID, null, null, false, true);
   assertEquals(expectedTGT, transcoder.decode(transcoder.encode(expectedTGT)));
 }
  @Override
  protected ModelAndView handleRequestInternal(
      final HttpServletRequest request, final HttpServletResponse response) throws Exception {
    String accessToken = request.getParameter(OAuthConstants.ACCESS_TOKEN);
    if (StringUtils.isBlank(accessToken)) {
      final String authHeader = request.getHeader("Authorization");
      if (StringUtils.isNotBlank(authHeader)
          && authHeader.toLowerCase().startsWith(OAuthConstants.BEARER_TOKEN.toLowerCase() + ' ')) {
        accessToken = authHeader.substring(OAuthConstants.BEARER_TOKEN.length() + 1);
      }
    }
    LOGGER.debug("{} : {}", OAuthConstants.ACCESS_TOKEN, accessToken);

    try (final JsonGenerator jsonGenerator =
        this.jsonFactory.createJsonGenerator(response.getWriter())) {
      response.setContentType("application/json");
      // accessToken is required
      if (StringUtils.isBlank(accessToken)) {
        LOGGER.error("Missing {}", OAuthConstants.ACCESS_TOKEN);
        jsonGenerator.writeStartObject();
        jsonGenerator.writeStringField("error", OAuthConstants.MISSING_ACCESS_TOKEN);
        jsonGenerator.writeEndObject();
        return null;
      }
      // get ticket granting ticket
      final TicketGrantingTicket ticketGrantingTicket =
          (TicketGrantingTicket) this.ticketRegistry.getTicket(accessToken);
      if (ticketGrantingTicket == null || ticketGrantingTicket.isExpired()) {
        LOGGER.error("expired accessToken : {}", accessToken);
        jsonGenerator.writeStartObject();
        jsonGenerator.writeStringField("error", OAuthConstants.EXPIRED_ACCESS_TOKEN);
        jsonGenerator.writeEndObject();
        return null;
      }
      // generate profile : identifier + attributes
      final Principal principal = ticketGrantingTicket.getAuthentication().getPrincipal();
      jsonGenerator.writeStartObject();
      jsonGenerator.writeStringField(ID, principal.getId());
      jsonGenerator.writeObjectFieldStart(ATTRIBUTES);
      final Map<String, Object> attributes = principal.getAttributes();
      for (final Map.Entry<String, Object> entry : attributes.entrySet()) {
        //                jsonGenerator.writeStartObject();
        jsonGenerator.writeObjectField(entry.getKey(), entry.getValue());
        //                jsonGenerator.writeEndObject();
      }
      //            jsonGenerator.writeEndArray();
      jsonGenerator.writeEndObject();
      return null;
    } finally {
      response.flushBuffer();
    }
  }
示例#8
0
  @Test
  public void verifyEncodeDecode() throws Exception {
    final ServiceTicket expectedST = new MockServiceTicket(ST_ID);
    assertEquals(expectedST, transcoder.decode(transcoder.encode(expectedST)));

    final Credential userPassCredential = new UsernamePasswordCredential(USERNAME, PASSWORD);
    final TicketGrantingTicket expectedTGT =
        new MockTicketGrantingTicket(TGT_ID, userPassCredential, this.principalAttributes);
    expectedTGT.grantServiceTicket(ST_ID, null, null, false, true);
    assertEquals(expectedTGT, transcoder.decode(transcoder.encode(expectedTGT)));

    internalProxyTest("http://localhost");
    internalProxyTest("https://localhost:8080/path/file.html?p1=v1&p2=v2#fragment");
  }
  @Audit(
      action = "PROXY_GRANTING_TICKET",
      actionResolverName = "GRANT_PROXY_GRANTING_TICKET_RESOLVER",
      resourceResolverName = "GRANT_PROXY_GRANTING_TICKET_RESOURCE_RESOLVER")
  @Profiled(tag = "GRANT_PROXY_GRANTING_TICKET", logFailuresSeparately = false)
  @Transactional(readOnly = false)
  @Override
  public String delegateTicketGrantingTicket(
      final String serviceTicketId, final Credential... credentials)
      throws AuthenticationException, TicketException {

    Assert.notNull(serviceTicketId, "serviceTicketId cannot be null");
    Assert.notNull(credentials, "credentials cannot be null");

    final ServiceTicket serviceTicket =
        this.serviceTicketRegistry.getTicket(serviceTicketId, ServiceTicket.class);

    if (serviceTicket == null || serviceTicket.isExpired()) {
      logger.debug(
          "ServiceTicket [{}] has expired or cannot be found in the ticket registry",
          serviceTicketId);
      throw new InvalidTicketException(serviceTicketId);
    }

    final RegisteredService registeredService =
        this.servicesManager.findServiceBy(serviceTicket.getService());

    verifyRegisteredServiceProperties(registeredService, serviceTicket.getService());

    if (!registeredService.isAllowedToProxy()) {
      logger.warn(
          "ServiceManagement: Service [{}] attempted to proxy, but is not allowed.",
          serviceTicket.getService().getId());
      throw new UnauthorizedProxyingException();
    }

    final Authentication authentication = this.authenticationManager.authenticate(credentials);

    final TicketGrantingTicket ticketGrantingTicket =
        serviceTicket.grantTicketGrantingTicket(
            this.ticketGrantingTicketUniqueTicketIdGenerator.getNewTicketId(
                TicketGrantingTicket.PREFIX),
            authentication,
            this.ticketGrantingTicketExpirationPolicy);

    this.ticketRegistry.addTicket(ticketGrantingTicket);

    return ticketGrantingTicket.getId();
  }
  private HttpServletRequest getHttpServletRequest() throws Exception {
    final TicketGrantingTicket tId =
        getCentralAuthenticationService()
            .createTicketGrantingTicket(TestUtils.getCredentialsWithSameUsernameAndPassword());
    getCentralAuthenticationService().grantServiceTicket(tId.getId(), TestUtils.getService());
    final ServiceTicket sId2 =
        getCentralAuthenticationService().grantServiceTicket(tId.getId(), TestUtils.getService());

    final MockHttpServletRequest request = new MockHttpServletRequest();
    request.addParameter("service", TestUtils.getService().getId());
    request.addParameter("ticket", sId2.getId());
    request.addParameter("renew", "true");

    return request;
  }
  /**
   * Gets the authentication satisfied by policy.
   *
   * @param ticket the ticket
   * @param context the context
   * @return the authentication satisfied by policy
   * @throws org.jasig.cas.ticket.TicketException the ticket exception
   */
  private Authentication getAuthenticationSatisfiedByPolicy(
      final TicketGrantingTicket ticket, final ServiceContext context) throws TicketException {

    final ContextualAuthenticationPolicy<ServiceContext> policy =
        serviceContextAuthenticationPolicyFactory.createPolicy(context);
    if (policy.isSatisfiedBy(ticket.getAuthentication())) {
      return ticket.getAuthentication();
    }
    for (final Authentication auth : ticket.getSupplementalAuthentications()) {
      if (policy.isSatisfiedBy(auth)) {
        return auth;
      }
    }
    throw new UnsatisfiedAuthenticationPolicyException(policy);
  }
示例#12
0
  @Test
  public void verifyEncodeDecodeTGTImpl() throws Exception {
    final Credential userPassCredential = new UsernamePasswordCredential(USERNAME, PASSWORD);
    final AuthenticationBuilder bldr =
        new DefaultAuthenticationBuilder(
            new DefaultPrincipalFactory()
                .createPrincipal("user", Collections.unmodifiableMap(this.principalAttributes)));
    bldr.setAttributes(Collections.unmodifiableMap(this.principalAttributes));
    bldr.setAuthenticationDate(new DateTime());
    bldr.addCredential(new BasicCredentialMetaData(userPassCredential));
    bldr.addFailure("error", AccountNotFoundException.class);
    bldr.addSuccess(
        "authn",
        new DefaultHandlerResult(
            new AcceptUsersAuthenticationHandler(),
            new BasicCredentialMetaData(userPassCredential)));

    final TicketGrantingTicket parent =
        new TicketGrantingTicketImpl(
            TGT_ID,
            org.jasig.cas.authentication.TestUtils.getService(),
            null,
            bldr.build(),
            new NeverExpiresExpirationPolicy());

    final TicketGrantingTicket expectedTGT =
        new TicketGrantingTicketImpl(
            TGT_ID,
            org.jasig.cas.services.TestUtils.getService(),
            null,
            bldr.build(),
            new NeverExpiresExpirationPolicy());

    final ServiceTicket ticket =
        expectedTGT.grantServiceTicket(
            ST_ID,
            org.jasig.cas.services.TestUtils.getService(),
            new NeverExpiresExpirationPolicy(),
            false,
            true);
    CachedData result = transcoder.encode(expectedTGT);
    final TicketGrantingTicket resultTicket = (TicketGrantingTicket) transcoder.decode(result);

    assertEquals(expectedTGT, resultTicket);
    result = transcoder.encode(ticket);
    final ServiceTicket resultStTicket = (ServiceTicket) transcoder.decode(result);
    assertEquals(ticket, resultStTicket);
  }
  protected final ModelAndView getModelAndViewUponServiceValidationWithSecurePgtUrl()
      throws Exception {
    final TicketGrantingTicket tId =
        getCentralAuthenticationService()
            .createTicketGrantingTicket(TestUtils.getCredentialsWithSameUsernameAndPassword());
    final ServiceTicket sId =
        getCentralAuthenticationService().grantServiceTicket(tId.getId(), TestUtils.getService());

    final MockHttpServletRequest request = new MockHttpServletRequest();
    request.addParameter("service", TestUtils.getService().getId());
    request.addParameter("ticket", sId.getId());
    request.addParameter("pgtUrl", "https://www.github.com");

    return this.serviceValidateController.handleRequestInternal(
        request, new MockHttpServletResponse());
  }
  @Test
  public void verifyValidServiceTicket() throws Exception {
    final TicketGrantingTicket tId =
        getCentralAuthenticationService()
            .createTicketGrantingTicket(TestUtils.getCredentialsWithSameUsernameAndPassword());
    final ServiceTicket sId =
        getCentralAuthenticationService().grantServiceTicket(tId.getId(), TestUtils.getService());

    final MockHttpServletRequest request = new MockHttpServletRequest();
    request.addParameter("service", TestUtils.getService().getId());
    request.addParameter("ticket", sId.getId());

    assertEquals(
        ServiceValidateController.DEFAULT_SERVICE_SUCCESS_VIEW_NAME,
        this.serviceValidateController
            .handleRequestInternal(request, new MockHttpServletResponse())
            .getViewName());
  }
  @Override
  protected ModelAndView handleRequestInternal(
      final HttpServletRequest request, final HttpServletResponse response) throws Exception {
    // get CAS ticket
    final String ticket = request.getParameter(OAuthConstants.TICKET);
    log.debug("ticket : {}", ticket);

    ServiceTicket serviceTicket = (ServiceTicket) ticketRegistry.getTicket(ticket);
    TicketGrantingTicket grantingTicket = serviceTicket.getGrantingTicket();
    log.debug("granting ticket : {}", grantingTicket);
    if (grantingTicket == null) return null;

    // retrieve callback url from session
    final HttpSession session = request.getSession();
    String callbackUrl = (String) session.getAttribute(OAuthConstants.OAUTH20_CALLBACKURL);
    log.debug("callbackUrl : {}", callbackUrl);
    session.removeAttribute(OAuthConstants.OAUTH20_CALLBACKURL);
    // and state
    final String state = (String) session.getAttribute(OAuthConstants.OAUTH20_STATE);
    log.debug("state : {}", state);
    session.removeAttribute(OAuthConstants.OAUTH20_STATE);

    if (callbackUrl == null) return null;

    // return callback url with code & state
    callbackUrl = OAuthUtils.addParameter(callbackUrl, OAuthConstants.CODE, ticket);
    if (state != null) {
      callbackUrl = OAuthUtils.addParameter(callbackUrl, OAuthConstants.STATE, state);
    }
    log.debug("callbackUrl : {}", callbackUrl);

    final Map<String, Object> model = new HashMap<String, Object>();
    model.put("callbackUrl", callbackUrl);
    // retrieve service name from session
    final String serviceName = (String) session.getAttribute(OAuthConstants.OAUTH20_SERVICE_NAME);
    log.debug("serviceName : {}", serviceName);
    model.put("serviceName", serviceName);

    model.put("userId", grantingTicket.getAuthentication().getPrincipal().getId());

    return new ModelAndView(OAuthConstants.CONFIRM_VIEW, model);
  }
  @Test
  public void verifyValidServiceTicketWithValidPgtAndProxyHandling() throws Exception {
    final TicketGrantingTicket tId =
        getCentralAuthenticationService()
            .createTicketGrantingTicket(TestUtils.getCredentialsWithSameUsernameAndPassword());
    final ServiceTicket sId =
        getCentralAuthenticationService().grantServiceTicket(tId.getId(), TestUtils.getService());

    final MockHttpServletRequest request = new MockHttpServletRequest();
    request.addParameter("service", TestUtils.getService().getId());
    request.addParameter("ticket", sId.getId());
    request.addParameter("pgtUrl", "https://www.github.com");

    final ModelAndView modelAndView =
        this.serviceValidateController.handleRequestInternal(
            request, new MockHttpServletResponse());
    assertEquals(
        ServiceValidateController.DEFAULT_SERVICE_SUCCESS_VIEW_NAME, modelAndView.getViewName());
    assertNotNull(modelAndView.getModel().get("pgtIou"));
  }
  @Test
  public void verifyValidServiceTicketWithDifferentEncoding() throws Exception {
    this.serviceValidateController.setProxyHandler(new Cas10ProxyHandler());
    final TicketGrantingTicket tId =
        getCentralAuthenticationService()
            .createTicketGrantingTicket(TestUtils.getCredentialsWithSameUsernameAndPassword());

    final String origSvc = "http://www.jasig.org?param=hello+world";
    final ServiceTicket sId =
        getCentralAuthenticationService()
            .grantServiceTicket(tId.getId(), TestUtils.getService(origSvc));

    final String reqSvc = "http://www.jasig.org?param=hello%20world";

    final MockHttpServletRequest request = new MockHttpServletRequest();
    request.addParameter("service", TestUtils.getService(reqSvc).getId());
    request.addParameter("ticket", sId.getId());

    assertEquals(
        ServiceValidateController.DEFAULT_SERVICE_SUCCESS_VIEW_NAME,
        this.serviceValidateController
            .handleRequestInternal(request, new MockHttpServletResponse())
            .getViewName());
  }
  @Audit(
      action = "TICKET_GRANTING_TICKET",
      actionResolverName = "CREATE_TICKET_GRANTING_TICKET_RESOLVER",
      resourceResolverName = "CREATE_TICKET_GRANTING_TICKET_RESOURCE_RESOLVER")
  @Profiled(tag = "CREATE_TICKET_GRANTING_TICKET", logFailuresSeparately = false)
  @Transactional(readOnly = false)
  @Override
  public String createTicketGrantingTicket(final Credential... credentials)
      throws AuthenticationException, TicketException {

    Assert.notNull(credentials, "credentials cannot be null");

    final Authentication authentication = this.authenticationManager.authenticate(credentials);

    final TicketGrantingTicket ticketGrantingTicket =
        new TicketGrantingTicketImpl(
            this.ticketGrantingTicketUniqueTicketIdGenerator.getNewTicketId(
                TicketGrantingTicket.PREFIX),
            authentication,
            this.ticketGrantingTicketExpirationPolicy);

    this.ticketRegistry.addTicket(ticketGrantingTicket);
    return ticketGrantingTicket.getId();
  }
  /**
   * Gets sso sessions.
   *
   * @param option the option
   * @return the sso sessions
   */
  private Collection<Map<String, Object>> getActiveSsoSessions(
      final SsoSessionReportOptions option) {
    final Collection<Map<String, Object>> activeSessions = new ArrayList<>();
    final ISOStandardDateFormat dateFormat = new ISOStandardDateFormat();

    for (final Ticket ticket : getNonExpiredTicketGrantingTickets()) {
      final TicketGrantingTicket tgt = (TicketGrantingTicket) ticket;

      if (option == SsoSessionReportOptions.DIRECT && tgt.getProxiedBy() != null) {
        continue;
      }

      final Authentication authentication = tgt.getAuthentication();
      final Principal principal = authentication.getPrincipal();

      final Map<String, Object> sso = new HashMap<>(SsoSessionAttributeKeys.values().length);
      sso.put(SsoSessionAttributeKeys.AUTHENTICATED_PRINCIPAL.toString(), principal.getId());
      sso.put(
          SsoSessionAttributeKeys.AUTHENTICATION_DATE.toString(),
          authentication.getAuthenticationDate());
      sso.put(
          SsoSessionAttributeKeys.AUTHENTICATION_DATE_FORMATTED.toString(),
          dateFormat.format(authentication.getAuthenticationDate()));
      sso.put(SsoSessionAttributeKeys.NUMBER_OF_USES.toString(), tgt.getCountOfUses());
      sso.put(SsoSessionAttributeKeys.TICKET_GRANTING_TICKET.toString(), tgt.getId());
      sso.put(SsoSessionAttributeKeys.PRINCIPAL_ATTRIBUTES.toString(), principal.getAttributes());
      sso.put(
          SsoSessionAttributeKeys.AUTHENTICATION_ATTRIBUTES.toString(),
          authentication.getAttributes());

      if (option != SsoSessionReportOptions.DIRECT) {
        if (tgt.getProxiedBy() != null) {
          sso.put(SsoSessionAttributeKeys.IS_PROXIED.toString(), Boolean.TRUE);
          sso.put(SsoSessionAttributeKeys.PROXIED_BY.toString(), tgt.getProxiedBy().getId());
        } else {
          sso.put(SsoSessionAttributeKeys.IS_PROXIED.toString(), Boolean.FALSE);
        }
      }

      sso.put(SsoSessionAttributeKeys.AUTHENTICATED_SERVICES.toString(), tgt.getServices());

      activeSessions.add(sso);
    }
    return activeSessions;
  }
  @Audit(
      action = "SERVICE_TICKET",
      actionResolverName = "GRANT_SERVICE_TICKET_RESOLVER",
      resourceResolverName = "GRANT_SERVICE_TICKET_RESOURCE_RESOLVER")
  @Profiled(tag = "GRANT_SERVICE_TICKET", logFailuresSeparately = false)
  @Transactional(readOnly = false)
  @Override
  public String grantServiceTicket(
      final String ticketGrantingTicketId, final Service service, final Credential... credentials)
      throws AuthenticationException, TicketException {
    Assert.notNull(ticketGrantingTicketId, "ticketGrantingticketId cannot be null");
    Assert.notNull(service, "service cannot be null");

    final TicketGrantingTicket ticketGrantingTicket =
        this.ticketRegistry.getTicket(ticketGrantingTicketId, TicketGrantingTicket.class);

    if (ticketGrantingTicket == null) {
      logger.debug(
          "TicketGrantingTicket [{}] cannot be found in the ticket registry.",
          ticketGrantingTicketId);
      throw new InvalidTicketException(ticketGrantingTicketId);
    }

    synchronized (ticketGrantingTicket) {
      if (ticketGrantingTicket.isExpired()) {
        this.ticketRegistry.deleteTicket(ticketGrantingTicketId);
        logger.debug(
            "TicketGrantingTicket[{}] has expired and is now deleted from the ticket registry.",
            ticketGrantingTicketId);
        throw new InvalidTicketException(ticketGrantingTicketId);
      }
    }

    final RegisteredService registeredService = this.servicesManager.findServiceBy(service);

    verifyRegisteredServiceProperties(registeredService, service);

    if (!registeredService.isSsoEnabled()
        && credentials == null
        && ticketGrantingTicket.getCountOfUses() > 0) {
      logger.warn("ServiceManagement: Service [{}] is not allowed to use SSO.", service.getId());
      throw new UnauthorizedSsoServiceException();
    }

    // CAS-1019
    final List<Authentication> authns = ticketGrantingTicket.getChainedAuthentications();
    if (authns.size() > 1) {
      if (!registeredService.isAllowedToProxy()) {
        final String message =
            String.format(
                "ServiceManagement: Proxy attempt by service [%s] (registered service [%s]) is not allowed.",
                service.getId(), registeredService.toString());
        logger.warn(message);
        throw new UnauthorizedProxyingException(message);
      }
    }

    if (credentials != null) {
      final Authentication current = this.authenticationManager.authenticate(credentials);
      final Authentication original = ticketGrantingTicket.getAuthentication();
      if (!current.getPrincipal().equals(original.getPrincipal())) {
        throw new MixedPrincipalException(current, current.getPrincipal(), original.getPrincipal());
      }
      ticketGrantingTicket.getSupplementalAuthentications().add(current);
    }

    // Perform security policy check by getting the authentication that satisfies the configured
    // policy
    // This throws if no suitable policy is found
    getAuthenticationSatisfiedByPolicy(
        ticketGrantingTicket, new ServiceContext(service, registeredService));

    final String uniqueTicketIdGenKey = service.getClass().getName();
    if (!this.uniqueTicketIdGeneratorsForService.containsKey(uniqueTicketIdGenKey)) {
      logger.warn(
          "Cannot create service ticket because the key [{}] for service [{}] is not linked to a ticket id generator",
          uniqueTicketIdGenKey,
          service.getId());
      throw new UnauthorizedSsoServiceException();
    }

    final UniqueTicketIdGenerator serviceTicketUniqueTicketIdGenerator =
        this.uniqueTicketIdGeneratorsForService.get(uniqueTicketIdGenKey);

    final String generatedServiceTicketId =
        serviceTicketUniqueTicketIdGenerator.getNewTicketId(ServiceTicket.PREFIX);
    logger.debug(
        "Generated service ticket id [{}] for ticket granting ticket [{}]",
        generatedServiceTicketId,
        ticketGrantingTicket.getId());

    final ServiceTicket serviceTicket =
        ticketGrantingTicket.grantServiceTicket(
            generatedServiceTicketId,
            service,
            this.serviceTicketExpirationPolicy,
            credentials != null);

    this.serviceTicketRegistry.addTicket(serviceTicket);

    if (logger.isInfoEnabled()) {
      final List<Authentication> authentications =
          serviceTicket.getGrantingTicket().getChainedAuthentications();
      final String formatString = "Granted %s ticket [%s] for service [%s] for user [%s]";
      final String type;
      final String principalId =
          authentications.get(authentications.size() - 1).getPrincipal().getId();

      if (authentications.size() == 1) {
        type = "service";
      } else {
        type = "proxy";
      }

      logger.info(
          String.format(formatString, type, serviceTicket.getId(), service.getId(), principalId));
    }

    return serviceTicket.getId();
  }
  @Audit(
      action = "SERVICE_TICKET",
      actionResolverName = "GRANT_SERVICE_TICKET_RESOLVER",
      resourceResolverName = "GRANT_SERVICE_TICKET_RESOURCE_RESOLVER")
  @Timed(name = "GRANT_SERVICE_TICKET_TIMER")
  @Metered(name = "GRANT_SERVICE_TICKET_METER")
  @Counted(name = "GRANT_SERVICE_TICKET_COUNTER", monotonic = true)
  @Transactional(readOnly = false)
  @Override
  public ServiceTicket grantServiceTicket(
      final String ticketGrantingTicketId, final Service service, final Credential... credentials)
      throws AuthenticationException, TicketException {

    final TicketGrantingTicket ticketGrantingTicket =
        getTicket(ticketGrantingTicketId, TicketGrantingTicket.class);
    final RegisteredService registeredService = this.servicesManager.findServiceBy(service);

    verifyRegisteredServiceProperties(registeredService, service);
    final Set<Credential> sanitizedCredentials = sanitizeCredentials(credentials);

    Authentication currentAuthentication = null;
    if (sanitizedCredentials.size() > 0) {
      currentAuthentication =
          this.authenticationManager.authenticate(
              sanitizedCredentials.toArray(new Credential[] {}));
      final Authentication original = ticketGrantingTicket.getAuthentication();
      if (!currentAuthentication.getPrincipal().equals(original.getPrincipal())) {
        throw new MixedPrincipalException(
            currentAuthentication, currentAuthentication.getPrincipal(), original.getPrincipal());
      }
      ticketGrantingTicket.getSupplementalAuthentications().add(currentAuthentication);
    }

    if (currentAuthentication == null
        && !registeredService.getAccessStrategy().isServiceAccessAllowedForSso()) {
      logger.warn("ServiceManagement: Service [{}] is not allowed to use SSO.", service.getId());
      throw new UnauthorizedSsoServiceException();
    }

    final Service proxiedBy = ticketGrantingTicket.getProxiedBy();
    if (proxiedBy != null) {
      logger.debug(
          "TGT is proxied by [{}]. Locating proxy service in registry...", proxiedBy.getId());
      final RegisteredService proxyingService = servicesManager.findServiceBy(proxiedBy);

      if (proxyingService != null) {
        logger.debug("Located proxying service [{}] in the service registry", proxyingService);
        if (!proxyingService.getProxyPolicy().isAllowedToProxy()) {
          logger.warn(
              "Found proxying service {}, but it is not authorized to fulfill the proxy attempt made by {}",
              proxyingService.getId(),
              service.getId());
          throw new UnauthorizedProxyingException(
              "Proxying is not allowed for registered service " + registeredService.getId());
        }
      } else {
        logger.warn(
            "No proxying service found. Proxy attempt by service [{}] (registered service [{}]) is not allowed.",
            service.getId(),
            registeredService.getId());
        throw new UnauthorizedProxyingException(
            "Proxying is not allowed for registered service " + registeredService.getId());
      }
    } else {
      logger.trace("TGT is not proxied by another service");
    }

    // Perform security policy check by getting the authentication that satisfies the configured
    // policy
    // This throws if no suitable policy is found
    getAuthenticationSatisfiedByPolicy(
        ticketGrantingTicket, new ServiceContext(service, registeredService));

    final List<Authentication> authentications = ticketGrantingTicket.getChainedAuthentications();
    final Principal principal = authentications.get(authentications.size() - 1).getPrincipal();

    final Map<String, Object> principalAttrs =
        registeredService.getAttributeReleasePolicy().getAttributes(principal);
    if (!registeredService
        .getAccessStrategy()
        .doPrincipalAttributesAllowServiceAccess(principalAttrs)) {
      logger.warn(
          "ServiceManagement: Cannot grant service ticket because Service [{}] is not authorized for use by [{}].",
          service.getId(),
          principal);
      throw new UnauthorizedServiceForPrincipalException();
    }

    final String uniqueTicketIdGenKey = service.getClass().getName();
    logger.debug("Looking up service ticket id generator for [{}]", uniqueTicketIdGenKey);
    UniqueTicketIdGenerator serviceTicketUniqueTicketIdGenerator =
        this.uniqueTicketIdGeneratorsForService.get(uniqueTicketIdGenKey);
    if (serviceTicketUniqueTicketIdGenerator == null) {
      serviceTicketUniqueTicketIdGenerator = this.defaultServiceTicketIdGenerator;
      logger.debug(
          "Service ticket id generator not found for [{}]. Using the default generator...",
          uniqueTicketIdGenKey);
    }

    final String ticketPrefix =
        authentications.size() == 1 ? ServiceTicket.PREFIX : ServiceTicket.PROXY_TICKET_PREFIX;
    final String ticketId = serviceTicketUniqueTicketIdGenerator.getNewTicketId(ticketPrefix);
    final ServiceTicket serviceTicket =
        ticketGrantingTicket.grantServiceTicket(
            ticketId, service, this.serviceTicketExpirationPolicy, currentAuthentication != null);

    this.serviceTicketRegistry.addTicket(serviceTicket);

    logger.info(
        "Granted ticket [{}] for service [{}] for user [{}]",
        serviceTicket.getId(),
        service.getId(),
        principal.getId());

    return serviceTicket;
  }
  @Override
  protected ModelAndView handleRequestInternal(
      final HttpServletRequest request, final HttpServletResponse response) throws Exception {
    final HttpSession session = request.getSession();

    // get cas login service ticket
    final String serviceTicketId = request.getParameter(OAuthConstants.TICKET);
    LOGGER.debug("{} : {}", OAuthConstants.TICKET, serviceTicketId);

    // first time this url is requested the login ticket will be a query parameter
    if (serviceTicketId != null) {
      // create the login ticket granting ticket
      final ServiceTicket serviceTicket = (ServiceTicket) ticketRegistry.getTicket(serviceTicketId);
      // login service ticket should be valid
      if (serviceTicket == null || serviceTicket.isExpired()) {
        LOGGER.error("Service Ticket expired : {}", serviceTicketId);
        return OAuthUtils.writeTextError(
            response, OAuthConstants.INVALID_GRANT, HttpStatus.SC_BAD_REQUEST);
      }
      final TicketGrantingTicket ticketGrantingTicket = serviceTicket.getGrantingTicket();
      // remove login service ticket
      ticketRegistry.deleteTicket(serviceTicket.getId());

      // store the login tgt id in the user's session, used to create service tickets for validation
      // and
      // oauth credentials later in the flow.
      session.setAttribute(OAuthConstants.OAUTH20_LOGIN_TICKET_ID, ticketGrantingTicket.getId());

      // redirect back to self, clears the service ticket from the url, allows the url to be
      // requested multiple
      // times w/o error
      return OAuthUtils.redirectTo(request.getRequestURL().toString());
    }

    // get cas login service ticket from the session
    String ticketGrantingTicketId =
        (String) session.getAttribute(OAuthConstants.OAUTH20_LOGIN_TICKET_ID);
    LOGGER.debug("{} : {}", OAuthConstants.TICKET, ticketGrantingTicketId);
    if (StringUtils.isBlank(ticketGrantingTicketId)) {
      LOGGER.error("Missing Ticket Granting Ticket");
      return OAuthUtils.writeTextError(
          response, OAuthConstants.INVALID_GRANT, HttpStatus.SC_BAD_REQUEST);
    }

    // verify the login ticket granting ticket is still valid
    TicketGrantingTicket ticketGrantingTicket =
        (TicketGrantingTicket) ticketRegistry.getTicket(ticketGrantingTicketId);
    if (ticketGrantingTicket == null || ticketGrantingTicket.isExpired()) {
      LOGGER.error("Ticket Granting Ticket expired : {}", ticketGrantingTicketId);
      return OAuthUtils.writeTextError(
          response, OAuthConstants.INVALID_GRANT, HttpStatus.SC_BAD_REQUEST);
    }

    String callbackUrl =
        request
            .getRequestURL()
            .toString()
            .replace(
                "/" + OAuthConstants.CALLBACK_AUTHORIZE_URL,
                "/" + OAuthConstants.CALLBACK_AUTHORIZE_ACTION_URL);
    LOGGER.debug("{} : {}", OAuthConstants.CALLBACK_AUTHORIZE_ACTION_URL, callbackUrl);

    String allowCallbackUrl =
        OAuthUtils.addParameter(
            callbackUrl,
            OAuthConstants.OAUTH20_APPROVAL_PROMPT_ACTION,
            OAuthConstants.OAUTH20_APPROVAL_PROMPT_ACTION_ALLOW);

    final Map<String, Object> model = new HashMap<>();
    model.put("callbackUrl", callbackUrl);

    final Boolean bypassApprovalPrompt =
        (Boolean) session.getAttribute(OAuthConstants.BYPASS_APPROVAL_PROMPT);
    LOGGER.debug("bypassApprovalPrompt : {}", bypassApprovalPrompt);
    if (bypassApprovalPrompt != null && bypassApprovalPrompt) {
      return OAuthUtils.redirectTo(allowCallbackUrl);
    }

    final String clientId = (String) session.getAttribute(OAuthConstants.OAUTH20_CLIENT_ID);
    LOGGER.debug("{} : {}", OAuthConstants.CLIENT_ID, clientId);

    final Principal loginPrincipal = ticketGrantingTicket.getAuthentication().getPrincipal();

    final String approvalPrompt =
        (String) session.getAttribute(OAuthConstants.OAUTH20_APPROVAL_PROMPT);
    LOGGER.debug("approvalPrompt : {}", approvalPrompt);
    if (StringUtils.isBlank(approvalPrompt)
        || !approvalPrompt.equalsIgnoreCase(OAuthConstants.APPROVAL_PROMPT_FORCE)) {
      final TicketGrantingTicket refreshToken =
          OAuthTokenUtils.getRefreshToken(centralAuthenticationService, clientId, loginPrincipal);
      if (refreshToken != null) {
        return OAuthUtils.redirectTo(allowCallbackUrl);
      }
    }

    // retrieve service name from session
    final String serviceName = (String) session.getAttribute(OAuthConstants.OAUTH20_SERVICE_NAME);
    LOGGER.debug("serviceName : {}", serviceName);
    model.put("serviceName", serviceName);

    return new ModelAndView(OAuthConstants.CONFIRM_VIEW, model);
  }