/***********************************************************
Copyright (C) 2019 VeriSign, Inc.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

http://www.verisign.com/nds/naming/namestore/techdocs.html
 ***********************************************************/

package com.verisign.epp.serverstub.v1_0;

import java.sql.Date;
import java.util.Calendar;
import java.util.GregorianCalendar;

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

import com.verisign.epp.codec.gen.EPPLoginCmd;
import com.verisign.epp.codec.gen.EPPResponse;
import com.verisign.epp.codec.gen.EPPResult;
import com.verisign.epp.codec.gen.EPPTransId;
import com.verisign.epp.codec.loginsec.v1_0.EPPLoginSec;
import com.verisign.epp.codec.loginsec.v1_0.EPPLoginSecData;
import com.verisign.epp.codec.loginsec.v1_0.EPPLoginSecEvent;
import com.verisign.epp.codec.loginsec.v1_0.EventLevel;
import com.verisign.epp.codec.loginsec.v1_0.EventType;
import com.verisign.epp.framework.EPPEvent;
import com.verisign.epp.framework.EPPEventResponse;
import com.verisign.epp.serverstub.LoginSecSubGenHandler;
import com.verisign.epp.serverstub.SessionData;


/**
 * The <code>LoginSecV04GenHandler</code> implements the LoginSecSubGenHandler
 * interface to handle version "1.0" of the login security extension.
 */
public class LoginSecV1_0GenHandler implements LoginSecSubGenHandler {

	/**
	 * Category for logging
	 */
	    private static Logger cat = LoggerFactory.getLogger(LoginSecV1_0GenHandler.class);
	      

