@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(); }
public boolean matches(final Service service) { return service != null && PATH_MATCHER.match(this.serviceId.toLowerCase(), service.getId().toLowerCase()); }