/* * Copyright (c) 2012, Paul Merlin. All Rights Reserved. * * 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.qi4j.library.shiro.web; import java.io.IOException; import javax.servlet.Servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.http.HttpException; import org.apache.http.HttpHost; import org.apache.http.HttpRequest; import org.apache.http.HttpRequestInterceptor; import org.apache.http.HttpResponse; import org.apache.http.auth.AuthScope; import org.apache.http.auth.AuthState; import org.apache.http.auth.Credentials; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.protocol.ClientContext; import org.apache.http.impl.auth.BasicScheme; import org.apache.http.impl.client.BasicResponseHandler; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.protocol.ExecutionContext; import org.apache.http.protocol.HttpContext; import org.apache.http.util.EntityUtils; import org.apache.shiro.authc.credential.DefaultPasswordService; import org.apache.shiro.authc.credential.PasswordMatcher; import org.apache.shiro.authc.credential.PasswordService; import org.apache.shiro.realm.Realm; import org.apache.shiro.realm.SimpleAccountRealm; import org.junit.Test; import org.qi4j.api.mixin.Mixins; import org.qi4j.api.service.ServiceActivation; import org.qi4j.api.service.ServiceComposite; import org.qi4j.bootstrap.AssemblyException; import org.qi4j.bootstrap.ModuleAssembly; import org.qi4j.library.http.JettyConfiguration; import org.qi4j.library.http.JettyServiceAssembler; import org.qi4j.library.shiro.ini.ShiroIniConfiguration; import org.qi4j.library.shiro.web.assembly.HttpShiroAssembler; import org.qi4j.test.AbstractQi4jTest; import org.qi4j.test.EntityTestAssembler; import org.qi4j.test.util.FreePortFinder; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import static org.qi4j.library.http.Servlets.addServlets; import static org.qi4j.library.http.Servlets.serve; public class WebRealmServiceTest extends AbstractQi4jTest { private int port; @Mixins( MyRealmMixin.class ) public interface MyRealmService extends Realm, ServiceComposite, ServiceActivation { } public class MyRealmMixin extends SimpleAccountRealm implements ServiceActivation { private final PasswordService passwordService; public MyRealmMixin() { super(); passwordService = new DefaultPasswordService(); PasswordMatcher matcher = new PasswordMatcher(); matcher.setPasswordService( passwordService ); setCredentialsMatcher( matcher ); } public void activateService() throws Exception { // Create a test account addAccount( "foo", passwordService.encryptPassword( "bar" ) ); } public void passivateService() throws Exception { } } @Mixins( MyServlet.class ) public interface MyServletService extends Servlet, ServiceComposite { } public static class MyServlet extends HttpServlet { @Override protected void doGet( HttpServletRequest req, HttpServletResponse resp ) throws ServletException, IOException { resp.getWriter().println( "FOO" ); } } @Override public void assemble( ModuleAssembly module ) throws AssemblyException { try { new EntityTestAssembler().assemble( module ); ModuleAssembly configModule = module; // START SNIPPET: assembly new JettyServiceAssembler().assemble( module ); // END SNIPPET: assembly port = FreePortFinder.findFreePortOnLoopback(); JettyConfiguration config = module.forMixin( JettyConfiguration.class ).declareDefaults(); config.hostName().set( "127.0.0.1" ); config.port().set( port ); // START SNIPPET: assembly new HttpShiroAssembler().withConfig( configModule ).assemble( module ); module.services( MyRealmService.class ); // END SNIPPET: assembly configModule.forMixin( ShiroIniConfiguration.class ). declareDefaults(). iniResourcePath().set( "classpath:web-shiro.ini" ); addServlets( serve( "/*" ).with( MyServletService.class ) ).to( module ); } catch ( IOException ex ) { throw new AssemblyException( "Unable to find free port to bind to", ex ); } } @Test public void test() throws IOException { DefaultHttpClient client = new DefaultHttpClient(); // Our request method HttpGet get = new HttpGet( "http://127.0.0.1:" + port + "/" ); HttpResponse response = client.execute( get ); int status = response.getStatusLine().getStatusCode(); assertThat( String.valueOf( status ).substring( 0, 1 ) + "xx", is( "4xx" ) ); EntityUtils.consume( response.getEntity() ); // Simple interceptor set as first interceptor in the protocol chain HttpRequestInterceptor preemptiveAuth = new BasicAuthRequestInterceptor(); client.addRequestInterceptor( preemptiveAuth, 0 ); // Set credentials UsernamePasswordCredentials creds = new UsernamePasswordCredentials( "foo", "bar" ); client.getCredentialsProvider().setCredentials( AuthScope.ANY, creds ); response = client.execute( get ); status = response.getStatusLine().getStatusCode(); assertThat( status, is( 200 ) ); String result = new BasicResponseHandler().handleResponse( response ).trim(); assertThat( result, is( "FOO" ) ); } /** * HttpClient Basic Auth Request Interceptor. * Needed for HTTP Basic Authentication. */ public class BasicAuthRequestInterceptor implements HttpRequestInterceptor { public void process( final HttpRequest request, final HttpContext context ) throws HttpException, IOException { AuthState authState = ( AuthState ) context.getAttribute( ClientContext.TARGET_AUTH_STATE ); CredentialsProvider credsProvider = ( CredentialsProvider ) context.getAttribute( ClientContext.CREDS_PROVIDER ); HttpHost targetHost = ( HttpHost ) context.getAttribute( ExecutionContext.HTTP_TARGET_HOST ); // If not auth scheme has been initialized yet if ( authState.getAuthScheme() == null ) { AuthScope authScope = new AuthScope( targetHost.getHostName(), targetHost.getPort() ); // Obtain credentials matching the target host Credentials creds = credsProvider.getCredentials( authScope ); // If found, generate BasicScheme preemptively if ( creds != null ) { authState.setAuthScheme( new BasicScheme() ); authState.setCredentials( creds ); } } } } }