//
// Copyright (c) 2005 PortWise AB. All rights reserved.
//
// This software is the confidential and proprietary information of PortWise
// AB. ("Confidential Information"). You shall not disclose such Confidential
// Information and shall use it only in accordance with the terms of the
// agreement you entered into with PortWise AB.
//
// Warning: This computer program is protected by copyright law and
// international treaties. Unauthorized reproduction or distribution of this
// program, or any portion of it, may result in severe civil and criminal
// penalties, and will be prosecuted to the maximum extent possible under law.
//

package com.portwise.xpi.epi.plugins.mac;

import java.util.HashMap;
import java.util.Locale;
import java.util.Vector;

import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

import com.portwise.core.plugins.plugin.BasePlugin;
import com.portwise.core.plugins.plugin.PluginProperties;
import com.portwise.xpi.epi.plugins.AssessPluginException;
import com.portwise.xpi.epi.plugins.AssessPluginResult;
import com.portwise.xpi.epi.plugins.AssessServerSettings;
import com.portwise.xpi.epi.plugins.AssessTermInformation;
import com.portwise.xpi.epi.plugins.ClientRegistry;
import com.portwise.xpi.epi.plugins.IAssessPlugin;
import com.portwise.xpi.epi.plugins.XMLDocument;


/**
 * The plug-in retrieves the MAC address of the connecting client and validates the address against
 * a pre-defined list of authorized client MAC addresses. If an address match is identified in the
 * authorized MAC address list, the client is allowed access to the protected resource. If the
 * client's MAC address is not authorized, the request is rejected.
 * 
 * @author PortWise AB
 */
