BasicAuthTest.java

/***************************************************************************
   Copyright 2015 Emily Estes

   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 net.metanotion.web.concrete;


import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import net.metanotion.authident.LogoutEvent;
import net.metanotion.authident.SimpleRealm;
import net.metanotion.authident.UserToken;
import net.metanotion.util.Base64;
import net.metanotion.util.Dispatcher;
import net.metanotion.util.Message;
import net.metanotion.util.SecureString;
import net.metanotion.util.StateMachine;
import net.metanotion.util.Unknown;
import net.metanotion.web.HttpMethod;
import net.metanotion.web.HttpValues;
import net.metanotion.web.RequestObject;

public final class BasicAuthTest {
	public static void assertTrue(final boolean result) {
		if(!result) { throw new AssertionError("Expected true"); }
	}

	private static final class FakeMessage  implements Message<Session> {
		public final String name;
		public FakeMessage(String name) { this.name = name; }
		@Override public Object call(final Session o) { return name; }
		@Override public Class<Session> receiverType() { return Session.class; }
	}

	private static final class FakeDispatcher implements Dispatcher<Session,RequestObject> {
		public final String name;
		public FakeDispatcher(String name) { this.name = name; }
		@Override public Message<Session> dispatch(final RequestObject data) {
			return new FakeMessage(name + data.getResource());
		}
	}

	private static final class Session implements Unknown, StateMachine<Session> {
		private final BasicAuth ba;
		public UserToken ut = null;
		public Object expectedNextState;
		public Session(final BasicAuth ba) {
			this.ba = ba;
		}
		@Override public <I> I lookupInterface(final Class<I> iface) {
			System.out.println("SERVICE: " + iface);
			if(BasicAuth.class.equals(iface)) { return (I) ba; }
			if(StateMachine.class.equals(iface)) { return (I) this; }
			if(Session.class.equals(iface)) { return (I) this; }
			if(UserToken.class.equals(iface)) { return (I) ut; }
			throw new RuntimeException("Can't find service for " + iface.toString());
		}
		@Override public Session state() { return this; }
		@Override public void nextState(final Object event) {
			System.out.println("NEXT: " + event);
			if(event instanceof UserToken) { this.ut = (UserToken) event; }
			if(event instanceof LogoutEvent) { this.ut = null; }
			assertTrue(expectedNextState.equals(event));
		}
	}

	public static final String HTTP_REALM = "test";

	public static void main(final String[] args) throws Exception {
		final SimpleRealm realm = new SimpleRealm();
		final UserToken ut = realm.createAuthentication("user1", new SecureString("asdf"));

		final FakeDispatcher disp = new FakeDispatcher("A");

		final Dispatcher<Unknown,RequestObject> baAuthReqDisp = BasicAuth.dispatcher(disp, true);

		final BasicAuth ba = new BasicAuth(realm, HTTP_REALM);
		final Session s = new Session(ba);

		final Message<Unknown> m1 = baAuthReqDisp.dispatch(new BasicRequestObject("/index.html"));
		final HttpValues hv = (HttpValues) m1.call(s);
		assertTrue(hv.getHttpStatus()==401);
		final Iterator<Map.Entry<String,Object>> head = hv.listHeaders();
		while(head.hasNext()) {
			final Map.Entry<String,Object> h = head.next();
			System.out.println(h.getKey() + " - " + h.getValue().toString());
			assertTrue("WWW-Authenticate".equals(h.getKey()));
			assertTrue("Basic realm=\"test\"".equals(h.getValue().toString()));
		}
		System.out.println(hv.unwrap());

		s.expectedNextState = ut;
		final HashMap<String,String> headers = new HashMap<>();
		headers.put("Authorization", "Basic " + Base64.encode("user1:asdf".getBytes(StandardCharsets.UTF_8)));
		final BasicRequestObject authReq =
			new BasicRequestObject(HttpMethod.GET, "example.com", "/index.html", 	null, headers, null);
		final Message<Unknown> m2 = baAuthReqDisp.dispatch(authReq);
		final String result2 = (String) m2.call(s);
		assertTrue("A/index.html".equals(result2));

		headers.put("Authorization", "Basic " + Base64.encode("user1:12345".getBytes(StandardCharsets.UTF_8)));
		final Message<Unknown> m3 = baAuthReqDisp.dispatch(authReq);
		final HttpValues hv3 = (HttpValues) m3.call(s);
		assertTrue(hv3.getHttpStatus()==401);
		final Iterator<Map.Entry<String,Object>> head3 = hv3.listHeaders();
		while(head3.hasNext()) {
			final Map.Entry<String,Object> h = head3.next();
			System.out.println(h.getKey() + " - " + h.getValue().toString());
			assertTrue("WWW-Authenticate".equals(h.getKey()));
			assertTrue("Basic realm=\"test\"".equals(h.getValue().toString()));
		}
		System.out.println(hv3.unwrap());

		// TO DO test cases
		headers.put("Authorization", "Basic " + Base64.encode("user:".getBytes(StandardCharsets.UTF_8)));
		headers.put("Authorization", "Basic " + Base64.encode("use".getBytes(StandardCharsets.UTF_8)));
		headers.put("Authorization", "Basic _!(&*$$+)");
		headers.put("Authorization", "Basic ");
		headers.put("Authorization", "Basic");
		headers.put("Authorization", "Bas ");
		headers.put("Authorization", "awef");

		final Dispatcher<Unknown,RequestObject> baDisp = BasicAuth.dispatcher(disp, false);
		// repeat all those test cases.
	}
}