/*
* 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.autoconfigure.security.oauth2.resource;
import javax.annotation.PostConstruct;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration;
import org.springframework.util.StringUtils;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
/**
* Configuration properties for OAuth2 Resources.
*
* @author Dave Syer
* @author Madhura Bhave
* @since 1.3.0
*/
@ConfigurationProperties(prefix = "security.oauth2.resource")
public class ResourceServerProperties implements BeanFactoryAware {
@JsonIgnore
private final String clientId;
@JsonIgnore
private final String clientSecret;
@JsonIgnore
private ListableBeanFactory beanFactory;
private String serviceId = "resource";
/**
* Identifier of the resource.
*/
private String id;
/**
* URI of the user endpoint.
*/
private String userInfoUri;
/**
* URI of the token decoding endpoint.
*/
private String tokenInfoUri;
/**
* Use the token info, can be set to false to use the user info.
*/
private boolean preferTokenInfo = true;
/**
* The token type to send when using the userInfoUri.
*/
private String tokenType = DefaultOAuth2AccessToken.BEARER_TYPE;
private Jwt jwt = new Jwt();
private Jwk jwk = new Jwk();
/**
* The order of the filter chain used to authenticate tokens. Default puts it after
* the actuator endpoints and before the default HTTP basic filter chain (catchall).
*/
private int filterOrder = SecurityProperties.ACCESS_OVERRIDE_ORDER - 1;
public ResourceServerProperties() {
this(null, null);
}
public ResourceServerProperties(String clientId, String clientSecret) {
this.clientId = clientId;
this.clientSecret = clientSecret;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (ListableBeanFactory) beanFactory;
}
public String getResourceId() {
return this.id;
}
public String getServiceId() {
return this.serviceId;
}
public void setServiceId(String serviceId) {
this.serviceId = serviceId;
}
public String getId() {
return this.id;
}
public void setId(String id) {
this.id = id;
}
public String getUserInfoUri() {
return this.userInfoUri;
}
public void setUserInfoUri(String userInfoUri) {
this.userInfoUri = userInfoUri;
}
public String getTokenInfoUri() {
return this.tokenInfoUri;
}
public void setTokenInfoUri(String tokenInfoUri) {
this.tokenInfoUri = tokenInfoUri;
}
public boolean isPreferTokenInfo() {
return this.preferTokenInfo;
}
public void setPreferTokenInfo(boolean preferTokenInfo) {
this.preferTokenInfo = preferTokenInfo;
}
public String getTokenType() {
return this.tokenType;
}
public void setTokenType(String tokenType) {
this.tokenType = tokenType;
}
public Jwt getJwt() {
return this.jwt;
}
public void setJwt(Jwt jwt) {
this.jwt = jwt;
}
public Jwk getJwk() {
return this.jwk;
}
public void setJwk(Jwk jwk) {
this.jwk = jwk;
}
public String getClientId() {
return this.clientId;
}
public String getClientSecret() {
return this.clientSecret;
}
public int getFilterOrder() {
return this.filterOrder;
}
public void setFilterOrder(int filterOrder) {
this.filterOrder = filterOrder;
}
@PostConstruct
public void validate() {
if (countBeans(AuthorizationServerEndpointsConfiguration.class) > 0) {
// If we are an authorization server we don't need remote resource token
// services
return;
}
if (countBeans(ResourceServerTokenServicesConfiguration.class) == 0) {
// If we are not a resource server or an SSO client we don't need remote
// resource token services
return;
}
if (!StringUtils.hasText(this.clientId)) {
return;
}
try {
doValidate();
}
catch (BindException ex) {
throw new IllegalStateException(ex);
}
}
private int countBeans(Class<?> type) {
return BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, type,
true, false).length;
}
private void doValidate() throws BindException {
BindingResult errors = new BeanPropertyBindingResult(this,
"resourceServerProperties");
boolean jwtConfigPresent = StringUtils.hasText(this.jwt.getKeyUri())
|| StringUtils.hasText(this.jwt.getKeyValue());
boolean jwkConfigPresent = StringUtils.hasText(this.jwk.getKeySetUri());
if (jwtConfigPresent && jwkConfigPresent) {
errors.reject("ambiguous.keyUri",
"Only one of jwt.keyUri (or jwt.keyValue) and jwk.keySetUri should"
+ " be configured.");
}
if (!jwtConfigPresent && !jwkConfigPresent) {
if (!StringUtils.hasText(this.userInfoUri)
&& !StringUtils.hasText(this.tokenInfoUri)) {
errors.rejectValue("tokenInfoUri", "missing.tokenInfoUri",
"Missing tokenInfoUri and userInfoUri and there is no "
+ "JWT verifier key");
}
if (StringUtils.hasText(this.tokenInfoUri) && this.isPreferTokenInfo()) {
if (!StringUtils.hasText(this.clientSecret)) {
errors.rejectValue("clientSecret", "missing.clientSecret",
"Missing client secret");
}
}
}
if (errors.hasErrors()) {
throw new BindException(errors);
}
}
public class Jwt {
/**
* The verification key of the JWT token. Can either be a symmetric secret or
* PEM-encoded RSA public key. If the value is not available, you can set the URI
* instead.
*/
private String keyValue;
/**
* The URI of the JWT token. Can be set if the value is not available and the key
* is public.
*/
private String keyUri;
public String getKeyValue() {
return this.keyValue;
}
public void setKeyValue(String keyValue) {
this.keyValue = keyValue;
}
public void setKeyUri(String keyUri) {
this.keyUri = keyUri;
}
public String getKeyUri() {
return this.keyUri;
}
}
public class Jwk {
/**
* The URI to get verification keys to verify the JWT token. This can be set when
* the authorization server returns a set of verification keys.
*/
private String keySetUri;
public String getKeySetUri() {
return this.keySetUri;
}
public void setKeySetUri(String keySetUri) {
this.keySetUri = keySetUri;
}
}
}