/*
* JBoss, Home of Professional Open Source.
* Copyright 2016 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.wildfly.security.auth.realm.token;
import mockit.Mock;
import mockit.MockUp;
import mockit.integration.junit4.JMockit;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.wildfly.security.auth.realm.token.validator.OAuth2IntrospectValidator;
import org.wildfly.security.auth.server.RealmIdentity;
import org.wildfly.security.auth.server.RealmUnavailableException;
import org.wildfly.security.authz.Attributes;
import org.wildfly.security.authz.AuthorizationIdentity;
import org.wildfly.security.evidence.BearerTokenEvidence;
import javax.json.Json;
import javax.json.JsonArrayBuilder;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.Principal;
import java.util.Arrays;
import java.util.function.Function;
import static org.junit.Assert.*;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
@RunWith(JMockit.class)
public class OAuth2TokenSecurityRealmTest {
@Test
public void testBasicActiveToken() throws Exception {
configureReplayTokenIntrospection();
TokenSecurityRealm securityRealm = TokenSecurityRealm.builder()
.validator(OAuth2IntrospectValidator.builder()
.clientId("wildfly-elytron")
.clientSecret("dont_tell_me")
.tokenIntrospectionUrl(new URL("http://as.test.org/oauth2/token/introspect")).build())
.build();
JsonObjectBuilder tokenBuilder = Json.createObjectBuilder();
tokenBuilder.add("active", true);
tokenBuilder.add("username", "elytron@jboss.org");
RealmIdentity realmIdentity = securityRealm.getRealmIdentity(new BearerTokenEvidence(tokenBuilder.build().toString()));
assertTrue(realmIdentity.exists());
Principal realmIdentityPrincipal = realmIdentity.getRealmIdentityPrincipal();
assertEquals("elytron@jboss.org", realmIdentityPrincipal.getName());
}
@Test
public void testUserDefinedPrincipalClaimName() throws Exception {
configureReplayTokenIntrospection();
TokenSecurityRealm securityRealm = TokenSecurityRealm.builder()
.principalClaimName("user-defined-claim")
.validator(OAuth2IntrospectValidator.builder()
.clientId("wildfly-elytron")
.clientSecret("dont_tell_me")
.tokenIntrospectionUrl(new URL("http://as.test.org/oauth2/token/introspect")).build())
.build();
JsonObjectBuilder tokenBuilder = Json.createObjectBuilder();
tokenBuilder.add("active", true);
tokenBuilder.add("user-defined-claim", "elytron@jboss.org");
RealmIdentity realmIdentity = securityRealm.getRealmIdentity(new BearerTokenEvidence(tokenBuilder.build().toString()));
assertTrue(realmIdentity.exists());
Principal realmIdentityPrincipal = realmIdentity.getRealmIdentityPrincipal();
assertEquals("elytron@jboss.org", realmIdentityPrincipal.getName());
}
@Test
public void testNotActiveToken() throws Exception {
configureReplayTokenIntrospection();
TokenSecurityRealm securityRealm = TokenSecurityRealm.builder()
.validator(OAuth2IntrospectValidator.builder()
.clientId("wildfly-elytron")
.clientSecret("dont_tell_me")
.tokenIntrospectionUrl(new URL("http://as.test.org/oauth2/token/introspect")).build())
.build();
JsonObjectBuilder tokenBuilder = Json.createObjectBuilder();
tokenBuilder.add("active", false);
RealmIdentity realmIdentity = securityRealm.getRealmIdentity(new BearerTokenEvidence(tokenBuilder.build().toString()));
assertFalse(realmIdentity.exists());
assertNull(realmIdentity.getRealmIdentityPrincipal());
}
@Test
public void testAttributesFromTokenMetadata() throws Exception {
configureReplayTokenIntrospection();
TokenSecurityRealm securityRealm = TokenSecurityRealm.builder()
.validator(OAuth2IntrospectValidator.builder()
.clientId("wildfly-elytron")
.clientSecret("dont_tell_me")
.tokenIntrospectionUrl(new URL("http://as.test.org/oauth2/token/introspect")).build())
.build();
JsonObjectBuilder tokenBuilder = Json.createObjectBuilder();
tokenBuilder.add("active", true);
tokenBuilder.add("username", "elytron@jboss.org");
tokenBuilder.add("attribute1", "value1");
tokenBuilder.add("attribute2", "value2");
tokenBuilder.add("attribute3", true);
tokenBuilder.add("attribute4", false);
tokenBuilder.add("attribute5", 10);
JsonArrayBuilder jsonArray = Json.createArrayBuilder();
jsonArray.add(1).add(2).add(3).add(4);
tokenBuilder.add("attribute6", jsonArray.build());
tokenBuilder.add("attribute7", Json.createObjectBuilder().add("objField1", "value1").add("objectField2", "value2"));
RealmIdentity realmIdentity = securityRealm.getRealmIdentity(new BearerTokenEvidence(tokenBuilder.build().toString()));
AuthorizationIdentity authorizationIdentity = realmIdentity.getAuthorizationIdentity();
Attributes attributes = authorizationIdentity.getAttributes();
assertEquals("value1", attributes.getFirst("attribute1"));
assertEquals("value2", attributes.getFirst("attribute2"));
assertEquals("true", attributes.getFirst("attribute3"));
assertEquals("false", attributes.getFirst("attribute4"));
assertEquals("10", attributes.getFirst("attribute5"));
Attributes.Entry attribute6 = attributes.get("attribute6");
assertEquals(4, attribute6.size());
assertTrue(attribute6.containsAll(Arrays.asList("1","2","3","4")));
assertEquals("{\"objField1\":\"value1\",\"objectField2\":\"value2\"}", attributes.getFirst("attribute7"));
}
@Test(expected = RealmUnavailableException.class)
public void testInErrorTokenIntrospectionEndpoint() throws Exception {
configureTokenIntrospectionEndpoint(s -> {throw new RuntimeException("Forcing exception.");});
TokenSecurityRealm securityRealm = TokenSecurityRealm.builder()
.validator(OAuth2IntrospectValidator.builder()
.clientId("wildfly-elytron")
.clientSecret("dont_tell_me")
.tokenIntrospectionUrl(new URL("http://as.test.org/oauth2/token/introspect")).build())
.build();
JsonObjectBuilder tokenBuilder = Json.createObjectBuilder();
tokenBuilder.add("active", true);
RealmIdentity realmIdentity = securityRealm.getRealmIdentity(new BearerTokenEvidence(tokenBuilder.build().toString()));
assertFalse(realmIdentity.exists());
}
@Test(expected = IllegalArgumentException.class)
public void failMissingSSLContext() throws Exception {
TokenSecurityRealm.builder()
.validator(OAuth2IntrospectValidator.builder()
.clientId("wildfly-elytron")
.clientSecret("dont_tell_me")
.tokenIntrospectionUrl(new URL("https://as.test.org/oauth2/token/introspect")).build())
.build();
}
private void configureReplayTokenIntrospection() {
configureTokenIntrospectionEndpoint(s -> Json.createReader(new ByteArrayInputStream(s.getBytes(StandardCharsets.UTF_8))).readObject());
}
private void configureTokenIntrospectionEndpoint(Function<String, JsonObject> introspector){
final Class<?> classToMock;
try {
classToMock = Class.forName("org.wildfly.security.auth.realm.token.validator.OAuth2IntrospectValidator", true, TokenSecurityRealm.class.getClassLoader());
} catch (ClassNotFoundException e) {
throw new NoClassDefFoundError(e.getMessage());
}
new MockUp<Object>(classToMock){
@Mock
public JsonObject introspectAccessToken(URL tokenIntrospectionUrl, String clientId, String clientSecret, String token, SSLContext sslContext, HostnameVerifier hostnameVerifier) throws IOException {
return introspector.apply(token);
}
};
}
}