diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryActuatorAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryActuatorAutoConfiguration.java deleted file mode 100644 index e3acc9959e..0000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/CloudFoundryActuatorAutoConfiguration.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.autoconfigure.cloudfoundry; - -import org.springframework.boot.actuate.autoconfigure.endpoint.infrastructure.ServletEndpointAutoConfiguration; -import org.springframework.boot.autoconfigure.AutoConfigureAfter; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.autoconfigure.security.IgnoredRequestCustomizer; -import org.springframework.boot.cloud.CloudPlatform; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.web.builders.WebSecurity; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; - -/** - * {@link EnableAutoConfiguration Auto-configuration} to expose actuator endpoints for - * cloud foundry to use. - * - * @author Madhura Bhave - * @since 2.0.0 - */ -@Configuration -@ConditionalOnProperty(prefix = "management.cloudfoundry", name = "enabled", matchIfMissing = true) -@AutoConfigureAfter(ServletEndpointAutoConfiguration.class) -@ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY) -public class CloudFoundryActuatorAutoConfiguration { - - /** - * Nested configuration for ignored requests if Spring Security is present. - */ - @ConditionalOnClass(WebSecurity.class) - static class CloudFoundryIgnoredRequestConfiguration { - - @Bean - public IgnoredRequestCustomizer cloudFoundryIgnoredRequestCustomizer() { - return new CloudFoundryIgnoredRequestCustomizer(); - } - - private static class CloudFoundryIgnoredRequestCustomizer - implements IgnoredRequestCustomizer { - - @Override - public void customize(WebSecurity.IgnoredRequestConfigurer configurer) { - configurer.requestMatchers( - new AntPathRequestMatcher("/cloudfoundryapplication/**")); - } - - } - - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/package-info.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/package-info.java deleted file mode 100644 index 789ec3b6ff..0000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Auto-configuration for Actuator's CloudFoundry integration. - */ -package org.springframework.boot.actuate.autoconfigure.cloudfoundry; diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/AccessLevel.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/AccessLevel.java deleted file mode 100644 index 3bd7e7e4a2..0000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/AccessLevel.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.cloudfoundry; - -import java.util.Arrays; -import java.util.List; - -import javax.servlet.http.HttpServletRequest; - -/** - * The specific access level granted to the cloud foundry user that's calling the - * endpoints. - * - * @author Madhura Bhave - */ -enum AccessLevel { - - /** - * Restricted access to a limited set of endpoints. - */ - RESTRICTED("", "/health", "/info"), - - /** - * Full access to all endpoints. - */ - FULL; - - private static final String REQUEST_ATTRIBUTE = "cloudFoundryAccessLevel"; - - private final List endpointPaths; - - AccessLevel(String... endpointPaths) { - this.endpointPaths = Arrays.asList(endpointPaths); - } - - /** - * Returns if the access level should allow access to the specified endpoint path. - * @param endpointPath the endpoint path - * @return {@code true} if access is allowed - */ - public boolean isAccessAllowed(String endpointPath) { - return this.endpointPaths.isEmpty() || this.endpointPaths.contains(endpointPath); - } - - public void put(HttpServletRequest request) { - request.setAttribute(REQUEST_ATTRIBUTE, this); - } - - public static AccessLevel get(HttpServletRequest request) { - return (AccessLevel) request.getAttribute(REQUEST_ATTRIBUTE); - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryAuthorizationException.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryAuthorizationException.java deleted file mode 100644 index d45e7601de..0000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryAuthorizationException.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.cloudfoundry; - -import org.springframework.http.HttpStatus; - -/** - * Authorization exceptions thrown to limit access to the endpoints. - * - * @author Madhura Bhave - */ -class CloudFoundryAuthorizationException extends RuntimeException { - - private final Reason reason; - - CloudFoundryAuthorizationException(Reason reason, String message) { - this(reason, message, null); - } - - CloudFoundryAuthorizationException(Reason reason, String message, Throwable cause) { - super(message); - this.reason = reason; - } - - /** - * Return the status code that should be returned to the client. - * @return the HTTP status code - */ - public HttpStatus getStatusCode() { - return getReason().getStatus(); - } - - /** - * Return the reason why the authorization exception was thrown. - * @return the reason - */ - public Reason getReason() { - return this.reason; - } - - /** - * Reasons why the exception can be thrown. - */ - enum Reason { - - ACCESS_DENIED(HttpStatus.FORBIDDEN), - - INVALID_AUDIENCE(HttpStatus.UNAUTHORIZED), - - INVALID_ISSUER(HttpStatus.UNAUTHORIZED), - - INVALID_KEY_ID(HttpStatus.UNAUTHORIZED), - - INVALID_SIGNATURE(HttpStatus.UNAUTHORIZED), - - INVALID_TOKEN(HttpStatus.UNAUTHORIZED), - - MISSING_AUTHORIZATION(HttpStatus.UNAUTHORIZED), - - TOKEN_EXPIRED(HttpStatus.UNAUTHORIZED), - - UNSUPPORTED_TOKEN_SIGNING_ALGORITHM(HttpStatus.UNAUTHORIZED), - - SERVICE_UNAVAILABLE(HttpStatus.SERVICE_UNAVAILABLE); - - private final HttpStatus status; - - Reason(HttpStatus status) { - this.status = status; - } - - public HttpStatus getStatus() { - return this.status; - } - - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryDiscoveryMvcEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryDiscoveryMvcEndpoint.java deleted file mode 100644 index f487938dda..0000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryDiscoveryMvcEndpoint.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.cloudfoundry; - -/** - * {@link MvcEndpoint} to expose HAL-formatted JSON for Cloud Foundry specific actuator - * endpoints. - * - * @author Madhura Bhave - */ -class CloudFoundryDiscoveryMvcEndpoint { - - // TODO Port to new infrastructure - - // private final Set endpoints; - // - // CloudFoundryDiscoveryMvcEndpoint(Set endpoints) { - // this.endpoints = endpoints; - // } - // - // @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE) - // @ResponseBody - // public Map> links(HttpServletRequest request) { - // Map links = new LinkedHashMap<>(); - // String url = request.getRequestURL().toString(); - // if (url.endsWith("/")) { - // url = url.substring(0, url.length() - 1); - // } - // links.put("self", Link.withHref(url)); - // AccessLevel accessLevel = AccessLevel.get(request); - // for (NamedMvcEndpoint endpoint : this.endpoints) { - // if (accessLevel != null && accessLevel.isAccessAllowed(endpoint.getPath())) { - // links.put(endpoint.getName(), - // Link.withHref(url + "/" + endpoint.getName())); - // } - // } - // return Collections.singletonMap("_links", links); - // } - // - // /** - // * Details for a link in the HAL response. - // */ - // static class Link { - // - // private String href; - // - // public String getHref() { - // return this.href; - // } - // - // public void setHref(String href) { - // this.href = href; - // } - // - // static Link withHref(Object href) { - // Link link = new Link(); - // link.setHref(href.toString()); - // return link; - // } - // - // } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryHealthMvcEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryHealthMvcEndpoint.java deleted file mode 100644 index 1b5e202f4d..0000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryHealthMvcEndpoint.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.cloudfoundry; - -import org.springframework.boot.actuate.endpoint.HealthEndpoint; -import org.springframework.boot.actuate.endpoint.web.HealthWebEndpointExtension; - -/** - * Extension of {@link HealthWebEndpointExtension} for Cloud Foundry. Since security for - * Cloud Foundry actuators is already handled by the - * {@link CloudFoundrySecurityInterceptor}, this endpoint skips the additional security - * checks done by the regular {@link HealthWebEndpointExtension}. - * - * @author Madhura Bhave - */ -class CloudFoundryHealthMvcEndpoint extends HealthWebEndpointExtension { - - // TODO Port to new infrastructure - - CloudFoundryHealthMvcEndpoint(HealthEndpoint delegate) { - super(delegate); - } - // - // @Override - // protected boolean exposeHealthDetails(HttpServletRequest request, - // Principal principal) { - // return true; - // } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundrySecurityInterceptor.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundrySecurityInterceptor.java deleted file mode 100644 index 3a766085a7..0000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundrySecurityInterceptor.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.cloudfoundry; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.boot.actuate.cloudfoundry.CloudFoundryAuthorizationException.Reason; -import org.springframework.web.servlet.HandlerInterceptor; -import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; - -/** - * {@link HandlerInterceptor} to check the cloud foundry token. - * - * @author Madhura Bhave - * @since 2.0.0 - */ -public class CloudFoundrySecurityInterceptor extends HandlerInterceptorAdapter { - - private static final Log logger = LogFactory - .getLog(CloudFoundrySecurityInterceptor.class); - - private final TokenValidator tokenValidator; - - private final CloudFoundrySecurityService cloudFoundrySecurityService; - - private final String applicationId; - - public CloudFoundrySecurityInterceptor(TokenValidator tokenValidator, - CloudFoundrySecurityService cloudFoundrySecurityService, - String applicationId) { - this.tokenValidator = tokenValidator; - this.cloudFoundrySecurityService = cloudFoundrySecurityService; - this.applicationId = applicationId; - } - - // TODO Port to new infrastructure - - @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, - Object handler) throws Exception { - // if (CorsUtils.isPreFlightRequest(request)) { - // return true; - // } - // try { - // if (!StringUtils.hasText(this.applicationId)) { - // throw new CloudFoundryAuthorizationException(Reason.SERVICE_UNAVAILABLE, - // "Application id is not available"); - // } - // if (this.cloudFoundrySecurityService == null) { - // throw new CloudFoundryAuthorizationException(Reason.SERVICE_UNAVAILABLE, - // "Cloud controller URL is not available"); - // } - // HandlerMethod handlerMethod = (HandlerMethod) handler; - // if (HttpMethod.OPTIONS.matches(request.getMethod()) - // && !(handlerMethod.getBean() instanceof MvcEndpoint)) { - // return true; - // } - // MvcEndpoint mvcEndpoint = (MvcEndpoint) handlerMethod.getBean(); - // check(request, mvcEndpoint); - // } - // catch (CloudFoundryAuthorizationException ex) { - // logger.error(ex); - // response.setContentType(MediaType.APPLICATION_JSON.toString()); - // response.getWriter() - // .write("{\"security_error\":\"" + ex.getMessage() + "\"}"); - // response.setStatus(ex.getStatusCode().value()); - // return false; - // } - return true; - } - - // private void check(HttpServletRequest request, MvcEndpoint mvcEndpoint) - // throws Exception { - // Token token = getToken(request); - // this.tokenValidator.validate(token); - // AccessLevel accessLevel = this.cloudFoundrySecurityService - // .getAccessLevel(token.toString(), this.applicationId); - // if (!accessLevel.isAccessAllowed(mvcEndpoint.getPath())) { - // throw new CloudFoundryAuthorizationException(Reason.ACCESS_DENIED, - // "Access denied"); - // } - // accessLevel.put(request); - // } - - private Token getToken(HttpServletRequest request) { - String authorization = request.getHeader("Authorization"); - String bearerPrefix = "bearer "; - if (authorization == null - || !authorization.toLowerCase().startsWith(bearerPrefix)) { - throw new CloudFoundryAuthorizationException(Reason.MISSING_AUTHORIZATION, - "Authorization header is missing or invalid"); - } - return new Token(authorization.substring(bearerPrefix.length())); - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundrySecurityService.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundrySecurityService.java deleted file mode 100644 index 4c495dc354..0000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundrySecurityService.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.cloudfoundry; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.springframework.boot.actuate.cloudfoundry.CloudFoundryAuthorizationException.Reason; -import org.springframework.boot.web.client.RestTemplateBuilder; -import org.springframework.http.HttpStatus; -import org.springframework.http.RequestEntity; -import org.springframework.util.Assert; -import org.springframework.web.client.HttpClientErrorException; -import org.springframework.web.client.HttpServerErrorException; -import org.springframework.web.client.HttpStatusCodeException; -import org.springframework.web.client.RestTemplate; - -/** - * Cloud Foundry security service to handle REST calls to the cloud controller and UAA. - * - * @author Madhura Bhave - * @since 2.0.0 - */ -public class CloudFoundrySecurityService { - - private final RestTemplate restTemplate; - - private final String cloudControllerUrl; - - private String uaaUrl; - - public CloudFoundrySecurityService(RestTemplateBuilder restTemplateBuilder, - String cloudControllerUrl, boolean skipSslValidation) { - Assert.notNull(restTemplateBuilder, "RestTemplateBuilder must not be null"); - Assert.notNull(cloudControllerUrl, "CloudControllerUrl must not be null"); - if (skipSslValidation) { - restTemplateBuilder = restTemplateBuilder - .requestFactory(SkipSslVerificationHttpRequestFactory.class); - } - this.restTemplate = restTemplateBuilder.build(); - this.cloudControllerUrl = cloudControllerUrl; - } - - /** - * Return the access level that should be granted to the given token. - * @param token the token - * @param applicationId the cloud foundry application ID - * @return the access level that should be granted - * @throws CloudFoundryAuthorizationException if the token is not authorized - */ - public AccessLevel getAccessLevel(String token, String applicationId) - throws CloudFoundryAuthorizationException { - try { - URI uri = getPermissionsUri(applicationId); - RequestEntity request = RequestEntity.get(uri) - .header("Authorization", "bearer " + token).build(); - Map body = this.restTemplate.exchange(request, Map.class).getBody(); - if (Boolean.TRUE.equals(body.get("read_sensitive_data"))) { - return AccessLevel.FULL; - } - return AccessLevel.RESTRICTED; - } - catch (HttpClientErrorException ex) { - if (ex.getStatusCode().equals(HttpStatus.FORBIDDEN)) { - throw new CloudFoundryAuthorizationException(Reason.ACCESS_DENIED, - "Access denied"); - } - throw new CloudFoundryAuthorizationException(Reason.INVALID_TOKEN, - "Invalid token", ex); - } - catch (HttpServerErrorException ex) { - throw new CloudFoundryAuthorizationException(Reason.SERVICE_UNAVAILABLE, - "Cloud controller not reachable"); - } - } - - private URI getPermissionsUri(String applicationId) { - try { - return new URI(this.cloudControllerUrl + "/v2/apps/" + applicationId - + "/permissions"); - } - catch (URISyntaxException ex) { - throw new IllegalStateException(ex); - } - } - - /** - * Return all token keys known by the UAA. - * @return a list of token keys - */ - public Map fetchTokenKeys() { - try { - return extractTokenKeys(this.restTemplate - .getForObject(getUaaUrl() + "/token_keys", Map.class)); - } - catch (HttpStatusCodeException e) { - throw new CloudFoundryAuthorizationException(Reason.SERVICE_UNAVAILABLE, - "UAA not reachable"); - } - } - - private Map extractTokenKeys(Map response) { - Map tokenKeys = new HashMap<>(); - for (Object key : (List) response.get("keys")) { - Map tokenKey = (Map) key; - tokenKeys.put((String) tokenKey.get("kid"), (String) tokenKey.get("value")); - } - return tokenKeys; - } - - /** - * Return the URL of the UAA. - * @return the UAA url - */ - public String getUaaUrl() { - if (this.uaaUrl == null) { - try { - Map response = this.restTemplate - .getForObject(this.cloudControllerUrl + "/info", Map.class); - this.uaaUrl = (String) response.get("token_endpoint"); - } - catch (HttpStatusCodeException ex) { - throw new CloudFoundryAuthorizationException(Reason.SERVICE_UNAVAILABLE, - "Unable to fetch token keys from UAA"); - } - } - return this.uaaUrl; - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/SkipSslVerificationHttpRequestFactory.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/SkipSslVerificationHttpRequestFactory.java deleted file mode 100644 index fc35f5714a..0000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/SkipSslVerificationHttpRequestFactory.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.cloudfoundry; - -import java.io.IOException; -import java.net.HttpURLConnection; -import java.security.SecureRandom; -import java.security.cert.X509Certificate; - -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSession; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; - -import org.springframework.http.client.SimpleClientHttpRequestFactory; - -/** - * {@link SimpleClientHttpRequestFactory} that skips SSL certificate verification. - * - * @author Madhura Bhave - */ -class SkipSslVerificationHttpRequestFactory extends SimpleClientHttpRequestFactory { - - @Override - protected void prepareConnection(HttpURLConnection connection, String httpMethod) - throws IOException { - if (connection instanceof HttpsURLConnection) { - prepareHttpsConnection((HttpsURLConnection) connection); - } - super.prepareConnection(connection, httpMethod); - } - - private void prepareHttpsConnection(HttpsURLConnection connection) { - connection.setHostnameVerifier(new SkipHostnameVerifier()); - try { - connection.setSSLSocketFactory(createSslSocketFactory()); - } - catch (Exception ex) { - // Ignore - } - } - - private SSLSocketFactory createSslSocketFactory() throws Exception { - SSLContext context = SSLContext.getInstance("TLS"); - context.init(null, new TrustManager[] { new SkipX509TrustManager() }, - new SecureRandom()); - return context.getSocketFactory(); - } - - private class SkipHostnameVerifier implements HostnameVerifier { - - @Override - public boolean verify(String s, SSLSession sslSession) { - return true; - } - - } - - private static class SkipX509TrustManager implements X509TrustManager { - - @Override - public X509Certificate[] getAcceptedIssuers() { - return new X509Certificate[0]; - } - - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) { - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) { - } - - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/Token.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/Token.java deleted file mode 100644 index 7da97acbc4..0000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/Token.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.cloudfoundry; - -import java.nio.charset.Charset; -import java.util.List; -import java.util.Map; - -import org.springframework.boot.json.JsonParserFactory; -import org.springframework.util.Base64Utils; -import org.springframework.util.StringUtils; - -/** - * The JSON web token provided with each request that originates from Cloud Foundry. - * - * @author Madhura Bhave - */ -class Token { - - private static final Charset UTF_8 = Charset.forName("UTF-8"); - - private final String encoded; - - private final String signature; - - private final Map header; - - private final Map claims; - - Token(String encoded) { - this.encoded = encoded; - int firstPeriod = encoded.indexOf('.'); - int lastPeriod = encoded.lastIndexOf('.'); - if (firstPeriod <= 0 || lastPeriod <= firstPeriod) { - throw new CloudFoundryAuthorizationException( - CloudFoundryAuthorizationException.Reason.INVALID_TOKEN, - "JWT must have header, body and signature"); - } - this.header = parseJson(encoded.substring(0, firstPeriod)); - this.claims = parseJson(encoded.substring(firstPeriod + 1, lastPeriod)); - this.signature = encoded.substring(lastPeriod + 1); - if (!StringUtils.hasLength(this.signature)) { - throw new CloudFoundryAuthorizationException( - CloudFoundryAuthorizationException.Reason.INVALID_TOKEN, - "Token must have non-empty crypto segment"); - } - } - - private Map parseJson(String base64) { - try { - byte[] bytes = Base64Utils.decodeFromUrlSafeString(base64); - return JsonParserFactory.getJsonParser().parseMap(new String(bytes, UTF_8)); - } - catch (RuntimeException ex) { - throw new CloudFoundryAuthorizationException( - CloudFoundryAuthorizationException.Reason.INVALID_TOKEN, - "Token could not be parsed", ex); - } - } - - public byte[] getContent() { - return this.encoded.substring(0, this.encoded.lastIndexOf(".")).getBytes(); - } - - public byte[] getSignature() { - return Base64Utils.decodeFromUrlSafeString(this.signature); - } - - public String getSignatureAlgorithm() { - return getRequired(this.header, "alg", String.class); - } - - public String getIssuer() { - return getRequired(this.claims, "iss", String.class); - } - - public long getExpiry() { - return getRequired(this.claims, "exp", Integer.class).longValue(); - } - - @SuppressWarnings("unchecked") - public List getScope() { - return getRequired(this.claims, "scope", List.class); - } - - public String getKeyId() { - return getRequired(this.header, "kid", String.class); - } - - @SuppressWarnings("unchecked") - private T getRequired(Map map, String key, Class type) { - Object value = map.get(key); - if (value == null) { - throw new CloudFoundryAuthorizationException( - CloudFoundryAuthorizationException.Reason.INVALID_TOKEN, - "Unable to get value from key " + key); - } - if (!type.isInstance(value)) { - throw new CloudFoundryAuthorizationException( - CloudFoundryAuthorizationException.Reason.INVALID_TOKEN, - "Unexpected value type from key " + key + " value " + value); - } - return (T) value; - } - - @Override - public String toString() { - return this.encoded; - }; - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/TokenValidator.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/TokenValidator.java deleted file mode 100644 index f3acd407ff..0000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/TokenValidator.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.cloudfoundry; - -import java.security.GeneralSecurityException; -import java.security.KeyFactory; -import java.security.NoSuchAlgorithmException; -import java.security.PublicKey; -import java.security.Signature; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.X509EncodedKeySpec; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -import org.springframework.boot.actuate.cloudfoundry.CloudFoundryAuthorizationException.Reason; -import org.springframework.util.Base64Utils; - -/** - * Validator used to ensure that a signed {@link Token} has not been tampered with. - * - * @author Madhura Bhave - * @since 2.0.0 - */ -public class TokenValidator { - - private final CloudFoundrySecurityService securityService; - - private Map tokenKeys; - - public TokenValidator(CloudFoundrySecurityService cloudFoundrySecurityService) { - this.securityService = cloudFoundrySecurityService; - } - - public void validate(Token token) { - validateAlgorithm(token); - validateKeyIdAndSignature(token); - validateExpiry(token); - validateIssuer(token); - validateAudience(token); - } - - private void validateAlgorithm(Token token) { - String algorithm = token.getSignatureAlgorithm(); - if (algorithm == null) { - throw new CloudFoundryAuthorizationException(Reason.INVALID_SIGNATURE, - "Signing algorithm cannot be null"); - } - if (!algorithm.equals("RS256")) { - throw new CloudFoundryAuthorizationException( - Reason.UNSUPPORTED_TOKEN_SIGNING_ALGORITHM, - "Signing algorithm " + algorithm + " not supported"); - } - } - - private void validateKeyIdAndSignature(Token token) { - String keyId = token.getKeyId(); - if (this.tokenKeys == null || !hasValidKeyId(keyId)) { - this.tokenKeys = this.securityService.fetchTokenKeys(); - if (!hasValidKeyId(keyId)) { - throw new CloudFoundryAuthorizationException(Reason.INVALID_KEY_ID, - "Key Id present in token header does not match"); - } - } - - if (!hasValidSignature(token, this.tokenKeys.get(keyId))) { - throw new CloudFoundryAuthorizationException(Reason.INVALID_SIGNATURE, - "RSA Signature did not match content"); - } - } - - private boolean hasValidKeyId(String tokenKey) { - for (String candidate : this.tokenKeys.keySet()) { - if (tokenKey.equals(candidate)) { - return true; - } - } - return false; - } - - private boolean hasValidSignature(Token token, String key) { - try { - PublicKey publicKey = getPublicKey(key); - Signature signature = Signature.getInstance("SHA256withRSA"); - signature.initVerify(publicKey); - signature.update(token.getContent()); - return signature.verify(token.getSignature()); - } - catch (GeneralSecurityException ex) { - return false; - } - } - - private PublicKey getPublicKey(String key) - throws NoSuchAlgorithmException, InvalidKeySpecException { - key = key.replace("-----BEGIN PUBLIC KEY-----\n", ""); - key = key.replace("-----END PUBLIC KEY-----", ""); - key = key.trim().replace("\n", ""); - byte[] bytes = Base64Utils.decodeFromString(key); - X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes); - return KeyFactory.getInstance("RSA").generatePublic(keySpec); - } - - private void validateExpiry(Token token) { - long currentTime = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()); - if (currentTime > token.getExpiry()) { - throw new CloudFoundryAuthorizationException(Reason.TOKEN_EXPIRED, - "Token expired"); - } - } - - private void validateIssuer(Token token) { - String uaaUrl = this.securityService.getUaaUrl(); - String issuerUri = String.format("%s/oauth/token", uaaUrl); - if (!issuerUri.equals(token.getIssuer())) { - throw new CloudFoundryAuthorizationException(Reason.INVALID_ISSUER, - "Token issuer does not match " + uaaUrl + "/oauth/token"); - } - } - - private void validateAudience(Token token) { - if (!token.getScope().contains("actuator.read")) { - throw new CloudFoundryAuthorizationException(Reason.INVALID_AUDIENCE, - "Token does not have audience actuator"); - } - - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/package-info.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/package-info.java deleted file mode 100644 index f8e206f7d2..0000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Classes for CloudFoundry integration. - */ -package org.springframework.boot.actuate.cloudfoundry; diff --git a/spring-boot-actuator/src/main/resources/META-INF/spring.factories b/spring-boot-actuator/src/main/resources/META-INF/spring.factories index b6465391e2..79f24c77ec 100644 --- a/spring-boot-actuator/src/main/resources/META-INF/spring.factories +++ b/spring-boot-actuator/src/main/resources/META-INF/spring.factories @@ -2,7 +2,6 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.actuate.autoconfigure.ManagementContextAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.audit.AuditAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.cache.CacheStatisticsAutoConfiguration,\ -org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryActuatorAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.endpoint.infrastructure.EndpointInfrastructureAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.endpoint.infrastructure.ServletEndpointAutoConfiguration,\ diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/AccessLevelTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/AccessLevelTests.java deleted file mode 100644 index 51397837ee..0000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/AccessLevelTests.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.cloudfoundry; - -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link AccessLevel}. - * - * @author Madhura Bhave - */ -public class AccessLevelTests { - - @Test - public void accessToHealthEndpointShouldNotBeRestricted() throws Exception { - assertThat(AccessLevel.RESTRICTED.isAccessAllowed("/health")).isTrue(); - assertThat(AccessLevel.FULL.isAccessAllowed("/health")).isTrue(); - } - - @Test - public void accessToInfoEndpointShouldNotBeRestricted() throws Exception { - assertThat(AccessLevel.RESTRICTED.isAccessAllowed("/info")).isTrue(); - assertThat(AccessLevel.FULL.isAccessAllowed("/info")).isTrue(); - } - - @Test - public void accessToDiscoveryEndpointShouldNotBeRestricted() throws Exception { - assertThat(AccessLevel.RESTRICTED.isAccessAllowed("")).isTrue(); - assertThat(AccessLevel.FULL.isAccessAllowed("")).isTrue(); - } - - @Test - public void accessToAnyOtherEndpointShouldBeRestricted() throws Exception { - assertThat(AccessLevel.RESTRICTED.isAccessAllowed("env")).isFalse(); - assertThat(AccessLevel.FULL.isAccessAllowed("")).isTrue(); - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/AuthorizationExceptionMatcher.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/AuthorizationExceptionMatcher.java deleted file mode 100644 index 81992a0fb5..0000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/AuthorizationExceptionMatcher.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.cloudfoundry; - -import org.hamcrest.CustomMatcher; -import org.hamcrest.Matcher; - -import org.springframework.boot.actuate.cloudfoundry.CloudFoundryAuthorizationException.Reason; - -/** - * Hamcrest matcher to check the {@link AuthorizationExceptionMatcher} {@link Reason}. - * - * @author Madhura Bhave - */ -final class AuthorizationExceptionMatcher { - - private AuthorizationExceptionMatcher() { - } - - static Matcher withReason(final Reason reason) { - return new CustomMatcher( - "CloudFoundryAuthorizationException with " + reason + " reason") { - - @Override - public boolean matches(Object object) { - return ((object instanceof CloudFoundryAuthorizationException) - && ((CloudFoundryAuthorizationException) object) - .getReason() == reason); - } - - }; - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryActuatorAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryActuatorAutoConfigurationTests.java deleted file mode 100644 index ef9a65b874..0000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryActuatorAutoConfigurationTests.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.cloudfoundry; - -/** - * Tests for {@link CloudFoundryActuatorAutoConfiguration}. - * - * @author Madhura Bhave - */ -public class CloudFoundryActuatorAutoConfigurationTests { - - // TODO CloudFoundry support - - // private AnnotationConfigWebApplicationContext context; - // - // @Before - // public void setup() { - // this.context = new AnnotationConfigWebApplicationContext(); - // this.context.setServletContext(new MockServletContext()); - // this.context.register(SecurityAutoConfiguration.class, - // WebMvcAutoConfiguration.class, - // ManagementWebSecurityAutoConfiguration.class, - // JacksonAutoConfiguration.class, - // HttpMessageConvertersAutoConfiguration.class, - // EndpointAutoConfiguration.class, EndpointServletWebAutoConfiguration.class, - // PropertyPlaceholderAutoConfiguration.class, - // RestTemplateAutoConfiguration.class, - // EndpointWebMvcManagementContextConfiguration.class, - // CloudFoundryActuatorAutoConfiguration.class); - // } - // - // @After - // public void close() { - // if (this.context != null) { - // this.context.close(); - // } - // } - // - // @Test - // public void cloudFoundryPlatformActive() throws Exception { - // CloudFoundryEndpointHandlerMapping handlerMapping = getHandlerMapping(); - // assertThat(handlerMapping.getPrefix()).isEqualTo("/cloudfoundryapplication"); - // CorsConfiguration corsConfiguration = (CorsConfiguration) ReflectionTestUtils - // .getField(handlerMapping, "corsConfiguration"); - // assertThat(corsConfiguration.getAllowedOrigins()).contains("*"); - // assertThat(corsConfiguration.getAllowedMethods()).containsAll( - // Arrays.asList(HttpMethod.GET.name(), HttpMethod.POST.name())); - // assertThat(corsConfiguration.getAllowedHeaders()).containsAll( - // Arrays.asList("Authorization", "X-Cf-App-Instance", "Content-Type")); - // } - // - // @Test - // public void cloudFoundryPlatformActiveSetsApplicationId() throws Exception { - // CloudFoundryEndpointHandlerMapping handlerMapping = getHandlerMapping(); - // Object interceptor = ReflectionTestUtils.getField(handlerMapping, - // "securityInterceptor"); - // String applicationId = (String) ReflectionTestUtils.getField(interceptor, - // "applicationId"); - // assertThat(applicationId).isEqualTo("my-app-id"); - // } - // - // @Test - // public void cloudFoundryPlatformActiveSetsCloudControllerUrl() throws Exception { - // CloudFoundryEndpointHandlerMapping handlerMapping = getHandlerMapping(); - // Object interceptor = ReflectionTestUtils.getField(handlerMapping, - // "securityInterceptor"); - // Object interceptorSecurityService = ReflectionTestUtils.getField(interceptor, - // "cloudFoundrySecurityService"); - // String cloudControllerUrl = (String) ReflectionTestUtils - // .getField(interceptorSecurityService, "cloudControllerUrl"); - // assertThat(cloudControllerUrl).isEqualTo("http://my-cloud-controller.com"); - // } - // - // @Test - // public void skipSslValidation() throws Exception { - // TestPropertyValues.of("management.cloudfoundry.skipSslValidation:true") - // .applyTo(this.context); - // ConfigurationPropertySources.attach(this.context.getEnvironment()); - // this.context.refresh(); - // CloudFoundryEndpointHandlerMapping handlerMapping = getHandlerMapping(); - // Object interceptor = ReflectionTestUtils.getField(handlerMapping, - // "securityInterceptor"); - // Object interceptorSecurityService = ReflectionTestUtils.getField(interceptor, - // "cloudFoundrySecurityService"); - // RestTemplate restTemplate = (RestTemplate) ReflectionTestUtils - // .getField(interceptorSecurityService, "restTemplate"); - // assertThat(restTemplate.getRequestFactory()) - // .isInstanceOf(SkipSslVerificationHttpRequestFactory.class); - // } - // - // @Test - // public void cloudFoundryPlatformActiveAndCloudControllerUrlNotPresent() - // throws Exception { - // TestPropertyValues - // .of("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id") - // .applyTo(this.context); - // this.context.refresh(); - // CloudFoundryEndpointHandlerMapping handlerMapping = this.context.getBean( - // "cloudFoundryEndpointHandlerMapping", - // CloudFoundryEndpointHandlerMapping.class); - // Object securityInterceptor = ReflectionTestUtils.getField(handlerMapping, - // "securityInterceptor"); - // Object interceptorSecurityService = ReflectionTestUtils - // .getField(securityInterceptor, "cloudFoundrySecurityService"); - // assertThat(interceptorSecurityService).isNull(); - // } - // - // @Test - // public void cloudFoundryPathsIgnoredBySpringSecurity() throws Exception { - // TestPropertyValues - // .of("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id") - // .applyTo(this.context); - // this.context.refresh(); - // IgnoredRequestCustomizer customizer = (IgnoredRequestCustomizer) this.context - // .getBean("cloudFoundryIgnoredRequestCustomizer"); - // IgnoredRequestConfigurer configurer = mock(IgnoredRequestConfigurer.class); - // customizer.customize(configurer); - // ArgumentCaptor requestMatcher = ArgumentCaptor - // .forClass(RequestMatcher.class); - // verify(configurer).requestMatchers(requestMatcher.capture()); - // RequestMatcher matcher = requestMatcher.getValue(); - // MockHttpServletRequest request = new MockHttpServletRequest(); - // request.setServletPath("/cloudfoundryapplication/my-path"); - // assertThat(matcher.matches(request)).isTrue(); - // request.setServletPath("/some-other-path"); - // assertThat(matcher.matches(request)).isFalse(); - // } - // - // @Test - // public void cloudFoundryPlatformInactive() throws Exception { - // this.context.refresh(); - // assertThat(this.context.containsBean("cloudFoundryEndpointHandlerMapping")) - // .isFalse(); - // } - // - // @Test - // public void cloudFoundryManagementEndpointsDisabled() throws Exception { - // TestPropertyValues - // .of("VCAP_APPLICATION=---", "management.cloudfoundry.enabled:false") - // .applyTo(this.context); - // this.context.refresh(); - // assertThat(this.context.containsBean("cloudFoundryEndpointHandlerMapping")) - // .isFalse(); - // } - // - // private CloudFoundryEndpointHandlerMapping getHandlerMapping() { - // TestPropertyValues - // .of("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id", - // "vcap.application.cf_api:http://my-cloud-controller.com") - // .applyTo(this.context); - // this.context.refresh(); - // return this.context.getBean("cloudFoundryEndpointHandlerMapping", - // CloudFoundryEndpointHandlerMapping.class); - // } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryAuthorizationExceptionTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryAuthorizationExceptionTests.java deleted file mode 100644 index 2a13144682..0000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryAuthorizationExceptionTests.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.cloudfoundry; - -import org.junit.Test; - -import org.springframework.boot.actuate.cloudfoundry.CloudFoundryAuthorizationException.Reason; -import org.springframework.http.HttpStatus; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link CloudFoundryAuthorizationException}. - * - * @author Madhura Bhave - */ -public class CloudFoundryAuthorizationExceptionTests { - - @Test - public void statusCodeForInvalidTokenReasonShouldBe401() throws Exception { - assertThat(createException(Reason.INVALID_TOKEN).getStatusCode()) - .isEqualTo(HttpStatus.UNAUTHORIZED); - } - - @Test - public void statusCodeForInvalidIssuerReasonShouldBe401() throws Exception { - assertThat(createException(Reason.INVALID_ISSUER).getStatusCode()) - .isEqualTo(HttpStatus.UNAUTHORIZED); - } - - @Test - public void statusCodeForInvalidAudienceReasonShouldBe401() throws Exception { - assertThat(createException(Reason.INVALID_AUDIENCE).getStatusCode()) - .isEqualTo(HttpStatus.UNAUTHORIZED); - } - - @Test - public void statusCodeForInvalidSignatureReasonShouldBe401() throws Exception { - assertThat(createException(Reason.INVALID_SIGNATURE).getStatusCode()) - .isEqualTo(HttpStatus.UNAUTHORIZED); - } - - @Test - public void statusCodeForMissingAuthorizationReasonShouldBe401() throws Exception { - assertThat(createException(Reason.MISSING_AUTHORIZATION).getStatusCode()) - .isEqualTo(HttpStatus.UNAUTHORIZED); - } - - @Test - public void statusCodeForUnsupportedSignatureAlgorithmReasonShouldBe401() - throws Exception { - assertThat(createException(Reason.UNSUPPORTED_TOKEN_SIGNING_ALGORITHM) - .getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); - } - - @Test - public void statusCodeForTokenExpiredReasonShouldBe401() throws Exception { - assertThat(createException(Reason.TOKEN_EXPIRED).getStatusCode()) - .isEqualTo(HttpStatus.UNAUTHORIZED); - } - - @Test - public void statusCodeForAccessDeniedReasonShouldBe403() throws Exception { - assertThat(createException(Reason.ACCESS_DENIED).getStatusCode()) - .isEqualTo(HttpStatus.FORBIDDEN); - } - - @Test - public void statusCodeForServiceUnavailableReasonShouldBe503() throws Exception { - assertThat(createException(Reason.SERVICE_UNAVAILABLE).getStatusCode()) - .isEqualTo(HttpStatus.SERVICE_UNAVAILABLE); - } - - private CloudFoundryAuthorizationException createException(Reason reason) { - return new CloudFoundryAuthorizationException(reason, "message"); - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryDiscoveryMvcEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryDiscoveryMvcEndpointTests.java deleted file mode 100644 index 267f71df5f..0000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryDiscoveryMvcEndpointTests.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.cloudfoundry; - -/** - * Tests for {@link CloudFoundryDiscoveryMvcEndpoint}. - * - * @author Madhura Bhave - */ -public class CloudFoundryDiscoveryMvcEndpointTests { - - // TODO CloudFoundry support - - // private CloudFoundryDiscoveryMvcEndpoint endpoint; - // - // private Set endpoints; - // - // @Before - // public void setup() { - // NamedMvcEndpoint endpoint = new TestMvcEndpoint(new TestEndpoint("a")); - // this.endpoints = new LinkedHashSet<>(); - // this.endpoints.add(endpoint); - // this.endpoint = new CloudFoundryDiscoveryMvcEndpoint(this.endpoints); - // } - // - // @Test - // public void cloudfoundryHalJsonEndpointHasEmptyPath() throws Exception { - // assertThat(this.endpoint.getPath()).isEmpty(); - // } - // - // @Test - // public void linksResponseWhenRequestUriHasNoTrailingSlash() throws Exception { - // MockHttpServletRequest request = new MockHttpServletRequest("GET", - // "/cloudfoundryapplication"); - // AccessLevel.FULL.put(request); - // Map links = this.endpoint - // .links(request).get("_links"); - // assertThat(links.get("self").getHref()) - // .isEqualTo("http://localhost/cloudfoundryapplication"); - // assertThat(links.get("a").getHref()) - // .isEqualTo("http://localhost/cloudfoundryapplication/a"); - // } - // - // @Test - // public void linksResponseWhenRequestUriHasTrailingSlash() throws Exception { - // MockHttpServletRequest request = new MockHttpServletRequest("GET", - // "/cloudfoundryapplication/"); - // AccessLevel.FULL.put(request); - // Map links = this.endpoint - // .links(request).get("_links"); - // assertThat(links.get("self").getHref()) - // .isEqualTo("http://localhost/cloudfoundryapplication"); - // assertThat(links.get("a").getHref()) - // .isEqualTo("http://localhost/cloudfoundryapplication/a"); - // } - // - // @Test - // public void linksResponseWhenRequestHasAccessLevelRestricted() throws Exception { - // NamedMvcEndpoint testHealthMvcEndpoint = new TestMvcEndpoint( - // new TestEndpoint("health")); - // this.endpoints.add(testHealthMvcEndpoint); - // MockHttpServletRequest request = new MockHttpServletRequest("GET", - // "/cloudfoundryapplication/"); - // AccessLevel.RESTRICTED.put(request); - // Map links = this.endpoint - // .links(request).get("_links"); - // assertThat(links.get("self").getHref()) - // .isEqualTo("http://localhost/cloudfoundryapplication"); - // assertThat(links.get("health").getHref()) - // .isEqualTo("http://localhost/cloudfoundryapplication/health"); - // assertThat(links.get("a")).isNull(); - // } - // - // private static class TestEndpoint extends AbstractEndpoint { - // - // TestEndpoint(String id) { - // super(id); - // } - // - // @Override - // public Object invoke() { - // return null; - // } - // - // } - // - // private static class TestMvcEndpoint extends EndpointMvcAdapter { - // - // TestMvcEndpoint(TestEndpoint delegate) { - // super(delegate); - // } - // - // } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryEndpointHandlerMappingTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryEndpointHandlerMappingTests.java deleted file mode 100644 index 05afb33341..0000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryEndpointHandlerMappingTests.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.cloudfoundry; - -/** - * Tests for {@link CloudFoundryEndpointHandlerMapping}. - * - * @author Madhura Bhave - */ -public class CloudFoundryEndpointHandlerMappingTests { - - // TODO CloudFoundry support - - // @Test - // public void getHandlerExecutionChainWhenEndpointHasPathShouldMapAgainstName() - // throws Exception { - // TestMvcEndpoint testMvcEndpoint = new TestMvcEndpoint(new TestEndpoint("a")); - // testMvcEndpoint.setPath("something-else"); - // CloudFoundryEndpointHandlerMapping handlerMapping = new - // CloudFoundryEndpointHandlerMapping( - // Collections.singleton(testMvcEndpoint), null, null); - // assertThat(handlerMapping.getPath(testMvcEndpoint)).isEqualTo("/a"); - // } - // - // @Test - // public void doesNotRegisterHalJsonMvcEndpoint() throws Exception { - // CloudFoundryEndpointHandlerMapping handlerMapping = new - // CloudFoundryEndpointHandlerMapping( - // Collections.singleton(new TestHalJsonMvcEndpoint()), null, null); - // assertThat(handlerMapping.getEndpoints()).hasSize(0); - // } - // - // @Test - // public void registersCloudFoundryDiscoveryEndpoint() throws Exception { - // StaticApplicationContext context = new StaticApplicationContext(); - // CloudFoundryEndpointHandlerMapping handlerMapping = new - // CloudFoundryEndpointHandlerMapping( - // Collections.emptySet(), null, null); - // handlerMapping.setPrefix("/test"); - // handlerMapping.setApplicationContext(context); - // handlerMapping.afterPropertiesSet(); - // HandlerExecutionChain handler = handlerMapping - // .getHandler(new MockHttpServletRequest("GET", "/test")); - // HandlerMethod handlerMethod = (HandlerMethod) handler.getHandler(); - // assertThat(handlerMethod.getBean()) - // .isInstanceOf(CloudFoundryDiscoveryMvcEndpoint.class); - // } - // - // @Test - // public void registersCloudFoundryHealthEndpoint() throws Exception { - // StaticApplicationContext context = new StaticApplicationContext(); - // HealthEndpoint delegate = new HealthEndpoint(new OrderedHealthAggregator(), - // Collections.emptyMap()); - // CloudFoundryEndpointHandlerMapping handlerMapping = new - // CloudFoundryEndpointHandlerMapping( - // Collections.singleton(new TestHealthMvcEndpoint(delegate)), null, null); - // handlerMapping.setPrefix("/test"); - // handlerMapping.setApplicationContext(context); - // handlerMapping.afterPropertiesSet(); - // HandlerExecutionChain handler = handlerMapping - // .getHandler(new MockHttpServletRequest("GET", "/test/health")); - // HandlerMethod handlerMethod = (HandlerMethod) handler.getHandler(); - // Object handlerMethodBean = handlerMethod.getBean(); - // assertThat(handlerMethodBean).isInstanceOf(CloudFoundryHealthMvcEndpoint.class); - // } - // - // private static class TestEndpoint extends AbstractEndpoint { - // - // TestEndpoint(String id) { - // super(id); - // } - // - // @Override - // public Object invoke() { - // return null; - // } - // - // } - // - // private static class TestMvcEndpoint extends EndpointMvcAdapter { - // - // TestMvcEndpoint(TestEndpoint delegate) { - // super(delegate); - // } - // - // } - // - // private static class TestHalJsonMvcEndpoint extends HalJsonMvcEndpoint { - // - // TestHalJsonMvcEndpoint() { - // super(new ManagementServletContext() { - // - // @Override - // public String getContextPath() { - // return ""; - // } - // - // }); - // } - // - // } - // - // private static class TestHealthMvcEndpoint extends HealthWebEndpointExtension { - // - // TestHealthMvcEndpoint(HealthEndpoint delegate) { - // super(delegate); - // } - // - // } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryHealthMvcEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryHealthMvcEndpointTests.java deleted file mode 100644 index a10f5705d7..0000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryHealthMvcEndpointTests.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.cloudfoundry; - -/** - * Tests for {@link CloudFoundryHealthMvcEndpoint}. - * - * @author Madhura Bhave - */ -public class CloudFoundryHealthMvcEndpointTests { - - // TODO CloudFoundry-specific health endpoint? - - // @Test - // public void cloudFoundryHealthEndpointShouldAlwaysReturnAllHealthDetails() - // throws Exception { - // HealthEndpoint endpoint = mock(HealthEndpoint.class); - // given(endpoint.isEnabled()).willReturn(true); - // CloudFoundryHealthMvcEndpoint mvc = new CloudFoundryHealthMvcEndpoint(endpoint); - // given(endpoint.invoke()) - // .willReturn(new Health.Builder().up().withDetail("foo", "bar").build()); - // given(endpoint.isSensitive()).willReturn(false); - // Object result = mvc.invoke(null, null); - // assertThat(result instanceof Health).isTrue(); - // assertThat(((Health) result).getStatus() == Status.UP).isTrue(); - // assertThat(((Health) result).getDetails().get("foo")).isEqualTo("bar"); - // } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundrySecurityInterceptorTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundrySecurityInterceptorTests.java deleted file mode 100644 index 6806991db7..0000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundrySecurityInterceptorTests.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.cloudfoundry; - -/** - * Tests for {@link CloudFoundrySecurityInterceptor}. - * - * @author Madhura Bhave - */ -public class CloudFoundrySecurityInterceptorTests { - - // TODO CloudFoundry-specific security - - // @Mock - // private TokenValidator tokenValidator; - // - // @Mock - // private CloudFoundrySecurityService securityService; - // - // private CloudFoundrySecurityInterceptor interceptor; - // - // private TestMvcEndpoint endpoint; - // - // private HandlerMethod handlerMethod; - // - // private MockHttpServletRequest request; - // - // private MockHttpServletResponse response; - // - // @Before - // public void setup() throws Exception { - // MockitoAnnotations.initMocks(this); - // this.interceptor = new CloudFoundrySecurityInterceptor(this.tokenValidator, - // this.securityService, "my-app-id"); - // this.endpoint = new TestMvcEndpoint(new TestEndpoint("a")); - // this.handlerMethod = new HandlerMethod(this.endpoint, "invoke"); - // this.request = new MockHttpServletRequest(); - // this.response = new MockHttpServletResponse(); - // } - // - // @Test - // public void preHandleWhenRequestIsPreFlightShouldReturnTrue() throws Exception { - // this.request.setMethod("OPTIONS"); - // this.request.addHeader(HttpHeaders.ORIGIN, "http://example.com"); - // this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET"); - // boolean preHandle = this.interceptor.preHandle(this.request, this.response, - // this.handlerMethod); - // assertThat(preHandle).isTrue(); - // } - // - // @Test - // public void preHandleWhenTokenIsMissingShouldReturnFalse() throws Exception { - // boolean preHandle = this.interceptor.preHandle(this.request, this.response, - // this.handlerMethod); - // assertThat(preHandle).isFalse(); - // assertThat(this.response.getStatus()) - // .isEqualTo(Reason.MISSING_AUTHORIZATION.getStatus().value()); - // assertThat(this.response.getContentAsString()).contains("security_error"); - // assertThat(this.response.getContentType()) - // .isEqualTo(MediaType.APPLICATION_JSON.toString()); - // } - // - // @Test - // public void preHandleWhenTokenIsNotBearerShouldReturnFalse() throws Exception { - // this.request.addHeader("Authorization", mockAccessToken()); - // boolean preHandle = this.interceptor.preHandle(this.request, this.response, - // this.handlerMethod); - // assertThat(preHandle).isFalse(); - // assertThat(this.response.getStatus()) - // .isEqualTo(Reason.MISSING_AUTHORIZATION.getStatus().value()); - // } - // - // @Test - // public void preHandleWhenApplicationIdIsNullShouldReturnFalse() throws Exception { - // this.interceptor = new CloudFoundrySecurityInterceptor(this.tokenValidator, - // this.securityService, null); - // this.request.addHeader("Authorization", "bearer " + mockAccessToken()); - // boolean preHandle = this.interceptor.preHandle(this.request, this.response, - // this.handlerMethod); - // assertThat(preHandle).isFalse(); - // assertThat(this.response.getStatus()) - // .isEqualTo(Reason.SERVICE_UNAVAILABLE.getStatus().value()); - // } - // - // @Test - // public void preHandleWhenCloudFoundrySecurityServiceIsNullShouldReturnFalse() - // throws Exception { - // this.interceptor = new CloudFoundrySecurityInterceptor(this.tokenValidator, null, - // "my-app-id"); - // this.request.addHeader("Authorization", "bearer " + mockAccessToken()); - // boolean preHandle = this.interceptor.preHandle(this.request, this.response, - // this.handlerMethod); - // assertThat(preHandle).isFalse(); - // assertThat(this.response.getStatus()) - // .isEqualTo(Reason.SERVICE_UNAVAILABLE.getStatus().value()); - // } - // - // @Test - // public void preHandleWhenAccessIsNotAllowedShouldReturnFalse() throws Exception { - // this.endpoint = new TestMvcEndpoint(new TestEndpoint("env")); - // this.handlerMethod = new HandlerMethod(this.endpoint, "invoke"); - // String accessToken = mockAccessToken(); - // this.request.addHeader("Authorization", "bearer " + accessToken); - // BDDMockito.given(this.securityService.getAccessLevel(accessToken, "my-app-id")) - // .willReturn(AccessLevel.RESTRICTED); - // boolean preHandle = this.interceptor.preHandle(this.request, this.response, - // this.handlerMethod); - // assertThat(preHandle).isFalse(); - // assertThat(this.response.getStatus()) - // .isEqualTo(Reason.ACCESS_DENIED.getStatus().value()); - // } - // - // @Test - // public void preHandleSuccessfulWithFullAccess() throws Exception { - // String accessToken = mockAccessToken(); - // this.request.addHeader("Authorization", "Bearer " + accessToken); - // BDDMockito.given(this.securityService.getAccessLevel(accessToken, "my-app-id")) - // .willReturn(AccessLevel.FULL); - // boolean preHandle = this.interceptor.preHandle(this.request, this.response, - // this.handlerMethod); - // ArgumentCaptor tokenArgumentCaptor = ArgumentCaptor.forClass(Token.class); - // verify(this.tokenValidator).validate(tokenArgumentCaptor.capture()); - // Token token = tokenArgumentCaptor.getValue(); - // assertThat(token.toString()).isEqualTo(accessToken); - // assertThat(preHandle).isTrue(); - // assertThat(this.response.getStatus()).isEqualTo(HttpStatus.OK.value()); - // assertThat(this.request.getAttribute("cloudFoundryAccessLevel")) - // .isEqualTo(AccessLevel.FULL); - // } - // - // @Test - // public void preHandleSuccessfulWithRestrictedAccess() throws Exception { - // this.endpoint = new TestMvcEndpoint(new TestEndpoint("info")); - // this.handlerMethod = new HandlerMethod(this.endpoint, "invoke"); - // String accessToken = mockAccessToken(); - // this.request.addHeader("Authorization", "Bearer " + accessToken); - // BDDMockito.given(this.securityService.getAccessLevel(accessToken, "my-app-id")) - // .willReturn(AccessLevel.RESTRICTED); - // boolean preHandle = this.interceptor.preHandle(this.request, this.response, - // this.handlerMethod); - // ArgumentCaptor tokenArgumentCaptor = ArgumentCaptor.forClass(Token.class); - // verify(this.tokenValidator).validate(tokenArgumentCaptor.capture()); - // Token token = tokenArgumentCaptor.getValue(); - // assertThat(token.toString()).isEqualTo(accessToken); - // assertThat(preHandle).isTrue(); - // assertThat(this.response.getStatus()).isEqualTo(HttpStatus.OK.value()); - // assertThat(this.request.getAttribute("cloudFoundryAccessLevel")) - // .isEqualTo(AccessLevel.RESTRICTED); - // } - // - // private String mockAccessToken() { - // return "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0b3B0YWwu" - // + "Y29tIiwiZXhwIjoxNDI2NDIwODAwLCJhd2Vzb21lIjp0cnVlfQ." - // + Base64Utils.encodeToString("signature".getBytes()); - // } - // - // private static class TestEndpoint extends AbstractEndpoint { - // - // TestEndpoint(String id) { - // super(id); - // } - // - // @Override - // public Object invoke() { - // return null; - // } - // - // } - // - // private static class TestMvcEndpoint extends EndpointMvcAdapter { - // - // TestMvcEndpoint(TestEndpoint delegate) { - // super(delegate); - // } - // - // } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundrySecurityServiceTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundrySecurityServiceTests.java deleted file mode 100644 index a5cf377bd5..0000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundrySecurityServiceTests.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.cloudfoundry; - -import java.util.Map; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import org.springframework.boot.actuate.cloudfoundry.CloudFoundryAuthorizationException.Reason; -import org.springframework.boot.test.web.client.MockServerRestTemplateCustomizer; -import org.springframework.boot.web.client.RestTemplateBuilder; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.test.web.client.MockRestServiceServer; -import org.springframework.web.client.RestTemplate; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.test.web.client.match.MockRestRequestMatchers.header; -import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; -import static org.springframework.test.web.client.response.MockRestResponseCreators.withServerError; -import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; -import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; -import static org.springframework.test.web.client.response.MockRestResponseCreators.withUnauthorizedRequest; - -/** - * Tests for {@link CloudFoundrySecurityService}. - * - * @author Madhura Bhave - */ -public class CloudFoundrySecurityServiceTests { - - @Rule - public ExpectedException thrown = ExpectedException.none(); - - private static final String CLOUD_CONTROLLER = "http://my-cloud-controller.com"; - - private static final String CLOUD_CONTROLLER_PERMISSIONS = CLOUD_CONTROLLER - + "/v2/apps/my-app-id/permissions"; - - private static final String UAA_URL = "http://my-uaa.com"; - - private CloudFoundrySecurityService securityService; - - private MockRestServiceServer server; - - @Before - public void setup() throws Exception { - MockServerRestTemplateCustomizer mockServerCustomizer = new MockServerRestTemplateCustomizer(); - RestTemplateBuilder builder = new RestTemplateBuilder(mockServerCustomizer); - this.securityService = new CloudFoundrySecurityService(builder, CLOUD_CONTROLLER, - false); - this.server = mockServerCustomizer.getServer(); - } - - @Test - public void skipSslValidationWhenTrue() throws Exception { - RestTemplateBuilder builder = new RestTemplateBuilder(); - this.securityService = new CloudFoundrySecurityService(builder, CLOUD_CONTROLLER, - true); - RestTemplate restTemplate = (RestTemplate) ReflectionTestUtils - .getField(this.securityService, "restTemplate"); - assertThat(restTemplate.getRequestFactory()) - .isInstanceOf(SkipSslVerificationHttpRequestFactory.class); - } - - @Test - public void doNotskipSslValidationWhenFalse() throws Exception { - RestTemplateBuilder builder = new RestTemplateBuilder(); - this.securityService = new CloudFoundrySecurityService(builder, CLOUD_CONTROLLER, - false); - RestTemplate restTemplate = (RestTemplate) ReflectionTestUtils - .getField(this.securityService, "restTemplate"); - assertThat(restTemplate.getRequestFactory()) - .isNotInstanceOf(SkipSslVerificationHttpRequestFactory.class); - } - - @Test - public void getAccessLevelWhenSpaceDeveloperShouldReturnFull() throws Exception { - String responseBody = "{\"read_sensitive_data\": true,\"read_basic_data\": true}"; - this.server.expect(requestTo(CLOUD_CONTROLLER_PERMISSIONS)) - .andExpect(header("Authorization", "bearer my-access-token")) - .andRespond(withSuccess(responseBody, MediaType.APPLICATION_JSON)); - AccessLevel accessLevel = this.securityService.getAccessLevel("my-access-token", - "my-app-id"); - this.server.verify(); - assertThat(accessLevel).isEqualTo(AccessLevel.FULL); - } - - @Test - public void getAccessLevelWhenNotSpaceDeveloperShouldReturnRestricted() - throws Exception { - String responseBody = "{\"read_sensitive_data\": false,\"read_basic_data\": true}"; - this.server.expect(requestTo(CLOUD_CONTROLLER_PERMISSIONS)) - .andExpect(header("Authorization", "bearer my-access-token")) - .andRespond(withSuccess(responseBody, MediaType.APPLICATION_JSON)); - AccessLevel accessLevel = this.securityService.getAccessLevel("my-access-token", - "my-app-id"); - this.server.verify(); - assertThat(accessLevel).isEqualTo(AccessLevel.RESTRICTED); - } - - @Test - public void getAccessLevelWhenTokenIsNotValidShouldThrowException() throws Exception { - this.server.expect(requestTo(CLOUD_CONTROLLER_PERMISSIONS)) - .andExpect(header("Authorization", "bearer my-access-token")) - .andRespond(withUnauthorizedRequest()); - this.thrown - .expect(AuthorizationExceptionMatcher.withReason(Reason.INVALID_TOKEN)); - this.securityService.getAccessLevel("my-access-token", "my-app-id"); - } - - @Test - public void getAccessLevelWhenForbiddenShouldThrowException() throws Exception { - this.server.expect(requestTo(CLOUD_CONTROLLER_PERMISSIONS)) - .andExpect(header("Authorization", "bearer my-access-token")) - .andRespond(withStatus(HttpStatus.FORBIDDEN)); - this.thrown - .expect(AuthorizationExceptionMatcher.withReason(Reason.ACCESS_DENIED)); - this.securityService.getAccessLevel("my-access-token", "my-app-id"); - } - - @Test - public void getAccessLevelWhenCloudControllerIsNotReachableThrowsException() - throws Exception { - this.server.expect(requestTo(CLOUD_CONTROLLER_PERMISSIONS)) - .andExpect(header("Authorization", "bearer my-access-token")) - .andRespond(withServerError()); - this.thrown.expect( - AuthorizationExceptionMatcher.withReason(Reason.SERVICE_UNAVAILABLE)); - this.securityService.getAccessLevel("my-access-token", "my-app-id"); - } - - @Test - public void fetchTokenKeysWhenSuccessfulShouldReturnListOfKeysFromUAA() - throws Exception { - this.server.expect(requestTo(CLOUD_CONTROLLER + "/info")) - .andRespond(withSuccess("{\"token_endpoint\":\"http://my-uaa.com\"}", - MediaType.APPLICATION_JSON)); - String tokenKeyValue = "-----BEGIN PUBLIC KEY-----\n" - + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0m59l2u9iDnMbrXHfqkO\n" - + "rn2dVQ3vfBJqcDuFUK03d+1PZGbVlNCqnkpIJ8syFppW8ljnWweP7+LiWpRoz0I7\n" - + "fYb3d8TjhV86Y997Fl4DBrxgM6KTJOuE/uxnoDhZQ14LgOU2ckXjOzOdTsnGMKQB\n" - + "LCl0vpcXBtFLMaSbpv1ozi8h7DJyVZ6EnFQZUWGdgTMhDrmqevfx95U/16c5WBDO\n" - + "kqwIn7Glry9n9Suxygbf8g5AzpWcusZgDLIIZ7JTUldBb8qU2a0Dl4mvLZOn4wPo\n" - + "jfj9Cw2QICsc5+Pwf21fP+hzf+1WSRHbnYv8uanRO0gZ8ekGaghM/2H6gqJbo2nI\n" - + "JwIDAQAB\n-----END PUBLIC KEY-----"; - String responseBody = "{\"keys\" : [ {\"kid\":\"test-key\",\"value\" : \"" - + tokenKeyValue.replace("\n", "\\n") + "\"} ]}"; - this.server.expect(requestTo(UAA_URL + "/token_keys")) - .andRespond(withSuccess(responseBody, MediaType.APPLICATION_JSON)); - Map tokenKeys = this.securityService.fetchTokenKeys(); - this.server.verify(); - assertThat(tokenKeys.get("test-key")).isEqualTo(tokenKeyValue); - } - - @Test - public void fetchTokenKeysWhenNoKeysReturnedFromUAA() throws Exception { - this.server.expect(requestTo(CLOUD_CONTROLLER + "/info")).andRespond(withSuccess( - "{\"token_endpoint\":\"" + UAA_URL + "\"}", MediaType.APPLICATION_JSON)); - String responseBody = "{\"keys\": []}"; - this.server.expect(requestTo(UAA_URL + "/token_keys")) - .andRespond(withSuccess(responseBody, MediaType.APPLICATION_JSON)); - Map tokenKeys = this.securityService.fetchTokenKeys(); - this.server.verify(); - assertThat(tokenKeys).hasSize(0); - } - - @Test - public void fetchTokenKeysWhenUnsuccessfulShouldThrowException() throws Exception { - this.server.expect(requestTo(CLOUD_CONTROLLER + "/info")).andRespond(withSuccess( - "{\"token_endpoint\":\"" + UAA_URL + "\"}", MediaType.APPLICATION_JSON)); - this.server.expect(requestTo(UAA_URL + "/token_keys")) - .andRespond(withServerError()); - this.thrown.expect( - AuthorizationExceptionMatcher.withReason(Reason.SERVICE_UNAVAILABLE)); - this.securityService.fetchTokenKeys(); - } - - @Test - public void getUaaUrlShouldCallCloudControllerInfoOnlyOnce() throws Exception { - this.server.expect(requestTo(CLOUD_CONTROLLER + "/info")).andRespond(withSuccess( - "{\"token_endpoint\":\"" + UAA_URL + "\"}", MediaType.APPLICATION_JSON)); - String uaaUrl = this.securityService.getUaaUrl(); - this.server.verify(); - assertThat(uaaUrl).isEqualTo(UAA_URL); - // Second call should not need to hit server - uaaUrl = this.securityService.getUaaUrl(); - assertThat(uaaUrl).isEqualTo(UAA_URL); - } - - @Test - public void getUaaUrlWhenCloudControllerUrlIsNotReachableShouldThrowException() - throws Exception { - this.server.expect(requestTo(CLOUD_CONTROLLER + "/info")) - .andRespond(withServerError()); - this.thrown.expect( - AuthorizationExceptionMatcher.withReason(Reason.SERVICE_UNAVAILABLE)); - this.securityService.getUaaUrl(); - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/SkipSslVerificationHttpRequestFactoryTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/SkipSslVerificationHttpRequestFactoryTests.java deleted file mode 100644 index df585067b1..0000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/SkipSslVerificationHttpRequestFactoryTests.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.cloudfoundry; - -import javax.net.ssl.SSLHandshakeException; - -import org.hamcrest.Matcher; -import org.junit.After; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import org.springframework.boot.testsupport.web.servlet.ExampleServlet; -import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; -import org.springframework.boot.web.server.Ssl; -import org.springframework.boot.web.server.WebServer; -import org.springframework.boot.web.servlet.ServletRegistrationBean; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.client.ResourceAccessException; -import org.springframework.web.client.RestTemplate; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.hamcrest.Matchers.instanceOf; - -/** - * Test for {@link SkipSslVerificationHttpRequestFactory}. - */ -public class SkipSslVerificationHttpRequestFactoryTests { - - @Rule - public ExpectedException thrown = ExpectedException.none(); - - private WebServer webServer; - - @After - public void shutdownContainer() { - if (this.webServer != null) { - this.webServer.stop(); - } - } - - @Test - public void restCallToSelfSignedServerShouldNotThrowSslException() throws Exception { - String httpsUrl = getHttpsUrl(); - SkipSslVerificationHttpRequestFactory requestFactory = new SkipSslVerificationHttpRequestFactory(); - RestTemplate restTemplate = new RestTemplate(requestFactory); - ResponseEntity responseEntity = restTemplate.getForEntity(httpsUrl, - String.class); - assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK); - this.thrown.expect(ResourceAccessException.class); - this.thrown.expectCause(isSSLHandshakeException()); - RestTemplate otherRestTemplate = new RestTemplate(); - otherRestTemplate.getForEntity(httpsUrl, String.class); - } - - private Matcher isSSLHandshakeException() { - return instanceOf(SSLHandshakeException.class); - } - - private String getHttpsUrl() { - TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(0); - factory.setSsl(getSsl("password", "classpath:test.jks")); - this.webServer = factory.getWebServer( - new ServletRegistrationBean<>(new ExampleServlet(), "/hello")); - this.webServer.start(); - return "https://localhost:" + this.webServer.getPort() + "/hello"; - } - - private Ssl getSsl(String keyPassword, String keyStore) { - Ssl ssl = new Ssl(); - ssl.setEnabled(true); - ssl.setKeyPassword(keyPassword); - ssl.setKeyStore(keyStore); - ssl.setKeyStorePassword("secret"); - return ssl; - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/TokenTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/TokenTests.java deleted file mode 100644 index 37ac32ecd6..0000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/TokenTests.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.cloudfoundry; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import org.springframework.boot.actuate.cloudfoundry.CloudFoundryAuthorizationException.Reason; -import org.springframework.util.Base64Utils; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link Token}. - * - * @author Madhura Bhave - */ -public class TokenTests { - - @Rule - public ExpectedException thrown = ExpectedException.none(); - - @Test - public void invalidJwtShouldThrowException() throws Exception { - this.thrown - .expect(AuthorizationExceptionMatcher.withReason(Reason.INVALID_TOKEN)); - new Token("invalid-token"); - } - - @Test - public void invalidJwtClaimsShouldThrowException() throws Exception { - String header = "{\"alg\": \"RS256\", \"kid\": \"key-id\", \"typ\": \"JWT\"}"; - String claims = "invalid-claims"; - this.thrown - .expect(AuthorizationExceptionMatcher.withReason(Reason.INVALID_TOKEN)); - new Token(Base64Utils.encodeToString(header.getBytes()) + "." - + Base64Utils.encodeToString(claims.getBytes())); - } - - @Test - public void invalidJwtHeaderShouldThrowException() throws Exception { - String header = "invalid-header"; - String claims = "{\"exp\": 2147483647, \"iss\": \"http://localhost:8080/uaa/oauth/token\"}"; - this.thrown - .expect(AuthorizationExceptionMatcher.withReason(Reason.INVALID_TOKEN)); - new Token(Base64Utils.encodeToString(header.getBytes()) + "." - + Base64Utils.encodeToString(claims.getBytes())); - } - - @Test - public void emptyJwtSignatureShouldThrowException() throws Exception { - String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0b3B0YWwu" - + "Y29tIiwiZXhwIjoxNDI2NDIwODAwLCJhd2Vzb21lIjp0cnVlfQ."; - this.thrown - .expect(AuthorizationExceptionMatcher.withReason(Reason.INVALID_TOKEN)); - new Token(token); - } - - @Test - public void validJwt() throws Exception { - String header = "{\"alg\": \"RS256\", \"kid\": \"key-id\", \"typ\": \"JWT\"}"; - String claims = "{\"exp\": 2147483647, \"iss\": \"http://localhost:8080/uaa/oauth/token\"}"; - String content = Base64Utils.encodeToString(header.getBytes()) + "." - + Base64Utils.encodeToString(claims.getBytes()); - String signature = Base64Utils.encodeToString("signature".getBytes()); - Token token = new Token(content + "." + signature); - assertThat(token.getExpiry()).isEqualTo(2147483647); - assertThat(token.getIssuer()).isEqualTo("http://localhost:8080/uaa/oauth/token"); - assertThat(token.getSignatureAlgorithm()).isEqualTo("RS256"); - assertThat(token.getKeyId()).isEqualTo("key-id"); - assertThat(token.getContent()).isEqualTo(content.getBytes()); - assertThat(token.getSignature()) - .isEqualTo(Base64Utils.decodeFromString(signature)); - } - - @Test - public void getSignatureAlgorithmWhenAlgIsNullShouldThrowException() - throws Exception { - String header = "{\"kid\": \"key-id\", \"typ\": \"JWT\"}"; - String claims = "{\"exp\": 2147483647, \"iss\": \"http://localhost:8080/uaa/oauth/token\"}"; - Token token = createToken(header, claims); - this.thrown - .expect(AuthorizationExceptionMatcher.withReason(Reason.INVALID_TOKEN)); - token.getSignatureAlgorithm(); - } - - @Test - public void getIssuerWhenIssIsNullShouldThrowException() throws Exception { - String header = "{\"alg\": \"RS256\", \"kid\": \"key-id\", \"typ\": \"JWT\"}"; - String claims = "{\"exp\": 2147483647}"; - Token token = createToken(header, claims); - this.thrown - .expect(AuthorizationExceptionMatcher.withReason(Reason.INVALID_TOKEN)); - token.getIssuer(); - } - - @Test - public void getKidWhenKidIsNullShouldThrowException() throws Exception { - String header = "{\"alg\": \"RS256\", \"typ\": \"JWT\"}"; - String claims = "{\"exp\": 2147483647}"; - Token token = createToken(header, claims); - this.thrown - .expect(AuthorizationExceptionMatcher.withReason(Reason.INVALID_TOKEN)); - token.getKeyId(); - } - - @Test - public void getExpiryWhenExpIsNullShouldThrowException() throws Exception { - String header = "{\"alg\": \"RS256\", \"kid\": \"key-id\", \"typ\": \"JWT\"}"; - String claims = "{\"iss\": \"http://localhost:8080/uaa/oauth/token\"" + "}"; - Token token = createToken(header, claims); - this.thrown - .expect(AuthorizationExceptionMatcher.withReason(Reason.INVALID_TOKEN)); - token.getExpiry(); - } - - private Token createToken(String header, String claims) { - Token token = new Token(Base64Utils.encodeToString(header.getBytes()) + "." - + Base64Utils.encodeToString(claims.getBytes()) + "." - + Base64Utils.encodeToString("signature".getBytes())); - return token; - } - -} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/TokenValidatorTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/TokenValidatorTests.java deleted file mode 100644 index a3adfcc658..0000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/TokenValidatorTests.java +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.actuate.cloudfoundry; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.nio.charset.Charset; -import java.security.KeyFactory; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.Signature; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.PKCS8EncodedKeySpec; -import java.util.Collections; -import java.util.Map; - -import org.apache.commons.codec.binary.Base64; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; - -import org.springframework.boot.actuate.cloudfoundry.CloudFoundryAuthorizationException.Reason; -import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.util.Base64Utils; -import org.springframework.util.StreamUtils; - -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.verify; - -/** - * Tests for {@link TokenValidator}. - * - * @author Madhura Bhave - */ -public class TokenValidatorTests { - - private static final byte[] DOT = ".".getBytes(); - - private static final Charset UTF_8 = Charset.forName("UTF-8"); - - @Rule - public ExpectedException thrown = ExpectedException.none(); - - @Mock - private CloudFoundrySecurityService securityService; - - private TokenValidator tokenValidator; - - private static final String VALID_KEY = "-----BEGIN PUBLIC KEY-----\n" - + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0m59l2u9iDnMbrXHfqkO\n" - + "rn2dVQ3vfBJqcDuFUK03d+1PZGbVlNCqnkpIJ8syFppW8ljnWweP7+LiWpRoz0I7\n" - + "fYb3d8TjhV86Y997Fl4DBrxgM6KTJOuE/uxnoDhZQ14LgOU2ckXjOzOdTsnGMKQB\n" - + "LCl0vpcXBtFLMaSbpv1ozi8h7DJyVZ6EnFQZUWGdgTMhDrmqevfx95U/16c5WBDO\n" - + "kqwIn7Glry9n9Suxygbf8g5AzpWcusZgDLIIZ7JTUldBb8qU2a0Dl4mvLZOn4wPo\n" - + "jfj9Cw2QICsc5+Pwf21fP+hzf+1WSRHbnYv8uanRO0gZ8ekGaghM/2H6gqJbo2nI\n" - + "JwIDAQAB\n-----END PUBLIC KEY-----"; - - private static final String INVALID_KEY = "-----BEGIN PUBLIC KEY-----\n" - + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxzYuc22QSst/dS7geYYK\n" - + "5l5kLxU0tayNdixkEQ17ix+CUcUbKIsnyftZxaCYT46rQtXgCaYRdJcbB3hmyrOa\n" - + "vkhTpX79xJZnQmfuamMbZBqitvscxW9zRR9tBUL6vdi/0rpoUwPMEh8+Bw7CgYR0\n" - + "FK0DhWYBNDfe9HKcyZEv3max8Cdq18htxjEsdYO0iwzhtKRXomBWTdhD5ykd/fAC\n" - + "VTr4+KEY+IeLvubHVmLUhbE5NgWXxrRpGasDqzKhCTmsa2Ysf712rl57SlH0Wz/M\n" - + "r3F7aM9YpErzeYLrl0GhQr9BVJxOvXcVd4kmY+XkiCcrkyS1cnghnllh+LCwQu1s\n" - + "YwIDAQAB\n-----END PUBLIC KEY-----"; - - private static final Map INVALID_KEYS = Collections - .singletonMap("invalid-key", INVALID_KEY); - - private static final Map VALID_KEYS = Collections - .singletonMap("valid-key", VALID_KEY); - - @Before - public void setup() throws Exception { - MockitoAnnotations.initMocks(this); - this.tokenValidator = new TokenValidator(this.securityService); - } - - @Test - public void validateTokenWhenKidValidationFailsTwiceShouldThrowException() - throws Exception { - ReflectionTestUtils.setField(this.tokenValidator, "tokenKeys", INVALID_KEYS); - given(this.securityService.fetchTokenKeys()).willReturn(INVALID_KEYS); - String header = "{\"alg\": \"RS256\", \"kid\": \"valid-key\",\"typ\": \"JWT\"}"; - String claims = "{\"exp\": 2147483647, \"iss\": \"http://localhost:8080/uaa/oauth/token\", \"scope\": [\"actuator.read\"]}"; - this.thrown - .expect(AuthorizationExceptionMatcher.withReason(Reason.INVALID_KEY_ID)); - this.tokenValidator.validate( - new Token(getSignedToken(header.getBytes(), claims.getBytes()))); - } - - @Test - public void validateTokenWhenKidValidationSucceedsInTheSecondAttempt() - throws Exception { - ReflectionTestUtils.setField(this.tokenValidator, "tokenKeys", INVALID_KEYS); - given(this.securityService.fetchTokenKeys()).willReturn(VALID_KEYS); - given(this.securityService.getUaaUrl()).willReturn("http://localhost:8080/uaa"); - String header = "{ \"alg\": \"RS256\", \"kid\": \"valid-key\",\"typ\": \"JWT\"}"; - String claims = "{ \"exp\": 2147483647, \"iss\": \"http://localhost:8080/uaa/oauth/token\", \"scope\": [\"actuator.read\"]}"; - this.tokenValidator.validate( - new Token(getSignedToken(header.getBytes(), claims.getBytes()))); - verify(this.securityService).fetchTokenKeys(); - } - - @Test - public void validateTokenShouldFetchTokenKeysIfNull() throws Exception { - given(this.securityService.fetchTokenKeys()).willReturn(VALID_KEYS); - given(this.securityService.getUaaUrl()).willReturn("http://localhost:8080/uaa"); - String header = "{ \"alg\": \"RS256\", \"kid\": \"valid-key\",\"typ\": \"JWT\"}"; - String claims = "{ \"exp\": 2147483647, \"iss\": \"http://localhost:8080/uaa/oauth/token\", \"scope\": [\"actuator.read\"]}"; - this.tokenValidator.validate( - new Token(getSignedToken(header.getBytes(), claims.getBytes()))); - verify(this.securityService).fetchTokenKeys(); - } - - @Test - public void validateTokenWhenValidShouldNotFetchTokenKeys() throws Exception { - ReflectionTestUtils.setField(this.tokenValidator, "tokenKeys", VALID_KEYS); - given(this.securityService.getUaaUrl()).willReturn("http://localhost:8080/uaa"); - String header = "{ \"alg\": \"RS256\", \"kid\": \"valid-key\",\"typ\": \"JWT\"}"; - String claims = "{ \"exp\": 2147483647, \"iss\": \"http://localhost:8080/uaa/oauth/token\", \"scope\": [\"actuator.read\"]}"; - this.tokenValidator.validate( - new Token(getSignedToken(header.getBytes(), claims.getBytes()))); - verify(this.securityService, Mockito.never()).fetchTokenKeys(); - } - - @Test - public void validateTokenWhenSignatureInvalidShouldThrowException() throws Exception { - ReflectionTestUtils.setField(this.tokenValidator, "tokenKeys", - Collections.singletonMap("valid-key", INVALID_KEY)); - given(this.securityService.getUaaUrl()).willReturn("http://localhost:8080/uaa"); - String header = "{ \"alg\": \"RS256\", \"kid\": \"valid-key\",\"typ\": \"JWT\"}"; - String claims = "{ \"exp\": 2147483647, \"iss\": \"http://localhost:8080/uaa/oauth/token\", \"scope\": [\"actuator.read\"]}"; - this.thrown.expect( - AuthorizationExceptionMatcher.withReason(Reason.INVALID_SIGNATURE)); - this.tokenValidator.validate( - new Token(getSignedToken(header.getBytes(), claims.getBytes()))); - } - - @Test - public void validateTokenWhenTokenAlgorithmIsNotRS256ShouldThrowException() - throws Exception { - given(this.securityService.fetchTokenKeys()).willReturn(VALID_KEYS); - String header = "{ \"alg\": \"HS256\", \"typ\": \"JWT\"}"; - String claims = "{ \"exp\": 2147483647, \"iss\": \"http://localhost:8080/uaa/oauth/token\", \"scope\": [\"actuator.read\"]}"; - this.thrown.expect(AuthorizationExceptionMatcher - .withReason(Reason.UNSUPPORTED_TOKEN_SIGNING_ALGORITHM)); - this.tokenValidator.validate( - new Token(getSignedToken(header.getBytes(), claims.getBytes()))); - } - - @Test - public void validateTokenWhenExpiredShouldThrowException() throws Exception { - given(this.securityService.fetchTokenKeys()).willReturn(VALID_KEYS); - given(this.securityService.fetchTokenKeys()).willReturn(VALID_KEYS); - String header = "{ \"alg\": \"RS256\", \"kid\": \"valid-key\", \"typ\": \"JWT\"}"; - String claims = "{ \"jti\": \"0236399c350c47f3ae77e67a75e75e7d\", \"exp\": 1477509977, \"scope\": [\"actuator.read\"]}"; - this.thrown - .expect(AuthorizationExceptionMatcher.withReason(Reason.TOKEN_EXPIRED)); - this.tokenValidator.validate( - new Token(getSignedToken(header.getBytes(), claims.getBytes()))); - } - - @Test - public void validateTokenWhenIssuerIsNotValidShouldThrowException() throws Exception { - given(this.securityService.fetchTokenKeys()).willReturn(VALID_KEYS); - given(this.securityService.getUaaUrl()).willReturn("http://other-uaa.com"); - String header = "{ \"alg\": \"RS256\", \"kid\": \"valid-key\", \"typ\": \"JWT\", \"scope\": [\"actuator.read\"]}"; - String claims = "{ \"exp\": 2147483647, \"iss\": \"http://localhost:8080/uaa/oauth/token\"}"; - this.thrown - .expect(AuthorizationExceptionMatcher.withReason(Reason.INVALID_ISSUER)); - this.tokenValidator.validate( - new Token(getSignedToken(header.getBytes(), claims.getBytes()))); - } - - @Test - public void validateTokenWhenAudienceIsNotValidShouldThrowException() - throws Exception { - given(this.securityService.fetchTokenKeys()).willReturn(VALID_KEYS); - given(this.securityService.getUaaUrl()).willReturn("http://localhost:8080/uaa"); - String header = "{ \"alg\": \"RS256\", \"kid\": \"valid-key\", \"typ\": \"JWT\"}"; - String claims = "{ \"exp\": 2147483647, \"iss\": \"http://localhost:8080/uaa/oauth/token\", \"scope\": [\"foo.bar\"]}"; - this.thrown.expect( - AuthorizationExceptionMatcher.withReason(Reason.INVALID_AUDIENCE)); - this.tokenValidator.validate( - new Token(getSignedToken(header.getBytes(), claims.getBytes()))); - } - - private String getSignedToken(byte[] header, byte[] claims) throws Exception { - PrivateKey privateKey = getPrivateKey(); - Signature signature = Signature.getInstance("SHA256WithRSA"); - signature.initSign(privateKey); - byte[] content = dotConcat(Base64Utils.encodeUrlSafe(header), - Base64Utils.encode(claims)); - signature.update(content); - byte[] crypto = signature.sign(); - byte[] token = dotConcat(Base64Utils.encodeUrlSafe(header), - Base64Utils.encodeUrlSafe(claims), Base64Utils.encodeUrlSafe(crypto)); - return new String(token, UTF_8); - } - - private PrivateKey getPrivateKey() - throws InvalidKeySpecException, NoSuchAlgorithmException { - String signingKey = "-----BEGIN PRIVATE KEY-----\n" - + "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDSbn2Xa72IOcxu\n" - + "tcd+qQ6ufZ1VDe98EmpwO4VQrTd37U9kZtWU0KqeSkgnyzIWmlbyWOdbB4/v4uJa\n" - + "lGjPQjt9hvd3xOOFXzpj33sWXgMGvGAzopMk64T+7GegOFlDXguA5TZyReM7M51O\n" - + "ycYwpAEsKXS+lxcG0UsxpJum/WjOLyHsMnJVnoScVBlRYZ2BMyEOuap69/H3lT/X\n" - + "pzlYEM6SrAifsaWvL2f1K7HKBt/yDkDOlZy6xmAMsghnslNSV0FvypTZrQOXia8t\n" - + "k6fjA+iN+P0LDZAgKxzn4/B/bV8/6HN/7VZJEdudi/y5qdE7SBnx6QZqCEz/YfqC\n" - + "olujacgnAgMBAAECggEAc9X2tJ/OWWrXqinOg160gkELloJxTi8lAFsDbAGuAwpT\n" - + "JcWl1KF5CmGBjsY/8ElNi2J9GJL1HOwcBhikCVNARD1DhF6RkB13mvquWwWtTMvt\n" - + "eP8JWM19DIc+E+hw2rCuTGngqs7l4vTqpzBTNPtS2eiIJ1IsjsgvSEiAlk/wnW48\n" - + "11cf6SQMQcT3HNTWrS+yLycEuWKb6Khh8RpD9D+i8w2+IspWz5lTP7BrKCUNsLOx\n" - + "6+5T52HcaZ9z3wMnDqfqIKWl3h8M+q+HFQ4EN5BPWYV4fF7EOx7+Qf2fKDFPoTjC\n" - + "VTWzDRNAA1xPqwdF7IdPVOXCdaUJDOhHeXZGaTNSwQKBgQDxb9UiR/Jh1R3muL7I\n" - + "neIt1gXa0O+SK7NWYl4DkArYo7V81ztxI8r+xKEeu5zRZZkpaJHxOnd3VfADascw\n" - + "UfALvxGxN2z42lE6zdhrmxZ3ma+akQFsv7NyXcBT00sdW+xmOiCaAj0cgxNOXiV3\n" - + "sYOwUy3SqUIPO2obpb+KC5ALHwKBgQDfH+NSQ/jn89oVZ3lzUORa+Z+aL1TGsgzs\n" - + "p7IG0MTEYiR9/AExYUwJab0M4PDXhumeoACMfkCFALNVhpch2nXZv7X5445yRgfD\n" - + "ONY4WknecuA0rfCLTruNWnQ3RR+BXmd9jD/5igd9hEIawz3V+jCHvAtzI8/CZIBt\n" - + "AArBs5kp+QKBgQCdxwN1n6baIDemK10iJWtFoPO6h4fH8h8EeMwPb/ZmlLVpnA4Q\n" - + "Zd+mlkDkoJ5eiRKKaPfWuOqRZeuvj/wTq7g/NOIO+bWQ+rrSvuqLh5IrHpgPXmub\n" - + "8bsHJhUlspMH4KagN6ROgOAG3fGj6Qp7KdpxRCpR3KJ66czxvGNrhxre6QKBgB+s\n" - + "MCGiYnfSprd5G8VhyziazKwfYeJerfT+DQhopDXYVKPJnQW8cQW5C8wDNkzx6sHI\n" - + "pqtK1K/MnKhcVaHJmAcT7qoNQlA4Xqu4qrgPIQNBvU/dDRNJVthG6c5aspEzrG8m\n" - + "9IHgtRV9K8EOy/1O6YqrB9kNUVWf3JccdWpvqyNJAoGAORzJiQCOk4egbdcozDTo\n" - + "4Tg4qk/03qpTy5k64DxkX1nJHu8V/hsKwq9Af7Fj/iHy2Av54BLPlBaGPwMi2bzB\n" - + "gYjmUomvx/fqOTQks9Rc4PIMB43p6Rdj0sh+52SKPDR2eHbwsmpuQUXnAs20BPPI\n" - + "J/OOn5zOs8yf26os0q3+JUM=\n-----END PRIVATE KEY-----"; - String privateKey = signingKey.replace("-----BEGIN PRIVATE KEY-----\n", ""); - privateKey = privateKey.replace("-----END PRIVATE KEY-----", ""); - byte[] pkcs8EncodedBytes = Base64.decodeBase64(privateKey); - PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pkcs8EncodedBytes); - KeyFactory keyFactory = KeyFactory.getInstance("RSA"); - return keyFactory.generatePrivate(keySpec); - } - - private byte[] dotConcat(byte[]... bytes) throws IOException { - ByteArrayOutputStream result = new ByteArrayOutputStream(); - for (int i = 0; i < bytes.length; i++) { - if (i > 0) { - StreamUtils.copy(DOT, result); - } - StreamUtils.copy(bytes[i], result); - } - return result.toByteArray(); - } - -}