public class AssessMACPlugin
    extends BasePlugin
    implements AssessMACPluginProperties, IAssessPlugin
{
    /** Holds the settings supplied by the Policy Service describing the access rule. */
    private PluginProperties mPluginProperties = null;


    /**
     * The plug-in needs an empty main method to be able to initialize the plugin.
     * 
     * @param args System arguments.
     */
    public static void main(String[] args)
    {
        // Nothing to do here
    }


    /**
     * @see com.portwise.core.plugins.plugin.IPlugin#getName()
     */
    public String getName()
    {
        return "Assessment MAC Plugin";
    }


    /**
     * @see com.portwise.core.plugins.plugin.IPlugin#getMajorVersion()
     */
    public int getMajorVersion()
    {
        return 1;
    }


    /**
     * @see com.portwise.core.plugins.plugin.IPlugin#getMinorVersion()
     */
    public int getMinorVersion()
    {
        return 0;
    }


    /**
     * @see com.portwise.core.plugins.plugin.BasePlugin#getDefaultsFile()
     */
    protected String getDefaultsFile()
    {
        return "/com/portwise/xpi/epi/plugins/mac/defaults.properties";
    }


    /**
     * @see com.portwise.core.plugins.plugin.BasePlugin#getLocaleFile()
     */
    protected String getLocaleFile()
    {
        return "com.portwise.xpi.epi.plugins.mac.locale";
    }


    /**
     * @see com.portwise.core.plugins.plugin.BasePlugin#getPropertyEnums()
     */
    protected int[] getPropertyEnums()
    {
        return PROPERTY_ENUMS;
    }


    /**
     * @see com.portwise.core.plugins.plugin.IPlugin#getPropertyKeys()
     */
    public String[] getPropertyKeys()
    {
        return PROPERTY_KEYS;
    }


    /**
     * @see com.portwise.core.plugins.plugin.IPlugin#isPropertyRequired(java.lang.String)
     */
    public boolean isPropertyRequired(String propertyKey)
    {
        // When configuring the plug-in in the administrator this method will decide if the
        // properties should be required or not. In this example only the MAC address property
        // is required.
        switch (getPropertyEnum(propertyKey))
        {
            case ENUM_MAC_ADDRESSES :
                return true;
            case ENUM_USE_DEFAULT_MESSAGE :
                return false;
            case ENUM_DEFAULT_MESSAGE :
                return false;
            default :
                return false;
        }
    }


    /**
     * @see com.portwise.core.plugins.plugin.IPlugin#testProperties(com.portwise.core.plugins.plugin.PluginProperties,
     *      java.util.Locale)
     */
    public HashMap testProperties(PluginProperties properties,
                                  Locale locale)
    {
        // Nothing to test here
        HashMap errors = new HashMap();
        return errors;
    }


    /**
     * @see com.portwise.core.plugins.plugin.IPlugin#isPropertyAdvanced(java.lang.String)
     */
    public boolean isPropertyAdvanced(String propertyKey)
    {
        // None of the properties are advanced so always return false
        return false;
    }


    /**
     * @see com.portwise.core.plugins.plugin.IPlugin#getPropertyMin(java.lang.String)
     */
    public int getPropertyMin(String propertyKey)
    {
        // None of the properties in the example have a min value.
        // Use this method for numeric property types.
        switch (getPropertyEnum(propertyKey))
        {
            default :
                return 0;
        }
    }


    /**
     * @see com.portwise.core.plugins.plugin.IPlugin#getPropertyMax(java.lang.String)
     */
    public int getPropertyMax(String propertyKey)
    {
        // None of the properties in the example have a max value.
        // Use this method for numeric property types.
        switch (getPropertyEnum(propertyKey))
        {
            default :
                return Integer.MAX_VALUE;
        }
    }


    /**
     * @see com.portwise.core.plugins.plugin.IPlugin#getPropertySelectValues(java.lang.String)
     */
    public String[] getPropertySelectValues(String propertyKey)
    {
        // The sample plug-in doesn't have any select lists in the property list, so we
        // return null.
        switch (getPropertyEnum(propertyKey))
        {
            default :
                return null;
        }
    }


    /**
     * @see com.portwise.core.plugins.plugin.IPlugin#getPropertyType(java.lang.String)
     */
    public int getPropertyType(String propertyKey)
    {
        // Return the type of the current property defined by the property key.
        switch (getPropertyEnum(propertyKey))
        {
            case ENUM_MAC_ADDRESSES :
                return PluginProperties.PRP_TYPE_STRING;
            case ENUM_USE_DEFAULT_MESSAGE :
                return PluginProperties.PRP_TYPE_BOOLEAN;
            case ENUM_DEFAULT_MESSAGE :
                return PluginProperties.PRP_TYPE_TEXT;
            default :
                return PluginProperties.PRP_TYPE_STRING;
        }
    }


    /**
     * @see com.portwise.xpi.epi.plugins.IAssessPlugin#addClientInfoCollectors(com.portwise.xpi.epi.plugins.XMLDocument)
     */
    public AssessPluginResult addClientInfoCollectors(XMLDocument document)
        throws AssessPluginException
    {
        // The plugin require the client scan to return network information.
        // The key in ServerPolicy.xml is
        // ServerPolicy.Windows.Policy.Assessment.General.CollectNetworkInfo.Active.
        // We must verify that the key is active. If it isn't, then we have to change the value in
        // the policy document.
        // This method is called just before the policy document is sent to the client for scanning.
        info(getName() + ": addClientInfoCollectors");

        try
        {
            HashMap attributes = new HashMap();
            String c;

            // Copy network attribute from existing settings and add our required
            c = document
                .getValueForKey("ServerPolicy.Windows.Policy.Assessment.General.CollectNetworkInfo.Active");
            if (c != null)
            {
                // Set the flag to true
                c = "true";
                attributes.put("Active", c);
                document.createElementWithAttributes(
                    "ServerPolicy.Windows.Policy.Assessment.General.CollectNetworkInfo.",
                    attributes, true);
            }

            // Copy windows attribute from existing settings and add our required
            c = document
                .getValueForKey("ServerPolicy.Windows.Policy.Assessment.General.CollectWindowsInfo.Active");
            if (c != null)
            {
                // Set the flag to true
                c = "true";
                attributes.put("Active", c);
                document.createElementWithAttributes(
                    "ServerPolicy.Windows.Policy.Assessment.General.CollectWindowsInfo.",
                    attributes, true);
            }
        }
        catch (Exception e)
        {
            return new AssessPluginResult("Plugin failed to add client collector",
                "Plugin could not add requirements needed");
        }

        return new AssessPluginResult();
    }


    /**
     * @see com.portwise.xpi.epi.plugins.IAssessPlugin#onAccessResource(com.portwise.xpi.epi.plugins.ClientRegistry,
     *      com.portwise.xpi.epi.plugins.AssessTermInformation, java.lang.String)
     */
    public AssessPluginResult onAccessResource(ClientRegistry clientRegistry,
                                               AssessTermInformation accessRule,
                                               String uri)
        throws AssessPluginException
    {
        info(getName() + ": onAccessResource");

        // Get network and domain info from the client scan data
        Vector clientData = clientRegistry
            .getNodesForKey("ClientRegistry.Windows.NetworkInterface");
        Vector clientDataDomain = clientRegistry
            .getNodesForKey("ClientRegistry.Windows.WindowsDomain");

        String domValue = null;
        if (clientDataDomain != null && clientDataDomain.size() > 0)
        {
            NamedNodeMap domMap = ((Node) clientDataDomain.firstElement()).getAttributes();
            domValue = domMap.getNamedItem("langroup").getNodeValue();
        }

        // Get the value configured by the PortWise Administrator
        String authorizeAddresses = mPluginProperties.getString(
            AssessMACPluginProperties.MAC_ADDRESSES, "");

        int vSize;
        if (clientData != null && authorizeAddresses != null)
        {
            // Create a new handler that will do the matching
            AccessHandler addressAuthorizer = new AccessHandler(authorizeAddresses);
            vSize = clientData.size();
            String value = null;
            // For all network interfaces
            for (int j = 0; j < vSize; j++)
            {
                // Get the clients physical address
                NamedNodeMap map = ((Node) clientData.elementAt(j)).getAttributes();
                value = map.getNamedItem("physicalAddr").getNodeValue();
                if (value.equals("N/A") && j != (vSize - 1))
                {
                    continue;
                }

                info("AssessMACPlugin message: Access Attempt by client MAC: " + value);

                if (domValue != null)
                    info("AssessMACPlugin message: client belongs to Lan group Domain: " + domValue);
                else
                    info("AssessMACPlugin message: Plugin failed to retrive client Domain");

                // Verify the end-user's MAC address against the list of valid addresses
                if (addressAuthorizer.checkAccess(value))
                    return new AssessPluginResult();
            }
        }

        // The user did not have access to the resource.
        // Returning a new AssessPluginResult object with the error messages.
        // The first argument is logged to the system log, the second argument
        // is displayed to the end user.
        String logMessage = "Assessment MAC Plugin error message";
        return new AssessPluginResult(logMessage, getEndUserMessage(logMessage));

    }


    /**
     * @see com.portwise.xpi.epi.plugins.IAssessPlugin#onLoad(com.portwise.xpi.epi.plugins.AssessServerSettings,
     *      com.portwise.core.plugins.plugin.PluginProperties)
     */
    public AssessPluginResult onLoad(AssessServerSettings serverSettings,
                                     PluginProperties environmentSettings)
        throws AssessPluginException
    {
        // Save the properties supplied by the policy service in a local
        // variable so we can use the information later.
        info(getName() + ": onLoad");
        mPluginProperties = environmentSettings;
        return new AssessPluginResult();
    }


    /**
     * @see com.portwise.xpi.epi.plugins.IAssessPlugin#onUnload()
     */
    public AssessPluginResult onUnload()
        throws AssessPluginException
    {
        // Never called in PortWise 4.5
        info(getName() + ": onUnload");
        return new AssessPluginResult();
    }


    /**
     * @see com.portwise.xpi.epi.plugins.IAssessPlugin#onUpdate(com.portwise.xpi.epi.plugins.ClientRegistry)
     */
    public AssessPluginResult onUpdate(ClientRegistry clientRegistry)
        throws AssessPluginException
    {
        // Never called in PortWise 4.5
        info(getName() + ": onUpdate");
        return new AssessPluginResult();
    }


    /**
     * @see com.portwise.xpi.epi.plugins.IAssessPlugin#onVerify(com.portwise.xpi.epi.plugins.ClientRegistry,
     *      java.lang.String, java.lang.String)
     */
    public AssessPluginResult onVerify(ClientRegistry clientRegistry,
                                       String sessionId,
                                       String username)
        throws AssessPluginException
    {
        // Always return true when the user sends client data otherwise will the authentication fail
        info(getName() + ": onVerify");
        return new AssessPluginResult();
    }


    /**
     * Method for getting the message that should be displayed for the end user. The message
     * displayed is the same as the log message, if not property USER_DEFAULT_MESSAGE is set. Then
     * the default message is used.
     * 
     * @param logMessage Log message
     * @return End user message
     */
    private String getEndUserMessage(String logMessage)
    {
        if (mPluginProperties.getBoolean(AssessMACPluginProperties.USE_DEFAULT_MESSAGE, false))
        {
            return mPluginProperties.getString(AssessMACPluginProperties.DEFAULT_MESSAGE, "");
        }
        return logMessage;
    }
}