FormsAuthTest.java

/***************************************************************************
   Copyright 2014 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.formsauth;


import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import net.metanotion.sqlauthident.SQLRealm;
import net.metanotion.authident.UserToken;
import net.metanotion.io.Serializer;
import net.metanotion.scripting.ObjectServer;
import net.metanotion.scripting.Scriptable;
import net.metanotion.scripting.ScriptingObjectMagic;
import net.metanotion.simpletemplate.Resource;
import net.metanotion.simpletemplate.TemplateResources;
import net.metanotion.sql.DbUtil;
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.util.Wrap;
import net.metanotion.web.HttpMethod;
import net.metanotion.web.HttpValues;
import net.metanotion.web.concrete.BasicRequestObject;
import net.metanotion.web.servlets.ServerUtil;

public final class FormsAuthTest {
	private static final Logger logger = LoggerFactory.getLogger(FormsAuthTest.class);

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

	public static final int ARGS_URL = 0;
	public static final int ARGS_DBNAME = 1;
	public static final int ARGS_USER = 2;
	public static final int ARGS_PASS = 3;

	private static String write(Object result) throws IOException {
		final ByteArrayOutputStream baos = new ByteArrayOutputStream();
		Serializer.write(result, baos, StandardCharsets.UTF_8);
		return new String(baos.toByteArray(), StandardCharsets.UTF_8);
	}

	public static final class TestSession implements Unknown, StateMachine<TestSession> {
		private static final ScriptingObjectMagic som = new ScriptingObjectMagic(TestSession.class);
		public Object lastEvent = null;
		public UserToken token = null;
		@Scriptable("AuthUI") public final Unknown authUI;
		private final ObjectServer os = som.instance(this);
		private final FormsAuthAPI ui;
		public TestSession(FormsAuth formsUI, FormsAuthAPI ui) {
			this.token = token;
			this.ui = ui;
			this.authUI = formsUI.newSession(this);
		}
		@Override public void nextState(Object event) {
			this.lastEvent = event;
			logger.debug("next state {}", event);
		}
		@Override public TestSession state() { return this; }
		@Override public <I> I lookupInterface(final Class<I> theInterface) {
			logger.debug("Lookup: {}", theInterface);
			if(theInterface == Unknown.class) { return (I) this; }
			if(theInterface == ObjectServer.class) { return (I) this.os; }
			if(theInterface == SimpleAuthAPI.class) { return (I) this.ui; }
			if(theInterface == AuthAPI.class) { return (I) this.ui; }
			if(theInterface == FormsAuthAPI.class) { return (I) this.ui; }
			if(theInterface == UserToken.class) { return (I) this.token; }
			if(theInterface == StateMachine.class) { return (I) this; }
			return null;
		}
	}

	public static void main(String[] args) throws Exception {
		final String url = args[ARGS_URL];
		final String dbName = args[ARGS_DBNAME];
		final String user = args[ARGS_USER];
		final String pass = args[ARGS_PASS];
		try (final Connection conn = DriverManager.getConnection(url, user, pass)) {
			DbUtil.createDatabase(conn, dbName, user, true);
		}

		final DataSource ds = DbUtil.startDBConnectionPool(url + dbName, user, pass);

		try (final Connection conn = ds.getConnection()) {
			DbUtil.runSchema(conn, SQLRealm.schemaFactory());
		}

		final SQLRealm realm = new SQLRealm(ds);
		final UserToken noName = realm.createUser();

		final AuthMailer.Urls urls = FormsAuth.standardUrls("/test/");
		final AuthImplTest.AuthMailerCache cache = new AuthImplTest.AuthMailerCache();
		final AuthAPI formsAuth = AuthFactory.instance(ds, realm, cache, "/auth/");

		logger.debug("Create HtmlFormsUI");
		final FormsAuth ui = new FormsAuth("/account/", formsAuth);
		final FormsAuthAPI auth = ui.instance();
		final TestSession session = new TestSession(ui, auth);

		auth.api();
		auth.login(session, "test", new SecureString("asdf"), null);
		auth.logout(session);
		auth.requestPasswordReset("");
		assertTrue(cache.emails.size() == 0);
		auth.requestPasswordReset("test");
		assertTrue(cache.emails.size() == 0);
		auth.resetPassword(1, "a", new SecureString(""), new SecureString(""));
		auth.resetPassword(1, "a", new SecureString("a"), new SecureString("b"));
		auth.resetPassword(1, "a", new SecureString("asdf"), new SecureString("asdf"));
		auth.sendAccountValidation("test");
		assertTrue(cache.emails.size() == 0);

		auth.createAccount(session, "test", new SecureString("asdf"), new SecureString("asdf"), null);
		assertTrue(cache.emails.size() == 1);
		auth.addAccount((UserToken) session.lastEvent, "a2", new SecureString("asdf"), null);
		assertTrue(cache.emails.size() == 2);
		auth.addAccount((UserToken) session.lastEvent, "", new SecureString("asdf"), null);
		auth.addAccount((UserToken) session.lastEvent, "a3", new SecureString("as"), null);
		auth.addAccount((UserToken) session.lastEvent, "a2", new SecureString("asdf"), null);

		auth.removeAccount((UserToken) session.lastEvent, "a2", new SecureString("asdf1"));
		auth.removeAccount((UserToken) session.lastEvent, "a2", new SecureString("asdf"));
		auth.sendAccountValidation("test");
		assertTrue(cache.emails.size() == 3);
		auth.sendAccountValidation("");
		assertTrue(cache.emails.size() == 3);
		auth.changePassword((UserToken) session.lastEvent, new SecureString(""),
			new SecureString("asdf"), new SecureString("asdf"));
		auth.changePassword((UserToken) session.lastEvent, new SecureString("awef"),
			new SecureString("asdf"), new SecureString("as"));
		auth.changePassword((UserToken) session.lastEvent, new SecureString("awef"),
			new SecureString("asd"), new SecureString("asdf"));
		auth.changePassword((UserToken) session.lastEvent, new SecureString("asdf"),
			new SecureString("awef"), new SecureString("awef"));
		auth.changePassword(noName, new SecureString("awef"),
			new SecureString("asdf"), new SecureString("asdf"));
		auth.validateAccount(1, "a");

		final AuthImplTest.Email v = cache.emails.get(cache.emails.size() - 1);
		auth.validateAccount(Long.valueOf(v.vid), URLDecoder.decode(v.token, "UTF-8"));
		final AccountInfo info = (AccountInfo) auth.whoAmI((UserToken) session.lastEvent);
		assertTrue("test".equals(info.username));
		assertTrue(info.accounts.size() == 1);
		//assertTrue(422 == ((HttpValues) auth.whoAmI(null)).getHttpStatus());
		//assertTrue(422 == ((HttpValues) auth.whoAmI(noName)).getHttpStatus());

		auth.logout(session);
		auth.login(session, "test", new SecureString("awef"), null);

		auth.requestPasswordReset("");
		assertTrue(cache.emails.size() == 3);
		auth.requestPasswordReset("test");
		assertTrue(cache.emails.size() == 4);
		final AuthImplTest.Email reset = cache.emails.get(cache.emails.size() - 1);
		auth.resetPassword(Long.valueOf(reset.rid), URLDecoder.decode(reset.token, "UTF-8"),
			new SecureString("qwerty"), new SecureString("qwerty"));

		auth.createAccount(session, "", new SecureString("asdf"), new SecureString("asdf"), null);
		auth.createAccount(session, "bad", new SecureString(""), new SecureString("asdf"), null);
		auth.createAccount(session, "bad", new SecureString("asd"), new SecureString("as"), null);
		auth.createAccount(session, "test", new SecureString("asdf"), new SecureString("asdf"), null);

		auth.validateAccountPage(1, "a");
		auth.createAccount(session, "test2", new SecureString("asdf"), new SecureString("asdf"), null);
		final AuthImplTest.Email validate = cache.emails.get(cache.emails.size() - 1);
		auth.validateAccountPage(Long.valueOf(validate.vid), URLDecoder.decode(validate.token, "UTF-8"));




		logger.debug("look up the dispatcher");
		logger.debug("{}", session.lookupInterface(ObjectServer.class).dispatcher("AuthUI"));
		logger.debug("{}", (Unknown) session.lookupInterface(ObjectServer.class).get("AuthUI"));
		Resource r = TemplateResources.getStringAsResource("Test Web Page: <? AuthUI.loginForm ?>", "text/html; charset=UTF-8");

		logger.debug(write(((Wrap)r.skin(session.lookupInterface(ObjectServer.class), new BasicRequestObject("/thePage.html"))).unwrap()));
//		r = TemplateResources.getStringAsResource("Test Web Page: <? AuthUI.accountPanel ?>", "text/html; charset=UTF-8");
//		logger.debug(write(((Wrap)r.skin(os, new BasicRequestObject())).unwrap()));

		Dispatcher webApi = FormsAuth.dispatcher();
		HashMap<String,Object> vars = new HashMap<>();
		vars.put("username", "qwerty");
		vars.put("password", "asdf");
		vars.put("currentPage", "/index.html");
		Message m = webApi.dispatch(new BasicRequestObject(HttpMethod.POST, "login", vars));
		logger.debug("{}", m.receiverType());
		HttpValues hv = (HttpValues) m.call(session);
		Iterator<Map.Entry<String,Object>> i = hv.listHeaders();
		while(i.hasNext()) {
			Map.Entry<String, Object> h = i.next();
			logger.debug("Header {} - {}", h.getKey(), h.getValue());
		}

		vars.clear();
		m = webApi.dispatch(new BasicRequestObject(HttpMethod.GET, "loginFailedPage.html", vars));
		logger.debug("Receiver: {}", m.receiverType());
		logger.debug(write(((Wrap) m.call(session.lookupInterface(m.receiverType()))).unwrap()));

		r = TemplateResources.getStringAsResource("Test Web Page: <? AuthUI.accountPanel ?>", "text/html; charset=UTF-8");
		logger.debug(write(((Wrap)r.skin(session.lookupInterface(ObjectServer.class), new BasicRequestObject("/aPage.html"))).unwrap()));

		r = TemplateResources.getStringAsResource("Test Web Page: <? AuthUI.accountList ?>", "text/html; charset=UTF-8");
		session.token = realm.createAuthentication("a2", new SecureString("asdf"));
		logger.debug(write(((Wrap)r.skin(session.lookupInterface(ObjectServer.class), new BasicRequestObject("/aPage.html"))).unwrap()));
		r = TemplateResources.getStringAsResource("Test Web Page: <? AuthUI.accountPanel ?>", "text/html; charset=UTF-8");
		logger.debug(write(((Wrap)r.skin(session.lookupInterface(ObjectServer.class), new BasicRequestObject("/aPage.html"))).unwrap()));

		r = TemplateResources.getStringAsResource("Test Web Page: <? AuthUI.addAccountForm ?>", "text/html; charset=UTF-8");
		logger.debug(write(((Wrap)r.skin(session.lookupInterface(ObjectServer.class), new BasicRequestObject("/aPage.html"))).unwrap()));

		r = TemplateResources.getStringAsResource("Test Web Page: <? AuthUI.changePasswordForm ?>", "text/html; charset=UTF-8");
		logger.debug(write(((Wrap)r.skin(session.lookupInterface(ObjectServer.class), new BasicRequestObject("/aPage.html"))).unwrap()));

		r = TemplateResources.getStringAsResource("Test Web Page: <? AuthUI.createAccountForm ?>", "text/html; charset=UTF-8");
		logger.debug(write(((Wrap)r.skin(session.lookupInterface(ObjectServer.class), new BasicRequestObject("/aPage.html"))).unwrap()));

		r = TemplateResources.getStringAsResource("Test Web Page: <? AuthUI.editAccountURI ?>", "text/html; charset=UTF-8");
		logger.debug(write(((Wrap)r.skin(session.lookupInterface(ObjectServer.class), new BasicRequestObject("/aPage.html"))).unwrap()));

		m = webApi.dispatch(new BasicRequestObject(HttpMethod.GET, "resetPasswordPage.html", vars));
		vars.put("rid", "1");
		vars.put("token", "asdf");
		logger.debug("Receiver: {}", m.receiverType());
		logger.debug(write(((Wrap) m.call(session.lookupInterface(m.receiverType()))).unwrap()));

		m = webApi.dispatch(new BasicRequestObject(HttpMethod.GET, "resetPasswordPage.html", vars));
		vars.put("vid", "1");
		logger.debug("Receiver: {}", m.receiverType());
		logger.debug(write(((Wrap) m.call(session.lookupInterface(m.receiverType()))).unwrap()));
	}
}