package org.apereo.cas.oidc.web.controllers;
import com.stormpath.sdk.lang.Assert;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apereo.cas.authentication.principal.PrincipalFactory;
import org.apereo.cas.authentication.principal.ServiceFactory;
import org.apereo.cas.authentication.principal.WebApplicationService;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.oidc.OidcConstants;
import org.apereo.cas.services.OidcRegisteredService;
import org.apereo.cas.services.ServicesManager;
import org.apereo.cas.support.oauth.profile.OAuth20ProfileScopeToAttributesFilter;
import org.apereo.cas.support.oauth.validator.OAuth20Validator;
import org.apereo.cas.support.oauth.web.endpoints.BaseOAuth20Controller;
import org.apereo.cas.ticket.accesstoken.AccessTokenFactory;
import org.apereo.cas.ticket.registry.TicketRegistry;
import org.apereo.cas.web.support.CookieRetrievingCookieGenerator;
import org.jooq.lambda.Unchecked;
import org.jose4j.jwk.JsonWebKey;
import org.jose4j.jwk.JsonWebKeySet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.nio.charset.StandardCharsets;
/**
* This is {@link OidcJwksEndpointController}.
*
* @author Misagh Moayyed
* @since 5.0.0
*/
public class OidcJwksEndpointController extends BaseOAuth20Controller {
private static final Logger LOGGER = LoggerFactory.getLogger(OidcJwksEndpointController.class);
@Autowired
private ResourceLoader resourceLoader;
private Resource jwksFile;
public OidcJwksEndpointController(final ServicesManager servicesManager,
final TicketRegistry ticketRegistry,
final OAuth20Validator validator,
final AccessTokenFactory accessTokenFactory,
final PrincipalFactory principalFactory,
final ServiceFactory<WebApplicationService> webApplicationServiceServiceFactory,
final OAuth20ProfileScopeToAttributesFilter scopeToAttributesFilter,
final CasConfigurationProperties casProperties,
final CookieRetrievingCookieGenerator ticketGrantingTicketCookieGenerator) {
super(servicesManager, ticketRegistry, validator, accessTokenFactory, principalFactory,
webApplicationServiceServiceFactory, scopeToAttributesFilter,
casProperties, ticketGrantingTicketCookieGenerator);
this.jwksFile = casProperties.getAuthn().getOidc().getJwksFile();
}
/**
* Handle request for jwk set.
*
* @param request the request
* @param response the response
* @param model the model
* @return the jwk set
* @throws Exception the exception
*/
@GetMapping(value = '/' + OidcConstants.BASE_OIDC_URL + '/' + OidcConstants.JWKS_URL, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> handleRequestInternal(final HttpServletRequest request,
final HttpServletResponse response,
final Model model) throws Exception {
Assert.notNull(this.jwksFile, "JWKS file cannot be undefined or null.");
try {
final String jsonJwks = IOUtils.toString(this.jwksFile.getInputStream(), StandardCharsets.UTF_8);
final JsonWebKeySet jsonWebKeySet = new JsonWebKeySet(jsonJwks);
this.servicesManager.getAllServices()
.stream()
.filter(s -> s instanceof OidcRegisteredService && StringUtils.isNotBlank(((OidcRegisteredService) s).getJwks()))
.forEach(
Unchecked.consumer(s -> {
final OidcRegisteredService service = (OidcRegisteredService) s;
final Resource resource = this.resourceLoader.getResource(service.getJwks());
final JsonWebKeySet set = new JsonWebKeySet(IOUtils.toString(resource.getInputStream(), StandardCharsets.UTF_8));
set.getJsonWebKeys().forEach(jsonWebKeySet::addJsonWebKey);
}));
final String body = jsonWebKeySet.toJson(JsonWebKey.OutputControlLevel.PUBLIC_ONLY);
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
return new ResponseEntity<>(body, HttpStatus.OK);
} catch (final Exception e) {
LOGGER.error(e.getMessage(), e);
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
}
}
}