/** * The MIT License (MIT) * * Copyright (c) 2014-2017 Yegor Bugayenko * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package org.takes.facets.auth; import com.jcabi.aspects.Tv; import javax.xml.bind.DatatypeConverter; import org.apache.commons.lang.RandomStringUtils; import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.Test; import org.takes.HttpException; import org.takes.Take; import org.takes.facets.forward.RsForward; import org.takes.facets.forward.TkForward; import org.takes.misc.Opt; import org.takes.rq.RqFake; import org.takes.rq.RqMethod; import org.takes.rq.RqWithHeader; import org.takes.rq.RqWithHeaders; import org.takes.rs.RsPrint; import org.takes.tk.TkText; /** * Test of {@link PsBasic}. * @author Endrigo Antonini (teamed@endrigo.com.br) * @version $Id: 8c0bdcb322adc416167c5091ad298d14e0b7a0dc $ * @since 0.20 * @checkstyle ClassDataAbstractionCouplingCheck (500 lines) */ public final class PsBasicTest { /** * Basic Auth. */ private static final String AUTH_BASIC = "Authorization: Basic %s"; /** * Valid code parameter. */ private static final String VALID_CODE = "?valid_code=%s"; /** * PsBasic can handle connection with valid credential. * @throws Exception if any error occurs */ @Test public void handleConnectionWithValidCredential() throws Exception { final String user = "john"; final Opt<Identity> identity = new PsBasic( "RealmA", new PsBasic.Fake(true) ).enter( new RqWithHeaders( new RqFake( RqMethod.GET, String.format( PsBasicTest.VALID_CODE, RandomStringUtils.randomAlphanumeric(Tv.TEN) ) ), PsBasicTest.header(user, "pass") ) ); MatcherAssert.assertThat(identity.has(), Matchers.is(true)); MatcherAssert.assertThat( identity.get().urn(), CoreMatchers.equalTo(PsBasicTest.urn(user)) ); } /** * PsBasic can handle connection with valid credential when Entry is * a instance of Default. * @throws Exception if any error occurs */ @Test public void handleConnectionWithValidCredentialDefaultEntry() throws Exception { final String user = "johny"; final String password = "password2"; final Opt<Identity> identity = new PsBasic( "RealmAA", new PsBasic.Default( "mike my%20password1 urn:basic:michael", String.format("%s %s urn:basic:%s", user, password, user) ) ).enter( new RqWithHeaders( new RqFake( RqMethod.GET, String.format( PsBasicTest.VALID_CODE, RandomStringUtils.randomAlphanumeric(Tv.TEN) ) ), PsBasicTest.header(user, password) ) ); MatcherAssert.assertThat(identity.has(), Matchers.is(true)); MatcherAssert.assertThat( identity.get().urn(), CoreMatchers.equalTo(PsBasicTest.urn(user)) ); } /** * PsBasic can handle connection with invalid credential. * @throws Exception If some problem inside */ @Test public void handleConnectionWithInvalidCredential() throws Exception { RsForward forward = new RsForward(); try { new PsBasic( "RealmB", new PsBasic.Empty() ).enter( new RqWithHeaders( new RqFake( RqMethod.GET, String.format( "?invalid_code=%s", RandomStringUtils.randomAlphanumeric(Tv.TEN) ) ), PsBasicTest.header("username", "wrong") ) ); } catch (final RsForward ex) { forward = ex; } MatcherAssert.assertThat( new RsPrint(forward).printHead(), Matchers.allOf( Matchers.containsString("HTTP/1.1 401 Unauthorized"), Matchers.containsString( "WWW-Authenticate: Basic ream=\"RealmB\"" ) ) ); } /** * PsBasic can handle multiple headers with valid credential. * @throws Exception If some problem inside */ @Test public void handleMultipleHeadersWithValidCredential() throws Exception { final String user = "bill"; final Opt<Identity> identity = new PsBasic( "RealmC", new PsBasic.Fake(true) ).enter( new RqWithHeaders( new RqFake( RqMethod.GET, String.format( "?multiple_code=%s", RandomStringUtils.randomAlphanumeric(Tv.TEN) ) ), PsBasicTest.header(user, "changeit"), "Referer: http://teamed.io/", "Connection:keep-alive", "Content-Encoding:gzip", "X-Check-Cacheable:YES", "X-Powered-By:Java/1.7" ) ); MatcherAssert.assertThat(identity.has(), Matchers.is(true)); MatcherAssert.assertThat( identity.get().urn(), CoreMatchers.equalTo(PsBasicTest.urn(user)) ); } /** * PsBasic can handle multiple headers with invalid content. * @throws Exception If some problem inside */ @Test(expected = HttpException.class) public void handleMultipleHeadersWithInvalidContent() throws Exception { MatcherAssert.assertThat( new PsBasic( "RealmD", new PsBasic.Fake(true) ).enter( new RqWithHeaders( new RqFake( "XPTO", "/wrong-url" ), String.format( "XYZ%s", PsBasicTest.header("user", "password") ), "XYZReferer: http://teamed.io/", "XYZConnection:keep-alive", "XYZContent-Encoding:gzip", "XYZX-Check-Cacheable:YES", "XYZX-Powered-By:Java/1.7" ) ).has(), Matchers.is(false) ); } /** * PsBasic can authenticate a user. * @throws Exception If some problem inside */ @Test public void authenticatesUser() throws Exception { final Take take = new TkAuth( new TkSecure( new TkText("secured") ), new PsBasic( "myrealm", new PsBasic.Default("mike secret11 urn:users:michael") ) ); MatcherAssert.assertThat( new RsPrint( take.act( new RqWithHeader( new RqFake(), PsBasicTest.header("mike", "secret11") ) ) ).print(), Matchers.containsString("HTTP/1.1 200 OK") ); } /** * PsBasic can request authentication. * @throws Exception If some problem inside */ @Test public void requestAuthentication() throws Exception { final Take take = new TkForward( new TkAuth( new TkSecure( new TkText("secured area...") ), new PsBasic( "the realm 5", new PsBasic.Default("bob pwd88 urn:users:bob") ) ) ); MatcherAssert.assertThat( new RsPrint( take.act(new RqFake()) ).print(), Matchers.containsString("HTTP/1.1 401 Unauthorized\r\n") ); } /** * Generate the identity urn. * @param user User * @return URN */ private static String urn(final String user) { return String.format("urn:basic:%s", user); } /** * Generate the string used on the request that store information about * authentication. * @param user Username * @param pass Password * @return Header string. */ private static String header(final String user, final String pass) { final String auth = String.format("%s:%s", user, pass); final String encoded = DatatypeConverter.printBase64Binary( auth.getBytes() ); return String.format(PsBasicTest.AUTH_BASIC, encoded); } }