Make introspection endpoint access authorization pluggable.

pull/598/head
Alexander Imfeld 11 years ago
parent 4b697ba909
commit 7cd36b471f

@ -0,0 +1,36 @@
/*******************************************************************************
* 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 java.util.Set;
import org.springframework.security.oauth2.provider.ClientDetails;
/**
* Strategy interface used for authorizing token introspection.
*/
public interface IntrospectionAuthorizer {
/**
* @param authClient the authenticated client wanting to perform token introspection
* @param tokenClient the client the token was issued to
* @param tokenScope the scope associated with the token
* @return {@code true} in case introspection is permitted; {@code false} otherwise
*/
boolean isIntrospectionPermitted(ClientDetails authClient, ClientDetails tokenClient, Set<String> tokenScope);
}

@ -0,0 +1,43 @@
/*******************************************************************************
* 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 java.util.Set;
import org.mitre.oauth2.service.IntrospectionAuthorizer;
import org.mitre.oauth2.service.SystemScopeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.stereotype.Service;
@Service
public class DefaultIntrospectionAuthorizer implements IntrospectionAuthorizer {
@Autowired
private SystemScopeService scopeService;
@Override
public boolean isIntrospectionPermitted(ClientDetails authClient,
ClientDetails tokenClient, Set<String> tokenScope) {
// permit introspection if it's the same client that the token was
// issued to, or it at least has all the scopes the token was issued
// with
return authClient.getClientId().equals(tokenClient.getClientId())
|| scopeService.scopesMatch(authClient.getScope(), tokenScope);
}
}

@ -24,8 +24,8 @@ 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.OAuth2TokenEntityService; import org.mitre.oauth2.service.OAuth2TokenEntityService;
import org.mitre.oauth2.service.SystemScopeService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -50,7 +50,7 @@ public class IntrospectionEndpoint {
private ClientDetailsEntityService clientService; private ClientDetailsEntityService clientService;
@Autowired @Autowired
private SystemScopeService scopeService; private IntrospectionAuthorizer introspectionAuthorizer;
private static Logger logger = LoggerFactory.getLogger(IntrospectionEndpoint.class); private static Logger logger = LoggerFactory.getLogger(IntrospectionEndpoint.class);
@ -117,14 +117,12 @@ public class IntrospectionEndpoint {
if (tokenClient != null && authClient != null) { if (tokenClient != null && authClient != null) {
if (authClient.isAllowIntrospection()) { if (authClient.isAllowIntrospection()) {
if (introspectionAuthorizer.isIntrospectionPermitted(authClient, tokenClient, scopes)) {
// if it's the same client that the token was issued to, or it at least has all the scopes the token was issued with
if (authClient.getClientId().equals(tokenClient.getClientId()) || scopeService.scopesMatch(authClient.getScope(), 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("entity", token); model.addAttribute("entity", token);
return "tokenIntrospection"; return "tokenIntrospection";
} else { } else {
logger.error("Verify failed; client tried to introspect a token of an incorrect scope"); logger.error("Verify failed; client configuration or scope don't permit token introspection");
model.addAttribute("code", HttpStatus.FORBIDDEN); model.addAttribute("code", HttpStatus.FORBIDDEN);
return "httpCodeView"; return "httpCodeView";
} }

@ -0,0 +1,107 @@
/*
* Copyright (C) 2014 by Netcetera AG.
* All rights reserved.
*
* The copyright to the computer program(s) herein is the property of Netcetera AG, Switzerland.
* The program(s) may be used and/or copied only with the written permission of Netcetera AG or
* in accordance with the terms and conditions stipulated in the agreement/contract under which
* the program(s) have been supplied.
*/
package org.mitre.oauth2.service.impl;
import java.util.Set;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mitre.oauth2.service.SystemScopeService;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.security.oauth2.provider.ClientDetails;
import static com.google.common.collect.Sets.newHashSet;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
@RunWith(MockitoJUnitRunner.class)
public class TestDefaultIntrospectionAuthorizer {
@InjectMocks
private DefaultIntrospectionAuthorizer introspectionPermitter;
@Mock
private SystemScopeService scopeService;
@Test
public void shouldPermitIntrospectionToSameClientTheTokenWasIssuedTo() {
// given
String sameClient = "same";
// when
boolean permitted = introspectionPermitter.isIntrospectionPermitted(
clientWithId(sameClient), clientWithId(sameClient),
scope("scope"));
// then
assertThat(permitted, is(true));
}
@Test
public void shouldPermitIntrospectionToDifferentClientIfScopesMatch() {
// given
String authClient = "auth";
String tokenClient = "token";
Set<String> authScope = scope("scope1", "scope2", "scope3");
Set<String> tokenScope = scope("scope1", "scope2");
given(scopeService.scopesMatch(authScope, tokenScope)).willReturn(true);
// when
boolean permitted = introspectionPermitter.isIntrospectionPermitted(
clientWithIdAndScope(authClient, authScope),
clientWithId(tokenClient), tokenScope);
// then
assertThat(permitted, is(true));
}
@Test
public void shouldNotPermitIntrospectionToDifferentClientIfScopesDontMatch() {
// given
String authClient = "auth";
String tokenClient = "token";
Set<String> authScope = scope("scope1", "scope2");
Set<String> tokenScope = scope("scope1", "scope2", "scope3");
given(scopeService.scopesMatch(authScope, tokenScope))
.willReturn(false);
// when
boolean permitted = introspectionPermitter.isIntrospectionPermitted(
clientWithIdAndScope(authClient, authScope),
clientWithId(tokenClient), tokenScope);
// then
assertThat(permitted, is(false));
}
private ClientDetails clientWithId(String clientId) {
ClientDetails client = mock(ClientDetails.class);
given(client.getClientId()).willReturn(clientId);
return client;
}
private ClientDetails clientWithIdAndScope(String clientId,
Set<String> scope) {
ClientDetails client = clientWithId(clientId);
given(client.getScope()).willReturn(scope);
return client;
}
private Set<String> scope(String... scopeItems) {
return newHashSet(scopeItems);
}
}
Loading…
Cancel
Save