groovy.
 *
 * @author <NAME>;
 */
@Extension
public final class NegotiateSSO extends GlobalConfiguration {
    private static final Logger LOGGER = Logger.getLogger(NegotiateSSO.class.getName());

    private boolean enabled = false;
    
    private boolean redirectEnabled = false;
    private String redirect = "yourdomain.com";
    private boolean allowLocalhost = true;

    private boolean allowImpersonate = false;
    private String principalFormat = "fqn";
    private String roleFormat = "fqn";
    private String protocols = "Negotiate NTLM";
    private String providers = NegotiateSecurityFilterProvider.class.getName() + " " + BasicSecurityFilterProvider.class.getName();

    private transient NegSecFilter filter;
    private transient NegSecUserSeedFilter userSeedFilter;

    /**
     * Fetches the singleton instance of this plugin.
     *
     * @return the instance.
     */
    public static NegotiateSSO getInstance() {
        Jenkins jenkins = Jenkins.get();
        return jenkins.getDescriptorByType(NegotiateSSO.class);
    }
    
    /**
     * Get the proper category for the settings location
     * @return GlobalConfigurationCategory.Security
     */
    @Override
    public GlobalConfigurationCategory getCategory() {
        return GlobalConfigurationCategory.get(GlobalConfigurationCategory.Security.class);
    }
    
    /**
     * The Plugin Display name
     * @return Display name
     */
    @Override
    public String getDisplayName() {
        return "NegotiateSSO";
    }
    
    /**
     * Initializes and starts the filter, if enabled.
     */
    public NegotiateSSO() {
        super();
        //load(); // start() calls load().
        try {
            start();
        }
        catch (ServletException e) {
            NegotiateSSO.LOGGER.log(Level.SEVERE, "Failed initialize plugin due to faulty config.", e);
            this.enabled = false;
        }
    }

    /**
     * Starts the plugin. Loads previous configuration if such exists.
     *
     * @throws ServletException if the Waffle NTLM/Kerberos filter cannot be added to
     * Jenkins.
     */
    public void start() throws ServletException {
        load();
        try {
            if (this.enabled) {
                startFilter();
            }
        } catch (ServletException e) {
            NegotiateSSO.LOGGER.log(Level.SEVERE, "Failed initialize plugin due to faulty config.", e);
            this.enabled = false;
            removeFilter();
        }
    }
    
    /**
     * Initializes the filter and inserts it into the chain
     * @throws ServletException 
     */
    private void startFilter() throws ServletException {
        if (!System.getProperty("os.name").toLowerCase().contains("win")) {
            NegotiateSSO.LOGGER.log(Level.SEVERE, "Not a Windows OS. NegotiateSSO will not work. Plugin Disabled.");
            this.enabled = false;
            return;
        }
        
        NegotiateSSO.LOGGER.log(Level.INFO, "Starting Security Filter");
        this.filter = new NegSecFilter();
        this.filter.setImpersonate(this.allowImpersonate);
        this.filter.setPrincipalFormat(this.principalFormat); // default "fqn", options "fqn", "sid", "both"
        this.filter.setRoleFormat(this.roleFormat); // default "fqn", options "fqn", "sid", "both", "none"
        this.filter.setAllowLocalhost(this.allowLocalhost);
        this.filter.setRedirect(this.redirectEnabled, this.redirect);
        SecurityFilterConfig config = new SecurityFilterConfig();
        config.setParameter("roleFormat", this.roleFormat);
        config.setParameter("principalFormat", this.principalFormat);
        config.setParameter("impersonate", String.valueOf(this.allowImpersonate));
        config.setParameter("allowGuestLogin", String.valueOf(Boolean.FALSE));
        config.setParameter("securityFilterProviders", this.providers); // split around any whitespace: \t\n\x0B\f\r
        //config.setParameter("securityFilterProviders", NegotiateSecurityFilterProvider.class.getName()); // split around any whitespace: \t\n\x0B\f\r
        //config.setParameter("securityFilterProviders", BasicSecurityFilterProvider.class.getName()); // split around any whitespace: \t\n\x0B\f\r
        
        //config.setParameter("allowLocalhost", String.valueOf(this.allowLocalhost));
        //config.setParameter("redirectEnabled", String.valueOf(this.redirectEnabled));
        //config.setParameter("redirect", this.redirect);
        if (this.providers.contains("NegotiateSecurityFilterProvider")) {
            //config.setParameter("waffle.servlet.spi.BasicSecurityFilterProvider/realm", "");
            //config.setParameter("waffle.servlet.spi.NegotiateSecurityFilterProvider/protocols", "Negotiate NTLM"); // split around any whitespace: \t\n\x0B\f\r
            config.setParameter("waffle.servlet.spi.NegotiateSecurityFilterProvider/protocols", protocols); // split around any whitespace: \t\n\x0B\f\r
        }
        
        this.filter.init(config);
        this.userSeedFilter = new NegSecUserSeedFilter();
        this.userSeedFilter.init(null);
        
        // https://github.com/dblock/waffle/blob/master/Docs/tomcat/TomcatSingleSignOnValve.md
        //    fqn: Fully qualified names, such as domain\\username. When unavailable, a SID is used. This is the default.
        //    sid: SID in the S- format.
        //    both: Both a fully qualified name and a SID in the S- format. The fully qualified name is placed in the list first. Tomcat assumes that the first entry of this list is a username.
        //    none Do not include a principal name. Permitted only for roleFormat.
        PluginServletFilter.addFilter(this.filter);
        PluginServletFilter.addFilter(this.userSeedFilter);
    }

    /**
     * Safe and complete removal of the filter from the system.
     *
     * @throws ServletException if PluginServletFilter does
     */
    private void removeFilter() throws ServletException {
        if (this.filter!= null) {
            PluginServletFilter.removeFilter(this.filter);
            this.filter.destroy();
            this.filter = null;
        }
        
        if (this.userSeedFilter!= null) {
            PluginServletFilter.removeFilter(this.userSeedFilter);
            this.userSeedFilter.destroy();
            this.userSeedFilter = null;
        }
    }

    /**
     * When submit is pressed on the global config page and any settings for
     * this plugin are changed, this method is called. It updates all the
     * fields, restarts or stops the filter depending on configuration and saves
     * the configuration to disk.
     *
     * @param req the Stapler Request to serve.
     * @param formData the JSON data containing the new configuration.
     * @return true if configuration successful; false otherwise
     * @throws Descriptor.FormException if any data in the form is wrong.
     */
    @Override
    public boolean configure(StaplerRequest req, JSONObject formData)
            throws Descriptor.FormException {
        try {
            if (!System.getProperty("os.name").toLowerCase().contains("win")) {
                NegotiateSSO.LOGGER.log(Level.SEVERE, "Not a Windows OS. NegotiateSSO will not work. Plugin Disabled.");
                removeFilter();
                this.enabled = false;
            }
            else if (formData.has("enabled")) {
                JSONObject data = formData.getJSONObject("enabled");
            
                //NegotiateSSO.LOGGER.log(Level.SEVERE, "data: " + data.toString());
                if (!data.has("allowImpersonate") ||!data.has("roleFormat") 
                        ||!data.has("principalFormat") ||!data.has("protocols") 
                        ||!data.