	/**
	 * Handle the login command using version 0.1 (v1_0) of the login security
	 * extension. This handler will log which password and which new password
	 * (EPPLoginCmd or EPPLoginSec extension) is used for authentication. In
	 * addition, the login security events will be returned based on predefined
	 * login identifiers (clientId), which include:<br>
	 * <ul>
	 * <li>test-expiring-pw - Will successfully login and return the password
	 * expiring warning that the password will expire in a week.
	 * <li>test-expired-pw - Will fail the login and return an authentication
	 * error response with the password expired error.
	 * <li>test-all-events - Will successfully login and will return the full
	 * list of security events in the response.
	 * </ul>
	 * An attempt to send a password or new password that is greater than 16
	 * characters long without the login security extension will fail with a 2001
	 * "Command syntax error", since the command will fail XML schema validation
	 * in the server. The EPP SDK will automatically send a password or new
	 * password greater than 16 characters with the login security extension and
	 * set the EPPLoginCmd password or new password with the predefined constant
	 * value.
	 * 
	 * @param aEvent
	 *           Event must contain an EPPLoginCmd for handling
	 * @param aData
	 *           Extra server data, which is <code>SessionData</code> for the
	 *           Stub Server.
	 *
	 * @return Response to handling the login command
	 */
	public EPPEventResponse doLogin(EPPEvent aEvent, Object aData) {
		SessionData sessionData = (SessionData) aData;

		EPPLoginCmd theLoginCmd = (EPPLoginCmd) aEvent.getMessage();

		EPPTransId transId = new EPPTransId(theLoginCmd.getTransId(), "54322-XYZ");
		EPPResponse theResponse = new EPPResponse(transId);
		EPPLoginSecData loginSecData = null;
		EPPLoginSecEvent theEvent = null;

		String thePassword = null;
		String theNewPassword = null;
		Calendar calendar = new GregorianCalendar();

		EPPLoginSec theExt = (EPPLoginSec) theLoginCmd
		      .getExtension(com.verisign.epp.codec.loginsec.v1_0.EPPLoginSec.class);

		// Which password will be used?
		if ((theExt != null) && (theLoginCmd.getPassword().equals(EPPLoginSec.LOGIN_SECURITY_PASSWORD)) && (theExt.hasPassword())) {
			thePassword = theExt.getPassword();
			cat.info("Authentication using EPPLoginSec extension password with length = " + thePassword.length());
		}
		else {
			thePassword = theLoginCmd.getPassword();
			cat.info("Authentication using EPPLoginCmd password with length = " + thePassword.length());
		}

		// Which new password will be used?
		if (theLoginCmd.hasNewPassword()) {
			if ((theExt != null) && (theLoginCmd.getNewPassword().equals(EPPLoginSec.LOGIN_SECURITY_PASSWORD)) && (theExt.hasNewPassword())) {
				theNewPassword = theExt.getNewPassword();
				cat.info("New password using EPPLoginSec extension with new password length = " + theNewPassword.length());
			}
			else {
				theNewPassword = theLoginCmd.getNewPassword();
				cat.info("New password using EPPLoginCmd with length = " + theNewPassword.length());
			}
		}
		
		// Has user agent?
		if ((theExt != null) && (theExt.hasUserAgent())) {
			cat.info("User agent = [" + theExt.getUserAgent() + "]");			
		}
		else {
			cat.info("No user agent passed");
		}

		// Process the login based on the login identifier (clientId)
		switch (theLoginCmd.getClientId()) {
			case "test-expiring-pw":
				theResponse.setResult(EPPResult.SUCCESS);
				sessionData.setLoggedIn(true);
				sessionData.setLoginCmd(theLoginCmd);

				loginSecData = new EPPLoginSecData();
				calendar = new GregorianCalendar();
				calendar.setTime(new Date(System.currentTimeMillis()));
				calendar.add(Calendar.DAY_OF_YEAR, 7);
				theEvent = new EPPLoginSecEvent(EventType.PASSWORD, EventLevel.WARNING, calendar.getTime(),
				      "Password expiring in a week");
				loginSecData.addEvent(theEvent);
				theResponse.addExtension(loginSecData);
				break;
			case "test-expired-pw":
				theResponse.setResult(EPPResult.AUTHENTICATION_ERROR);

				loginSecData = new EPPLoginSecData();
				calendar = new GregorianCalendar();
				calendar.setTime(new Date(System.currentTimeMillis()));
				calendar.add(Calendar.DAY_OF_YEAR, -1);
				theEvent = new EPPLoginSecEvent(EventType.PASSWORD, EventLevel.ERROR, calendar.getTime(),
				      "Password has expired");
				loginSecData.addEvent(theEvent);
				theResponse.addExtension(loginSecData);
				break;
			case "test-all-events":
				theResponse.setResult(EPPResult.SUCCESS);
				sessionData.setLoggedIn(true);
				sessionData.setLoginCmd(theLoginCmd);

				loginSecData = new EPPLoginSecData();

				// Expiring password event
				calendar = new GregorianCalendar();
				calendar.setTime(new Date(System.currentTimeMillis()));
				calendar.add(Calendar.DAY_OF_YEAR, 7);
				theEvent = new EPPLoginSecEvent(EventType.PASSWORD, EventLevel.WARNING, calendar.getTime(),
				      "Password expiring in a week");
				loginSecData.addEvent(theEvent);

				// Expiring certificate event (no description)
				calendar = new GregorianCalendar();
				calendar.setTime(new Date(System.currentTimeMillis()));
				calendar.add(Calendar.DAY_OF_YEAR, 5);
				theEvent = new EPPLoginSecEvent(EventType.CERTIFICATE, EventLevel.WARNING, calendar.getTime(), null);
				loginSecData.addEvent(theEvent);

				// Insecure cipher
				theEvent = new EPPLoginSecEvent(EventType.CIPHER, EventLevel.WARNING, "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
				      "Insecure cipher negotiated");
				loginSecData.addEvent(theEvent);

				// Insecure protocol
				theEvent = new EPPLoginSecEvent(EventType.TLS_PROTOCOL, EventLevel.WARNING, "SSLv3",
				      "Insecure TLS protocol negotiated");
				loginSecData.addEvent(theEvent);

				// Invalid new password
				theEvent = new EPPLoginSecEvent(EventType.NEW_PW, EventLevel.ERROR,
				      "New password does not meet complexity requirements");
				loginSecData.addEvent(theEvent);

				// Failed login statistical event
				theEvent = new EPPLoginSecEvent(EventType.STAT, "failedLogins", EventLevel.WARNING, "100", "P1D",
				      "100 invalid logins over 1 day");
				loginSecData.addEvent(theEvent);

				// Custom security event
				theEvent = new EPPLoginSecEvent(EventType.CUSTOM, "myCustomEvent", EventLevel.WARNING,
				      "A custom login security event occured");
				loginSecData.addEvent(theEvent);

				theResponse.addExtension(loginSecData);
				break;
			default:
				theResponse.setResult(EPPResult.SUCCESS);
				sessionData.setLoggedIn(true);
				sessionData.setLoginCmd(theLoginCmd);
		}

		return new EPPEventResponse(theResponse);
	}

}
