Introduce introspection result assembler to allow for customized introspection results
parent
bf00c1f5e0
commit
9dfac35912
|
@ -0,0 +1,48 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright 2014 The MITRE Corporation
|
||||||
|
* and the MIT Kerberos and Internet Trust Consortium
|
||||||
|
*
|
||||||
|
* 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.mitre.oauth2.service;
|
||||||
|
|
||||||
|
import org.mitre.oauth2.model.OAuth2AccessTokenEntity;
|
||||||
|
import org.mitre.oauth2.model.OAuth2RefreshTokenEntity;
|
||||||
|
import org.mitre.openid.connect.model.UserInfo;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strategy interface for assembling a token introspection result.
|
||||||
|
*/
|
||||||
|
public interface IntrospectionResultAssembler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assemble a token introspection result from the given access token and user info.
|
||||||
|
*
|
||||||
|
* @param accessToken the access token
|
||||||
|
* @param userInfo the user info
|
||||||
|
* @return the token introspection result
|
||||||
|
*/
|
||||||
|
Map<String, Object> assembleFrom(OAuth2AccessTokenEntity accessToken, UserInfo userInfo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assemble a token introspection result from the given refresh token and user info.
|
||||||
|
*
|
||||||
|
* @param refreshToken the refresh token
|
||||||
|
* @param userInfo the user info
|
||||||
|
* @return the token introspection result
|
||||||
|
*/
|
||||||
|
Map<String, Object> assembleFrom(OAuth2RefreshTokenEntity refreshToken, UserInfo userInfo);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright 2014 The MITRE Corporation
|
||||||
|
* and the MIT Kerberos and Internet Trust Consortium
|
||||||
|
*
|
||||||
|
* 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.mitre.oauth2.service.impl;
|
||||||
|
|
||||||
|
import com.google.common.base.Joiner;
|
||||||
|
import org.mitre.oauth2.model.OAuth2AccessTokenEntity;
|
||||||
|
import org.mitre.oauth2.model.OAuth2RefreshTokenEntity;
|
||||||
|
import org.mitre.oauth2.service.IntrospectionResultAssembler;
|
||||||
|
import org.mitre.openid.connect.model.UserInfo;
|
||||||
|
import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static com.google.common.collect.Maps.newLinkedHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation of the {@link IntrospectionResultAssembler} interface.
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class DefaultIntrospectionResultAssembler implements IntrospectionResultAssembler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> assembleFrom(OAuth2AccessTokenEntity accessToken, UserInfo userInfo) {
|
||||||
|
|
||||||
|
Map<String, Object> result = newLinkedHashMap();
|
||||||
|
OAuth2Authentication authentication = accessToken.getAuthenticationHolder().getAuthentication();
|
||||||
|
|
||||||
|
result.put("active", true);
|
||||||
|
|
||||||
|
result.put("scope", Joiner.on(" ").join(accessToken.getScope()));
|
||||||
|
|
||||||
|
if (accessToken.getExpiration() != null) {
|
||||||
|
result.put("exp", accessToken.getExpiration());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userInfo != null) {
|
||||||
|
// if we have a UserInfo, use that for the subject
|
||||||
|
result.put("sub", userInfo.getSub());
|
||||||
|
} else {
|
||||||
|
// otherwise, use the authentication's username
|
||||||
|
result.put("sub", authentication.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
result.put("user_id", authentication.getName());
|
||||||
|
|
||||||
|
result.put("client_id", authentication.getOAuth2Request().getClientId());
|
||||||
|
|
||||||
|
result.put("token_type", accessToken.getTokenType());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> assembleFrom(OAuth2RefreshTokenEntity refreshToken, UserInfo userInfo) {
|
||||||
|
|
||||||
|
Map<String, Object> result = newLinkedHashMap();
|
||||||
|
OAuth2Authentication authentication = refreshToken.getAuthenticationHolder().getAuthentication();
|
||||||
|
|
||||||
|
result.put("active", true);
|
||||||
|
|
||||||
|
result.put("scope", Joiner.on(" ").join(authentication.getOAuth2Request().getScope()));
|
||||||
|
|
||||||
|
if (refreshToken.getExpiration() != null) {
|
||||||
|
result.put("exp", refreshToken.getExpiration());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userInfo != null) {
|
||||||
|
// if we have a UserInfo, use that for the subject
|
||||||
|
result.put("sub", userInfo.getSub());
|
||||||
|
} else {
|
||||||
|
// otherwise, use the authentication's username
|
||||||
|
result.put("sub", authentication.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
result.put("user_id", authentication.getName());
|
||||||
|
|
||||||
|
result.put("client_id", authentication.getOAuth2Request().getClientId());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,143 +0,0 @@
|
||||||
/*******************************************************************************
|
|
||||||
* Copyright 2014 The MITRE Corporation
|
|
||||||
* and the MIT Kerberos and Internet Trust Consortium
|
|
||||||
*
|
|
||||||
* 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.mitre.oauth2.view;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.Writer;
|
|
||||||
import java.text.ParseException;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import javax.swing.text.DateFormatter;
|
|
||||||
|
|
||||||
import org.mitre.oauth2.model.OAuth2AccessTokenEntity;
|
|
||||||
import org.mitre.oauth2.model.OAuth2RefreshTokenEntity;
|
|
||||||
import org.mitre.openid.connect.model.UserInfo;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.web.servlet.view.AbstractView;
|
|
||||||
|
|
||||||
import com.google.common.base.Joiner;
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.GsonBuilder;
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
|
|
||||||
@Component(TokenIntrospectionView.VIEWNAME)
|
|
||||||
public class TokenIntrospectionView extends AbstractView {
|
|
||||||
|
|
||||||
public static final String VIEWNAME = "tokenIntrospection";
|
|
||||||
|
|
||||||
private static Logger logger = LoggerFactory.getLogger(TokenIntrospectionView.class);
|
|
||||||
|
|
||||||
private static DateFormatter isoDateFormatter = new DateFormatter(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"));
|
|
||||||
|
|
||||||
private Gson gson = new GsonBuilder().create();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) {
|
|
||||||
|
|
||||||
response.setContentType("application/json");
|
|
||||||
|
|
||||||
Writer out;
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
out = response.getWriter();
|
|
||||||
UserInfo user = (UserInfo)model.get("user");
|
|
||||||
Object obj = model.get("token");
|
|
||||||
if (obj instanceof OAuth2AccessTokenEntity) {
|
|
||||||
gson.toJson(renderAccessToken((OAuth2AccessTokenEntity)obj, user), out);
|
|
||||||
} else if (obj instanceof OAuth2RefreshTokenEntity) {
|
|
||||||
gson.toJson(renderRefreshToken((OAuth2RefreshTokenEntity)obj, user), out);
|
|
||||||
} else {
|
|
||||||
throw new IOException("Couldn't find a valid entity to render");
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
|
|
||||||
logger.error("IOException occurred in TokenIntrospectionView.java: ", e);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private JsonObject renderAccessToken(OAuth2AccessTokenEntity src, UserInfo user) {
|
|
||||||
JsonObject token = new JsonObject();
|
|
||||||
|
|
||||||
token.addProperty("active", true);
|
|
||||||
|
|
||||||
token.addProperty("scope", Joiner.on(" ").join(src.getScope()));
|
|
||||||
|
|
||||||
if (src.getExpiration() != null) {
|
|
||||||
try {
|
|
||||||
token.addProperty("exp", isoDateFormatter.valueToString(src.getExpiration()));
|
|
||||||
} catch (ParseException e) {
|
|
||||||
logger.error("Problem formatting expiration date: " + src.getExpiration(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user != null) {
|
|
||||||
// if we have a UserInfo, use that for the subject
|
|
||||||
token.addProperty("sub", user.getSub());
|
|
||||||
token.addProperty("user_id", src.getAuthenticationHolder().getAuthentication().getName());
|
|
||||||
} else {
|
|
||||||
// otherwise, use the authentication's username
|
|
||||||
token.addProperty("sub", src.getAuthenticationHolder().getAuthentication().getName());
|
|
||||||
token.addProperty("user_id", src.getAuthenticationHolder().getAuthentication().getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
token.addProperty("client_id", src.getAuthenticationHolder().getAuthentication().getOAuth2Request().getClientId());
|
|
||||||
|
|
||||||
token.addProperty("token_type", src.getTokenType());
|
|
||||||
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
private JsonObject renderRefreshToken(OAuth2RefreshTokenEntity src, UserInfo user) {
|
|
||||||
JsonObject token = new JsonObject();
|
|
||||||
|
|
||||||
token.addProperty("active", true);
|
|
||||||
|
|
||||||
token.addProperty("scope", Joiner.on(" ").join(src.getAuthenticationHolder().getAuthentication().getOAuth2Request().getScope()));
|
|
||||||
|
|
||||||
if (src.getExpiration() != null) {
|
|
||||||
try {
|
|
||||||
token.addProperty("exp", isoDateFormatter.valueToString(src.getExpiration()));
|
|
||||||
} catch (ParseException e) {
|
|
||||||
logger.error("Problem formatting expiration date: " + src.getExpiration(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user != null) {
|
|
||||||
// if we have a UserInfo, use that for the subject
|
|
||||||
token.addProperty("sub", user.getSub());
|
|
||||||
token.addProperty("user_id", src.getAuthenticationHolder().getAuthentication().getName());
|
|
||||||
} else {
|
|
||||||
// otherwise, use the authentication's username
|
|
||||||
token.addProperty("sub", src.getAuthenticationHolder().getAuthentication().getName());
|
|
||||||
token.addProperty("user_id", src.getAuthenticationHolder().getAuthentication().getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
token.addProperty("client_id", src.getAuthenticationHolder().getAuthentication().getOAuth2Request().getClientId());
|
|
||||||
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -16,17 +16,15 @@
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package org.mitre.oauth2.web;
|
package org.mitre.oauth2.web;
|
||||||
|
|
||||||
import java.security.Principal;
|
import com.google.common.base.Strings;
|
||||||
import java.util.Map;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.mitre.oauth2.model.ClientDetailsEntity;
|
import org.mitre.oauth2.model.ClientDetailsEntity;
|
||||||
import org.mitre.oauth2.model.OAuth2AccessTokenEntity;
|
import org.mitre.oauth2.model.OAuth2AccessTokenEntity;
|
||||||
import org.mitre.oauth2.model.OAuth2RefreshTokenEntity;
|
import org.mitre.oauth2.model.OAuth2RefreshTokenEntity;
|
||||||
import org.mitre.oauth2.service.ClientDetailsEntityService;
|
import org.mitre.oauth2.service.ClientDetailsEntityService;
|
||||||
import org.mitre.oauth2.service.IntrospectionAuthorizer;
|
import org.mitre.oauth2.service.IntrospectionAuthorizer;
|
||||||
|
import org.mitre.oauth2.service.IntrospectionResultAssembler;
|
||||||
import org.mitre.oauth2.service.OAuth2TokenEntityService;
|
import org.mitre.oauth2.service.OAuth2TokenEntityService;
|
||||||
import org.mitre.oauth2.view.TokenIntrospectionView;
|
|
||||||
import org.mitre.openid.connect.model.UserInfo;
|
import org.mitre.openid.connect.model.UserInfo;
|
||||||
import org.mitre.openid.connect.service.UserInfoService;
|
import org.mitre.openid.connect.service.UserInfoService;
|
||||||
import org.mitre.openid.connect.view.HttpCodeView;
|
import org.mitre.openid.connect.view.HttpCodeView;
|
||||||
|
@ -42,8 +40,9 @@ import org.springframework.ui.Model;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
|
||||||
import com.google.common.base.Strings;
|
import java.security.Principal;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
public class IntrospectionEndpoint {
|
public class IntrospectionEndpoint {
|
||||||
|
@ -57,6 +56,9 @@ public class IntrospectionEndpoint {
|
||||||
@Autowired
|
@Autowired
|
||||||
private IntrospectionAuthorizer introspectionAuthorizer;
|
private IntrospectionAuthorizer introspectionAuthorizer;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IntrospectionResultAssembler introspectionResultAssembler;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private UserInfoService userInfoService;
|
private UserInfoService userInfoService;
|
||||||
|
|
||||||
|
@ -84,56 +86,54 @@ public class IntrospectionEndpoint {
|
||||||
return JsonEntityView.VIEWNAME;
|
return JsonEntityView.VIEWNAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
// clientID is the principal name in the authentication
|
OAuth2AccessTokenEntity accessToken = null;
|
||||||
String clientId = p.getName();
|
OAuth2RefreshTokenEntity refreshToken = null;
|
||||||
ClientDetailsEntity authClient = clientService.loadClientByClientId(clientId);
|
ClientDetailsEntity tokenClient;
|
||||||
|
Set<String> scopes;
|
||||||
ClientDetailsEntity tokenClient = null;
|
UserInfo user;
|
||||||
Set<String> scopes = null;
|
|
||||||
Object token = null;
|
|
||||||
UserInfo user = null;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// check access tokens first (includes ID tokens)
|
// check access tokens first (includes ID tokens)
|
||||||
OAuth2AccessTokenEntity access = tokenServices.readAccessToken(tokenValue);
|
accessToken = tokenServices.readAccessToken(tokenValue);
|
||||||
|
|
||||||
tokenClient = access.getClient();
|
tokenClient = accessToken.getClient();
|
||||||
scopes = access.getScope();
|
scopes = accessToken.getScope();
|
||||||
|
|
||||||
token = access;
|
user = userInfoService.getByUsernameAndClientId(accessToken.getAuthenticationHolder().getAuthentication().getName(), tokenClient.getClientId());
|
||||||
|
|
||||||
user = userInfoService.getByUsernameAndClientId(access.getAuthenticationHolder().getAuthentication().getName(), tokenClient.getClientId());
|
|
||||||
|
|
||||||
} catch (InvalidTokenException e) {
|
} catch (InvalidTokenException e) {
|
||||||
logger.error("Verify failed; Invalid access token. Checking refresh token.", e);
|
logger.info("Verify failed; Invalid access token. Checking refresh token.");
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// check refresh tokens next
|
// check refresh tokens next
|
||||||
OAuth2RefreshTokenEntity refresh = tokenServices.getRefreshToken(tokenValue);
|
refreshToken = tokenServices.getRefreshToken(tokenValue);
|
||||||
|
|
||||||
tokenClient = refresh.getClient();
|
tokenClient = refreshToken.getClient();
|
||||||
scopes = refresh.getAuthenticationHolder().getAuthentication().getOAuth2Request().getScope();
|
scopes = refreshToken.getAuthenticationHolder().getAuthentication().getOAuth2Request().getScope();
|
||||||
|
|
||||||
user = userInfoService.getByUsernameAndClientId(refresh.getAuthenticationHolder().getAuthentication().getName(), tokenClient.getClientId());
|
user = userInfoService.getByUsernameAndClientId(refreshToken.getAuthenticationHolder().getAuthentication().getName(), tokenClient.getClientId());
|
||||||
|
|
||||||
token = refresh;
|
|
||||||
|
|
||||||
} catch (InvalidTokenException e2) {
|
} catch (InvalidTokenException e2) {
|
||||||
logger.error("Verify failed; Invalid refresh token", e2);
|
logger.error("Verify failed; Invalid access/refresh token", e2);
|
||||||
Map<String,Boolean> entity = ImmutableMap.of("active", Boolean.FALSE);
|
Map<String,Boolean> entity = ImmutableMap.of("active", Boolean.FALSE);
|
||||||
model.addAttribute("entity", entity);
|
model.addAttribute("entity", entity);
|
||||||
return JsonEntityView.VIEWNAME;
|
return JsonEntityView.VIEWNAME;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tokenClient != null && authClient != null) {
|
// clientID is the principal name in the authentication
|
||||||
|
String clientId = p.getName();
|
||||||
|
ClientDetailsEntity authClient = clientService.loadClientByClientId(clientId);
|
||||||
|
|
||||||
if (authClient.isAllowIntrospection()) {
|
if (authClient.isAllowIntrospection()) {
|
||||||
if (introspectionAuthorizer.isIntrospectionPermitted(authClient, tokenClient, scopes)) {
|
if (introspectionAuthorizer.isIntrospectionPermitted(authClient, tokenClient, scopes)) {
|
||||||
// if it's a valid token, we'll print out information on it
|
// if it's a valid token, we'll print out information on it
|
||||||
model.addAttribute("token", token);
|
Map<String, Object> entity = accessToken != null
|
||||||
model.addAttribute("user", user);
|
? introspectionResultAssembler.assembleFrom(accessToken, user)
|
||||||
return TokenIntrospectionView.VIEWNAME;
|
: introspectionResultAssembler.assembleFrom(refreshToken, user);
|
||||||
|
model.addAttribute("entity", entity);
|
||||||
|
return JsonEntityView.VIEWNAME;
|
||||||
} else {
|
} else {
|
||||||
logger.error("Verify failed; client configuration or scope don't permit token introspection");
|
logger.error("Verify failed; client configuration or scope don't permit token introspection");
|
||||||
model.addAttribute("code", HttpStatus.FORBIDDEN);
|
model.addAttribute("code", HttpStatus.FORBIDDEN);
|
||||||
|
@ -144,12 +144,6 @@ public class IntrospectionEndpoint {
|
||||||
model.addAttribute("code", HttpStatus.FORBIDDEN);
|
model.addAttribute("code", HttpStatus.FORBIDDEN);
|
||||||
return HttpCodeView.VIEWNAME;
|
return HttpCodeView.VIEWNAME;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// This is a bad error -- I think it means we have a token outstanding that doesn't map to a client?
|
|
||||||
logger.error("Verify failed; client " + clientId + " not found.");
|
|
||||||
model.addAttribute("code", HttpStatus.NOT_FOUND);
|
|
||||||
return HttpCodeView.VIEWNAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,230 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright 2014 The MITRE Corporation
|
||||||
|
* and the MIT Kerberos and Internet Trust Consortium
|
||||||
|
*
|
||||||
|
* 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.mitre.oauth2.service.impl;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mitre.oauth2.model.OAuth2AccessTokenEntity;
|
||||||
|
import org.mitre.oauth2.model.OAuth2RefreshTokenEntity;
|
||||||
|
import org.mitre.openid.connect.model.UserInfo;
|
||||||
|
import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
||||||
|
import org.springframework.security.oauth2.provider.OAuth2Request;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static com.google.common.collect.Sets.newHashSet;
|
||||||
|
import static org.hamcrest.CoreMatchers.equalTo;
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
|
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
public class TestDefaultIntrospectionResultAssembler {
|
||||||
|
|
||||||
|
private DefaultIntrospectionResultAssembler assembler = new DefaultIntrospectionResultAssembler();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldAssembleExpectedResultForAccessToken() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
OAuth2AccessTokenEntity accessToken = accessToken(new Date(123), scopes("foo", "bar"), "Bearer",
|
||||||
|
authentication("name", request("clientId")));
|
||||||
|
|
||||||
|
UserInfo userInfo = userInfo("sub");
|
||||||
|
|
||||||
|
// when
|
||||||
|
Map<String, Object> result = assembler.assembleFrom(accessToken, userInfo);
|
||||||
|
|
||||||
|
|
||||||
|
// then
|
||||||
|
Map<String, Object> expected = new ImmutableMap.Builder<String, Object>()
|
||||||
|
.put("sub", "sub")
|
||||||
|
.put("exp", new Date(123))
|
||||||
|
.put("scope", "bar foo")
|
||||||
|
.put("active", Boolean.TRUE)
|
||||||
|
.put("user_id", "name")
|
||||||
|
.put("client_id", "clientId")
|
||||||
|
.put("token_type", "Bearer")
|
||||||
|
.build();
|
||||||
|
assertThat(result, is(equalTo(expected)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldAssembleExpectedResultForAccessTokenWithoutUserInfo() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
OAuth2AccessTokenEntity accessToken = accessToken(new Date(123), scopes("foo", "bar"), "Bearer",
|
||||||
|
authentication("name", request("clientId")));
|
||||||
|
|
||||||
|
// when
|
||||||
|
Map<String, Object> result = assembler.assembleFrom(accessToken, null);
|
||||||
|
|
||||||
|
|
||||||
|
// then
|
||||||
|
Map<String, Object> expected = new ImmutableMap.Builder<String, Object>()
|
||||||
|
.put("sub", "name")
|
||||||
|
.put("exp", new Date(123))
|
||||||
|
.put("scope", "bar foo")
|
||||||
|
.put("active", Boolean.TRUE)
|
||||||
|
.put("user_id", "name")
|
||||||
|
.put("client_id", "clientId")
|
||||||
|
.put("token_type", "Bearer")
|
||||||
|
.build();
|
||||||
|
assertThat(result, is(equalTo(expected)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldAssembleExpectedResultForAccessTokenWithoutExpiry() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
OAuth2AccessTokenEntity accessToken = accessToken(null, scopes("foo", "bar"), "Bearer",
|
||||||
|
authentication("name", request("clientId")));
|
||||||
|
|
||||||
|
UserInfo userInfo = userInfo("sub");
|
||||||
|
|
||||||
|
// when
|
||||||
|
Map<String, Object> result = assembler.assembleFrom(accessToken, userInfo);
|
||||||
|
|
||||||
|
|
||||||
|
// then
|
||||||
|
Map<String, Object> expected = new ImmutableMap.Builder<String, Object>()
|
||||||
|
.put("sub", "sub")
|
||||||
|
.put("scope", "bar foo")
|
||||||
|
.put("active", Boolean.TRUE)
|
||||||
|
.put("user_id", "name")
|
||||||
|
.put("client_id", "clientId")
|
||||||
|
.put("token_type", "Bearer")
|
||||||
|
.build();
|
||||||
|
assertThat(result, is(equalTo(expected)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldAssembleExpectedResultForRefreshToken() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
OAuth2RefreshTokenEntity refreshToken = refreshToken(new Date(123),
|
||||||
|
authentication("name", request("clientId", scopes("foo", "bar"))));
|
||||||
|
|
||||||
|
UserInfo userInfo = userInfo("sub");
|
||||||
|
|
||||||
|
// when
|
||||||
|
Map<String, Object> result = assembler.assembleFrom(refreshToken, userInfo);
|
||||||
|
|
||||||
|
|
||||||
|
// then
|
||||||
|
Map<String, Object> expected = new ImmutableMap.Builder<String, Object>()
|
||||||
|
.put("sub", "sub")
|
||||||
|
.put("exp", new Date(123))
|
||||||
|
.put("scope", "bar foo")
|
||||||
|
.put("active", Boolean.TRUE)
|
||||||
|
.put("user_id", "name")
|
||||||
|
.put("client_id", "clientId")
|
||||||
|
.build();
|
||||||
|
assertThat(result, is(equalTo(expected)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldAssembleExpectedResultForRefreshTokenWithoutUserInfo() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
OAuth2RefreshTokenEntity refreshToken = refreshToken(new Date(123),
|
||||||
|
authentication("name", request("clientId", scopes("foo", "bar"))));
|
||||||
|
|
||||||
|
// when
|
||||||
|
Map<String, Object> result = assembler.assembleFrom(refreshToken, null);
|
||||||
|
|
||||||
|
|
||||||
|
// then
|
||||||
|
Map<String, Object> expected = new ImmutableMap.Builder<String, Object>()
|
||||||
|
.put("sub", "name")
|
||||||
|
.put("exp", new Date(123))
|
||||||
|
.put("scope", "bar foo")
|
||||||
|
.put("active", Boolean.TRUE)
|
||||||
|
.put("user_id", "name")
|
||||||
|
.put("client_id", "clientId")
|
||||||
|
.build();
|
||||||
|
assertThat(result, is(equalTo(expected)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldAssembleExpectedResultForRefreshTokenWithoutExpiry() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
OAuth2RefreshTokenEntity refreshToken = refreshToken(null,
|
||||||
|
authentication("name", request("clientId", scopes("foo", "bar"))));
|
||||||
|
|
||||||
|
UserInfo userInfo = userInfo("sub");
|
||||||
|
|
||||||
|
// when
|
||||||
|
Map<String, Object> result = assembler.assembleFrom(refreshToken, userInfo);
|
||||||
|
|
||||||
|
|
||||||
|
// then
|
||||||
|
Map<String, Object> expected = new ImmutableMap.Builder<String, Object>()
|
||||||
|
.put("sub", "sub")
|
||||||
|
.put("scope", "bar foo")
|
||||||
|
.put("active", Boolean.TRUE)
|
||||||
|
.put("user_id", "name")
|
||||||
|
.put("client_id", "clientId")
|
||||||
|
.build();
|
||||||
|
assertThat(result, is(equalTo(expected)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private UserInfo userInfo(String sub) {
|
||||||
|
UserInfo userInfo = mock(UserInfo.class);
|
||||||
|
given(userInfo.getSub()).willReturn(sub);
|
||||||
|
return userInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private OAuth2AccessTokenEntity accessToken(Date exp, Set<String> scopes, String tokenType, OAuth2Authentication authentication) {
|
||||||
|
OAuth2AccessTokenEntity accessToken = mock(OAuth2AccessTokenEntity.class, RETURNS_DEEP_STUBS);
|
||||||
|
given(accessToken.getExpiration()).willReturn(exp);
|
||||||
|
given(accessToken.getScope()).willReturn(scopes);
|
||||||
|
given(accessToken.getTokenType()).willReturn(tokenType);
|
||||||
|
given(accessToken.getAuthenticationHolder().getAuthentication()).willReturn(authentication);
|
||||||
|
return accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
private OAuth2RefreshTokenEntity refreshToken(Date exp, OAuth2Authentication authentication) {
|
||||||
|
OAuth2RefreshTokenEntity refreshToken = mock(OAuth2RefreshTokenEntity.class, RETURNS_DEEP_STUBS);
|
||||||
|
given(refreshToken.getExpiration()).willReturn(exp);
|
||||||
|
given(refreshToken.getAuthenticationHolder().getAuthentication()).willReturn(authentication);
|
||||||
|
return refreshToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
private OAuth2Authentication authentication(String name, OAuth2Request request) {
|
||||||
|
OAuth2Authentication authentication = mock(OAuth2Authentication.class);
|
||||||
|
given(authentication.getName()).willReturn(name);
|
||||||
|
given(authentication.getOAuth2Request()).willReturn(request);
|
||||||
|
return authentication;
|
||||||
|
}
|
||||||
|
|
||||||
|
private OAuth2Request request(String clientId) {
|
||||||
|
return request(clientId, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OAuth2Request request(String clientId, Set<String> scopes) {
|
||||||
|
return new OAuth2Request(null, clientId, null, true, scopes, null, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> scopes(String... scopes) {
|
||||||
|
return newHashSet(scopes);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue