package org.apereo.cas.support.saml.authentication;

import org.apereo.cas.CentralAuthenticationService;
import org.apereo.cas.authentication.AuthenticationBuilder;
import org.apereo.cas.authentication.AuthenticationTransaction;
import org.apereo.cas.authentication.Credential;
import org.apereo.cas.authentication.HttpBasedServiceCredential;
import org.apereo.cas.authentication.UsernamePasswordCredential;
import org.apereo.cas.authentication.metadata.BaseAuthenticationMetaDataPopulator;

import lombok.Setter;
import lombok.ToString;
import lombok.val;
import org.reflections.Reflections;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;

import java.util.HashMap;
import java.util.Map;

/**
 * Capture SAML authentication metadata.
 *
 * @author Misagh Moayyed
 * @since 5.0.0
 */
@ToString(callSuper = true)
@Setter
public class SamlAuthenticationMetaDataPopulator extends BaseAuthenticationMetaDataPopulator {

    /**
     * The Constant ATTRIBUTE_AUTHENTICATION_METHOD.
     */
    public static final String ATTRIBUTE_AUTHENTICATION_METHOD = "samlAuthenticationStatementAuthMethod";

    /**
     * The Constant AUTHN_METHOD_PASSWORD.
     */
    public static final String AUTHN_METHOD_PASSWORD = "urn:oasis:names:tc:SAML:1.0:am:password";

    /**
     * The Constant AUTHN_METHOD_SSL_TLS_CLIENT.
     */
    public static final String AUTHN_METHOD_SSL_TLS_CLIENT = "urn:ietf:rfc:2246";

    /**
     * The Constant AUTHN_METHOD_X509_PUBLICKEY.
     */
    public static final String AUTHN_METHOD_X509_PUBLICKEY = "urn:oasis:names:tc:SAML:1.0:am:X509-PKI";

    /**
     * The Constant AUTHN_METHOD_UNSPECIFIED.
     */
    public static final String AUTHN_METHOD_UNSPECIFIED = "urn:oasis:names:tc:SAML:1.0:am:unspecified";

    private final Map<String, String> authenticationMethods = new HashMap<>();

    /**
     * Instantiates a new SAML authentication meta data populator.
     */
    public SamlAuthenticationMetaDataPopulator() {
        val packageName = CentralAuthenticationService.NAMESPACE;
        val reflections =
            new Reflections(new ConfigurationBuilder()
                .filterInputsBy(new FilterBuilder().includePackage(packageName))
                .setUrls(ClasspathHelper.forPackage(packageName))
                .setScanners(new SubTypesScanner(true)));

        val subTypes = reflections.getSubTypesOf(Credential.class);
        subTypes.forEach(t -> this.authenticationMethods.put(t.getName(), AUTHN_METHOD_UNSPECIFIED));
        this.authenticationMethods.put(HttpBasedServiceCredential.class.getName(), AUTHN_METHOD_SSL_TLS_CLIENT);
        this.authenticationMethods.put(UsernamePasswordCredential.class.getName(), AUTHN_METHOD_PASSWORD);
        this.authenticationMethods.put("org.apereo.cas.adaptors.x509.authentication.principal.X509CertificateCredentials", AUTHN_METHOD_X509_PUBLICKEY);
    }

    @Override
    public void populateAttributes(final AuthenticationBuilder builder, final AuthenticationTransaction transaction) {
        transaction.getPrimaryCredential().ifPresent(c -> {
            val credentialsClass = c.getClass().getName();
            val authenticationMethod = this.authenticationMethods.get(credentialsClass);
            builder.addAttribute(ATTRIBUTE_AUTHENTICATION_METHOD, authenticationMethod);
        });

    }

    @Override
    public boolean supports(final Credential credential) {
        return true;
    }

    /**
     * Map of user-defined mappings. Note it is possible to override the
     * defaults. Mapping should be of the following type:
     * <pre>Package/Class Name as String -&gt; Name SAML Type</pre>
     * <p>
     * Example: ({@code "HttpBasedServiceCredential"
     * -&gt; SAMLAuthenticationStatement.AuthenticationMethod_SSL_TLS_Client})
     *
     * @param userDefinedMappings map of user defined authentication types.
     */
    public void setUserDefinedMappings(final Map<String, String> userDefinedMappings) {
        this.authenticationMethods.putAll(userDefinedMappings);
    }
}

@@ -50,60 +50,8 @@
 n;%0A%0A
-import org.apereo.cas.CentralAuthenticationService;%0A
 impo
@@ -98,32 +98,32 @@
 icationBuilder;%0A
+
 import org.apere
@@ -501,231 +501,8 @@
 val;
-%0Aimport org.reflections.Reflections;%0Aimport org.reflections.scanners.SubTypesScanner;%0Aimport org.reflections.util.ClasspathHelper;%0Aimport org.reflections.util.ConfigurationBuilder;%0Aimport org.reflections.util.FilterBuilder;
 %0A%0Aim
@@ -1811,531 +1811,8 @@
 ) %7B%0A
-        val packageName = CentralAuthenticationService.NAMESPACE;%0A        val reflections =%0A            new Reflections(new ConfigurationBuilder()%0A                .filterInputsBy(new FilterBuilder().includePackage(packageName))%0A                .setUrls(ClasspathHelper.forPackage(packageName))%0A                .setScanners(new SubTypesScanner(true)));%0A%0A        val subTypes = reflections.getSubTypesOf(Credential.class);%0A        subTypes.forEach(t -%3E this.authenticationMethods.put(t.getName(), AUTHN_METHOD_UNSPECIFIED));%0A
     
@@ -2508,16 +2508,25 @@
 hods.get
+OrDefault
 (credent
@@ -2534,16 +2534,42 @@
 alsClass
+, AUTHN_METHOD_UNSPECIFIED
 );%0A     

refactor credential discovery for saml metadata populator