@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();
    }
  }
 private JSONObject createJSONPrinciple(Authentication auth) {
   Principal principal = auth.getPrincipal();
   JSONObject principle = new JSONObject();
   principle.put("id", principal.getId());
   JSONArray jsonResult = new JSONArray();
   for (Map.Entry<String, Object> entry : principal.getAttributes().entrySet()) {
     JSONObject attribute = new JSONObject();
     attribute.put(entry.getKey(), entry.getValue());
     jsonResult.add(attribute);
   }
   principle.put("attributes", jsonResult);
   return principle;
 }
  /**
   * 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;
  }
  /**
   * Determines the principal id to use for a {@link RegisteredService} using the following rules:
   *
   * <ul>
   *   <li>If the service is marked to allow anonymous access, a persistent id is returned.
   *   <li>If the {@link org.jasig.cas.services.RegisteredService#getUsernameAttribute()} is blank,
   *       then the default principal id is returned.
   *   <li>If the username attribute is available as part of the principal's attributes, the
   *       corresponding attribute value will be returned.
   *   <li>Otherwise, the default principal's id is returned as the username attribute with an
   *       additional warning.
   * </ul>
   *
   * @param principal The principal object to be validated and constructed
   * @param registeredService Requesting service for which a principal is being validated.
   * @param serviceTicket An instance of the service ticket used for validation
   * @return The principal id to use for the requesting registered service
   */
  private String determinePrincipalIdForRegisteredService(
      final Principal principal,
      final RegisteredService registeredService,
      final ServiceTicket serviceTicket) {
    String principalId = null;
    final String serviceUsernameAttribute = registeredService.getUsernameAttribute();

    if (registeredService.isAnonymousAccess()) {
      principalId = this.persistentIdGenerator.generate(principal, serviceTicket.getService());
    } else if (StringUtils.isBlank(serviceUsernameAttribute)) {
      principalId = principal.getId();
    } else {
      if (principal.getAttributes().containsKey(serviceUsernameAttribute)) {
        principalId = principal.getAttributes().get(serviceUsernameAttribute).toString();
      } else {
        principalId = principal.getId();
        final Object[] errorLogParameters =
            new Object[] {
              principalId,
              registeredService.getUsernameAttribute(),
              principal.getAttributes(),
              registeredService.getServiceId(),
              principalId
            };
        logger.warn(
            "Principal [{}] did not have attribute [{}] among attributes [{}] so CAS cannot "
                + "provide on the validation response the user attribute the registered service [{}] expects. "
                + "CAS will instead return the default username attribute [{}]",
            errorLogParameters);
      }
    }

    logger.debug(
        "Principal id to return for service [{}] is [{}]. The default principal id is [{}].",
        new Object[] {registeredService.getName(), principal.getId(), principalId});
    return principalId;
  }
  @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;
  }