package org.apereo.cas.token.authentication.principal; import com.google.common.base.Throwables; import com.nimbusds.jwt.JWTClaimsSet; import net.minidev.json.JSONObject; import org.apache.commons.lang3.BooleanUtils; import org.apereo.cas.CasProtocolConstants; import org.apereo.cas.CipherExecutor; import org.apereo.cas.authentication.principal.AbstractWebApplicationService; import org.apereo.cas.authentication.principal.Service; import org.apereo.cas.authentication.principal.WebApplicationService; import org.apereo.cas.authentication.principal.WebApplicationServiceResponseBuilder; import org.apereo.cas.configuration.CasConfigurationProperties; import org.apereo.cas.services.RegisteredService; import org.apereo.cas.services.RegisteredServiceAccessStrategyUtils; import org.apereo.cas.services.RegisteredServiceProperty; import org.apereo.cas.services.ServicesManager; import org.apereo.cas.ticket.ExpirationPolicy; import org.apereo.cas.token.TokenConstants; import org.apereo.cas.util.DateTimeUtils; import org.jasig.cas.client.validation.Assertion; import org.jasig.cas.client.validation.Cas30ServiceTicketValidator; import org.springframework.beans.factory.annotation.Autowired; import java.time.ZonedDateTime; import java.util.Map; /** * This is {@link TokenWebApplicationServiceResponseBuilder}. * * @author Misagh Moayyed * @since 5.1.0 */ public class TokenWebApplicationServiceResponseBuilder extends WebApplicationServiceResponseBuilder { private static final long serialVersionUID = -2863268279032438778L; @Autowired private CasConfigurationProperties casProperties; private final ServicesManager servicesManager; private final CipherExecutor<String, String> tokenCipherExecutor; private final ExpirationPolicy ticketGrantingTicketExpirationPolicy; public TokenWebApplicationServiceResponseBuilder(final ServicesManager servicesManager, final CipherExecutor tokenCipherExecutor, final ExpirationPolicy ticketGrantingTicketExpirationPolicy) { this.servicesManager = servicesManager; this.tokenCipherExecutor = tokenCipherExecutor; this.ticketGrantingTicketExpirationPolicy = ticketGrantingTicketExpirationPolicy; } @Override protected WebApplicationService buildInternal(final WebApplicationService service, final Map<String, String> parameters) { final RegisteredService registeredService = this.servicesManager.findServiceBy(service); RegisteredServiceAccessStrategyUtils.ensureServiceAccessIsAllowed(service, registeredService); final Map.Entry<String, RegisteredServiceProperty> property = registeredService.getProperties() .entrySet().stream() .filter(entry -> entry.getKey().equalsIgnoreCase(TokenConstants.PROPERTY_NAME_TOKEN_AS_RESPONSE) && BooleanUtils.toBoolean(entry.getValue().getValue())) .distinct() .findFirst() .orElse(null); if (property == null) { return super.buildInternal(service, parameters); } final String jwt = generateToken(service, parameters); final TokenWebApplicationService jwtService = new TokenWebApplicationService(service.getId(), service.getOriginalUrl(), service.getArtifactId()); jwtService.setFormat(service.getFormat()); jwtService.setLoggedOutAlready(service.isLoggedOutAlready()); parameters.put(CasProtocolConstants.PARAMETER_TICKET, jwt); return jwtService; } /** * Generate token string. * * @param service the service * @param parameters the parameters * @return the jwt */ protected String generateToken(final Service service, final Map<String, String> parameters) { try { final String ticketId = parameters.get(CasProtocolConstants.PARAMETER_TICKET); final Cas30ServiceTicketValidator validator = new Cas30ServiceTicketValidator(casProperties.getServer().getPrefix()); final Assertion assertion = validator.validate(ticketId, service.getId()); final JWTClaimsSet.Builder claims = new JWTClaimsSet.Builder() .audience(service.getId()) .issuer(casProperties.getServer().getPrefix()) .jwtID(ticketId) .issueTime(assertion.getAuthenticationDate()) .subject(assertion.getPrincipal().getName()); assertion.getAttributes().forEach(claims::claim); assertion.getPrincipal().getAttributes().forEach(claims::claim); if (assertion.getValidUntilDate() != null) { claims.expirationTime(assertion.getValidUntilDate()); } else { final ZonedDateTime dt = ZonedDateTime.now().plusSeconds(ticketGrantingTicketExpirationPolicy.getTimeToLive()); claims.expirationTime(DateTimeUtils.dateOf(dt)); } final JWTClaimsSet claimsSet = claims.build(); final JSONObject object = claimsSet.toJSONObject(); return tokenCipherExecutor.encode(object.toJSONString()); } catch (final Exception e) { throw Throwables.propagate(e); } } /** * Token/JWT web application service. */ public static class TokenWebApplicationService extends AbstractWebApplicationService { private static final long serialVersionUID = -8844121291312069964L; public TokenWebApplicationService(final String id, final String originalUrl, final String artifactId) { super(id, originalUrl, artifactId); } } }