@Override
  protected Event doExecute(final RequestContext context) throws Exception {
    final Service service = WebUtils.getService(context);
    // No service == plain /login request. Return success indicating transition to the login form
    if (service == null) {
      return success();
    }
    final RegisteredService registeredService = this.servicesManager.findServiceBy(service);

    if (registeredService == null) {
      logger.warn(
          "Unauthorized Service Access for Service: [ {} ] - service is not defined in the service registry.",
          service.getId());
      throw new UnauthorizedServiceException();
    } else if (!registeredService.isEnabled()) {
      logger.warn(
          "Unauthorized Service Access for Service: [ {} ] - service is not enabled in the service registry.",
          service.getId());
      if (registeredService instanceof RegisteredServiceWithAttributes) {
        String disabledServiceUrl =
            (String)
                RegisteredServiceWithAttributes.class
                    .cast(registeredService)
                    .getExtraAttributes()
                    .get(DISABLED_SERVICE_URL_ATTRIBUTE);
        if (disabledServiceUrl != null) {
          context.getRequestScope().put(DISABLED_SERVICE_URL_ATTRIBUTE, disabledServiceUrl);
          return no();
        }
      }
      throw new UnauthorizedServiceException();
    }
    return success();
  }
  @Override
  protected Event doExecute(final RequestContext context) throws Exception {
    final Service service = WebUtils.getService(context);

    final boolean match = this.servicesManager.matchesExistingService(service);

    if (match) {
      return success();
    }

    throw new UnauthorizedServiceException(
        String.format("Service [%s] is not authorized to use CAS.", service.getId()));
  }
 /**
  * Normalize the path of a service by removing the query string and everything after a semi-colon.
  *
  * @param service the service to normalize
  * @return the normalized path
  */
 private static String normalizePath(final Service service) {
   String path = service.getId();
   path = StringUtils.substringBefore(path, "?");
   path = StringUtils.substringBefore(path, ";");
   path = StringUtils.substringBefore(path, "#");
   return path;
 }
  @Override
  protected Event doExecute(final RequestContext context) throws Exception {
    final Service service = WebUtils.getService(context);

    if (service == null) {
      logger.debug("No service found in the request context, so resuming normally.");
      return success();
    }

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

    if (registeredService == null) {
      logger.warn(
          "Unauthorized Service Access for Service: [{}] - service is not defined in the service registry.",
          service.getId());
      throw new UnauthorizedServiceException();
    }

    if (!registeredService.isEnabled()) {
      logger.warn(
          "Unauthorized Service Access for Service: [{}] - service is not enabled in the service registry.",
          service.getId());
      throw new UnauthorizedServiceException();
    }

    if (registeredService instanceof RegisteredServiceWithAttributes) {
      final RegisteredServiceWithAttributes regSvcWithAttr =
          RegisteredServiceWithAttributes.class.cast(registeredService);

      final String redirectToUrl =
          (String) regSvcWithAttr.getExtraAttributes().get(REDIRECT_TO_URL_ATTRIBUTE);
      if (redirectToUrl != null
          && this.redirectionAdvisor.shouldRedirectServiceRequest(
              context, regSvcWithAttr, redirectToUrl)) {
        logger.info("Redirecting to url [{}] for service [{}]", redirectToUrl, service.getId());
        context.getRequestScope().put(REDIRECT_TO_URL_ATTRIBUTE, redirectToUrl);
        return yes();
      }
    }

    logger.debug(
        "No redirect url is configured, or redirection for service [{}] is not needed",
        service.getId());
    return success();
  }
  @Override
  protected void prepareResponse(final Response response, final Map<String, Object> model) {

    final DateTime issuedAt = response.getIssueInstant();
    final Service service = getAssertionFrom(model).getService();

    final Authentication authentication = getPrimaryAuthenticationFrom(model);
    final String authenticationMethod =
        (String)
            authentication
                .getAttributes()
                .get(SamlAuthenticationMetaDataPopulator.ATTRIBUTE_AUTHENTICATION_METHOD);

    final AuthenticationStatement authnStatement =
        this.samlObjectBuilder.newAuthenticationStatement(
            authentication.getAuthenticationDate(),
            authenticationMethod,
            getPrincipal(model).getId());

    final Assertion assertion =
        this.samlObjectBuilder.newAssertion(
            authnStatement, this.issuer, issuedAt, this.samlObjectBuilder.generateSecureRandomId());
    final Conditions conditions =
        this.samlObjectBuilder.newConditions(issuedAt, service.getId(), this.issueLength);
    assertion.setConditions(conditions);

    final Subject subject = this.samlObjectBuilder.newSubject(getPrincipal(model).getId());
    final Map<String, Object> attributesToSend = prepareSamlAttributes(model, service);

    if (!attributesToSend.isEmpty()) {
      assertion
          .getAttributeStatements()
          .add(
              this.samlObjectBuilder.newAttributeStatement(
                  subject, attributesToSend, VALIDATION_SAML_ATTRIBUTE_NAMESPACE));
    }

    response.setStatus(this.samlObjectBuilder.newStatus(StatusCode.SUCCESS, null));
    response.getAssertions().add(assertion);
  }
  @Test
  public void verifyValidServiceTicketAndPgtUrlMismatch() throws Exception {
    final TicketGrantingTicket tId =
        getCentralAuthenticationService()
            .createTicketGrantingTicket(TestUtils.getCredentialsWithSameUsernameAndPassword());

    final Service svc = TestUtils.getService("proxyService");
    final ServiceTicket sId =
        getCentralAuthenticationService().grantServiceTicket(tId.getId(), svc);

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

    final ModelAndView modelAndView =
        this.serviceValidateController.handleRequestInternal(
            request, new MockHttpServletResponse());
    assertEquals(
        ServiceValidateController.DEFAULT_SERVICE_FAILURE_VIEW_NAME, modelAndView.getViewName());
    assertNull(modelAndView.getModel().get("pgtIou"));
  }
  /**
   * Ensure that the service is found and enabled in the service registry.
   *
   * @param registeredService the located entry in the registry
   * @param service authenticating service
   * @throws UnauthorizedServiceException
   */
  private void verifyRegisteredServiceProperties(
      final RegisteredService registeredService, final Service service) {
    if (registeredService == null) {
      final String msg =
          String.format(
              "ServiceManagement: Unauthorized Service Access. "
                  + "Service [%s] is not found in service registry.",
              service.getId());
      logger.warn(msg);
      throw new UnauthorizedServiceException(
          UnauthorizedServiceException.CODE_UNAUTHZ_SERVICE, msg);
    }
    if (!registeredService.getAccessStrategy().isServiceAccessAllowed()) {
      final String msg =
          String.format(
              "ServiceManagement: Unauthorized Service Access. "
                  + "Service [%s] is not enabled in service registry.",
              service.getId());

      logger.warn(msg);
      throw new UnauthorizedServiceException(
          UnauthorizedServiceException.CODE_UNAUTHZ_SERVICE, msg);
    }
  }
  /**
   * Update service and track session.
   *
   * @param id the id
   * @param service the service
   * @param onlyTrackMostRecentSession the only track most recent session
   */
  protected void updateServiceAndTrackSession(
      final String id, final Service service, final boolean onlyTrackMostRecentSession) {
    updateState();

    final List<Authentication> authentications = getChainedAuthentications();
    service.setPrincipal(authentications.get(authentications.size() - 1).getPrincipal());

    if (onlyTrackMostRecentSession) {
      final String path = normalizePath(service);
      final Collection<Service> existingServices = services.values();
      // loop on existing services
      for (final Service existingService : existingServices) {
        final String existingPath = normalizePath(existingService);
        // if an existing service has the same normalized path, remove it
        // and its service ticket to keep the latest one
        if (StringUtils.equals(path, existingPath)) {
          existingServices.remove(existingService);
          LOGGER.trace("Removed previous tickets for service: {}", existingService);
          break;
        }
      }
    }
    this.services.put(id, service);
  }
  @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;
  }
 public boolean matches(final Service service) {
   if (servicePattern == null) {
     servicePattern = createPattern(serviceId);
   }
   return service != null && servicePattern.matcher(service.getId()).matches();
 }
  @Override
  protected void renderMergedOutputModel(
      final Map model, final HttpServletRequest request, final HttpServletResponse response)
      throws Exception {

    try {
      final Assertion assertion = getAssertionFrom(model);
      final Authentication authentication = assertion.getChainedAuthentications().get(0);
      final Date currentDate = new Date();
      final String authenticationMethod =
          (String)
              authentication
                  .getAttributes()
                  .get(SamlAuthenticationMetaDataPopulator.ATTRIBUTE_AUTHENTICATION_METHOD);
      final Service service = assertion.getService();
      final SAMLResponse samlResponse =
          new SAMLResponse(null, service.getId(), new ArrayList<Object>(), null);
      final boolean isRemembered =
          (authentication
                      .getAttributes()
                      .get(RememberMeCredentials.AUTHENTICATION_ATTRIBUTE_REMEMBER_ME)
                  == Boolean.TRUE
              && !assertion.isFromNewLogin());

      samlResponse.setIssueInstant(currentDate);

      // this should be true, but we never enforced it, so we need to check to be safe
      if (service instanceof SamlService) {
        final SamlService samlService = (SamlService) service;

        if (samlService.getRequestID() != null) {
          samlResponse.setInResponseTo(samlService.getRequestID());
        }
      }

      final SAMLAssertion samlAssertion = new SAMLAssertion();
      samlAssertion.setIssueInstant(currentDate);
      samlAssertion.setIssuer(this.issuer);
      samlAssertion.setNotBefore(currentDate);
      samlAssertion.setNotOnOrAfter(new Date(currentDate.getTime() + this.issueLength));

      final SAMLAudienceRestrictionCondition samlAudienceRestrictionCondition =
          new SAMLAudienceRestrictionCondition();
      samlAudienceRestrictionCondition.addAudience(service.getId());

      final SAMLAuthenticationStatement samlAuthenticationStatement =
          new SAMLAuthenticationStatement();
      samlAuthenticationStatement.setAuthInstant(authentication.getAuthenticatedDate());
      samlAuthenticationStatement.setAuthMethod(
          authenticationMethod != null
              ? authenticationMethod
              : SAMLAuthenticationStatement.AuthenticationMethod_Unspecified);

      samlAuthenticationStatement.setSubject(getSamlSubject(authentication));

      if (!authentication.getPrincipal().getAttributes().isEmpty() || isRemembered) {
        final SAMLAttributeStatement attributeStatement = new SAMLAttributeStatement();

        attributeStatement.setSubject(getSamlSubject(authentication));
        samlAssertion.addStatement(attributeStatement);

        for (final Entry<String, Object> e :
            authentication.getPrincipal().getAttributes().entrySet()) {
          final SAMLAttribute attribute = new SAMLAttribute();
          attribute.setName(e.getKey());
          attribute.setNamespace(NAMESPACE);

          if (e.getValue() instanceof Collection<?>) {
            final Collection<?> c = (Collection<?>) e.getValue();
            if (c.isEmpty()) {
              // 100323 bnoordhuis: don't add the attribute, it causes a
              // org.opensaml.MalformedException
              continue;
            }
            attribute.setValues(c);
          } else {
            attribute.addValue(e.getValue());
          }

          attributeStatement.addAttribute(attribute);
        }

        if (isRemembered) {
          final SAMLAttribute attribute = new SAMLAttribute();
          attribute.setName(REMEMBER_ME_ATTRIBUTE_NAME);
          attribute.setNamespace(NAMESPACE);
          attribute.addValue(true);
          attributeStatement.addAttribute(attribute);
        }
      }

      samlAssertion.addStatement(samlAuthenticationStatement);
      samlAssertion.addCondition(samlAudienceRestrictionCondition);
      samlResponse.addAssertion(samlAssertion);

      final String xmlResponse = samlResponse.toString();

      response.setContentType("text/xml; charset=" + this.encoding);
      response.getWriter().print("<?xml version=\"1.0\" encoding=\"" + this.encoding + "\"?>");
      response
          .getWriter()
          .print(
              "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"><SOAP-ENV:Header/><SOAP-ENV:Body>");
      response.getWriter().print(xmlResponse);
      response.getWriter().print("</SOAP-ENV:Body></SOAP-ENV:Envelope>");
      response.flushBuffer();
    } catch (final Exception e) {
      log.error(e.getMessage(), e);
      throw e;
    }
  }
  @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();
  }
Example #13
0
 public boolean matches(final Service service) {
   return service != null
       && PATH_MATCHER.match(this.serviceId.toLowerCase(), service.getId().toLowerCase());
 }