package org.apereo.cas.support.rest; import org.apache.commons.lang3.StringUtils; import org.apereo.cas.CentralAuthenticationService; import org.apereo.cas.configuration.CasConfigurationProperties; import org.apereo.cas.services.DefaultRegisteredServiceAccessStrategy; import org.apereo.cas.services.RegexRegisteredService; import org.apereo.cas.services.RegisteredService; import org.apereo.cas.services.ServicesManager; import org.apereo.cas.ticket.InvalidTicketException; import org.apereo.cas.ticket.TicketGrantingTicket; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; import java.io.Serializable; import java.util.Collection; import java.util.HashSet; import java.util.Map; /** * {@link RestController} implementation of a REST API * that allows for registration of CAS services. * * @author Misagh Moayyed * @since 4.2 */ @RestController("registeredServiceResourceRestController") @EnableConfigurationProperties(CasConfigurationProperties.class) public class RegisteredServiceResource { private static final Logger LOGGER = LoggerFactory.getLogger(RegisteredServiceResource.class); private final ServicesManager servicesManager; private final CentralAuthenticationService centralAuthenticationService; private final String attributeName; private final String attributeValue; public RegisteredServiceResource(final ServicesManager servicesManager, final CentralAuthenticationService centralAuthenticationService, final String attributeName, final String attributeValue) { this.servicesManager = servicesManager; this.centralAuthenticationService = centralAuthenticationService; this.attributeName = attributeName; this.attributeValue = attributeValue; } /** * Create new service. * * @param tgtId ticket granting ticket id URI path param * @param serviceDataHolder the service to register and save in rest form * @return {@link ResponseEntity} representing RESTful response */ @PostMapping(value = "/v1/services/add/{tgtId:.+}", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) public ResponseEntity<String> createService(@ModelAttribute final ServiceDataHolder serviceDataHolder, @PathVariable("tgtId") final String tgtId) { try { if (StringUtils.isBlank(this.attributeName) || StringUtils.isBlank(this.attributeValue)) { throw new IllegalArgumentException("Attribute name and/or value must be configured"); } final TicketGrantingTicket ticket = this.centralAuthenticationService.getTicket(tgtId, TicketGrantingTicket.class); if (ticket == null || ticket.isExpired()) { throw new InvalidTicketException("Ticket-granting ticket " + tgtId + " is not found"); } final Map<String, Object> attributes = ticket.getAuthentication().getPrincipal().getAttributes(); if (attributes.containsKey(this.attributeName)) { final Collection<String> attributeValuesToCompare = new HashSet<>(); final Object value = attributes.get(this.attributeName); if (value instanceof Collection) { attributeValuesToCompare.addAll((Collection<String>) value); } else { attributeValuesToCompare.add(value.toString()); } if (attributeValuesToCompare.contains(this.attributeValue)) { final RegisteredService service = serviceDataHolder.getRegisteredService(); final RegisteredService savedService = this.servicesManager.save(service); return new ResponseEntity<>(String.valueOf(savedService.getId()), HttpStatus.OK); } } throw new IllegalArgumentException("Request is not authorized"); } catch (final InvalidTicketException e) { return new ResponseEntity<>("TicketGrantingTicket could not be found", HttpStatus.NOT_FOUND); } catch (final Exception e) { LOGGER.error(e.getMessage(), e); return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); } } private static class ServiceDataHolder implements Serializable { private static final long serialVersionUID = 3035541944428412672L; private String serviceId; private String name; private String description; private int evaluationOrder = Integer.MAX_VALUE; private boolean enabled; private boolean ssoEnabled; public void setServiceId(final String serviceId) { this.serviceId = serviceId; } public void setName(final String serviceName) { this.name = serviceName; } public void setDescription(final String description) { this.description = description; } public void setEvaluationOrder(final int evaluationOrder) { this.evaluationOrder = evaluationOrder; } public void setEnabled(final boolean enabled) { this.enabled = enabled; } public void setSsoEnabled(final boolean ssoEnabled) { this.ssoEnabled = ssoEnabled; } public RegisteredService getRegisteredService() { if (StringUtils.isBlank(this.serviceId) || StringUtils.isBlank(this.name) || StringUtils.isBlank(this.description)) { throw new IllegalArgumentException("Service name/description/id is missing"); } final RegexRegisteredService service = new RegexRegisteredService(); service.setServiceId(this.serviceId); service.setDescription(this.description); service.setName(this.name); service.setEvaluationOrder(this.evaluationOrder); service.setAccessStrategy( new DefaultRegisteredServiceAccessStrategy(this.enabled, this.ssoEnabled)); return service; } } }