// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.athenz.instanceproviderservice;

import com.google.inject.Inject;
import com.yahoo.cloud.config.ConfigserverConfig;
import com.yahoo.jdisc.http.ssl.SslTrustStoreConfigurator;
import com.yahoo.jdisc.http.ssl.SslTrustStoreContext;
import com.yahoo.log.LogLevel;
import com.yahoo.vespa.athenz.tls.KeyStoreBuilder;
import com.yahoo.vespa.athenz.tls.KeyStoreType;
import com.yahoo.vespa.athenz.tls.SignatureAlgorithm;
import com.yahoo.vespa.athenz.tls.X509CertificateBuilder;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig;

import javax.security.auth.x500.X500Principal;
import java.io.File;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.time.Instant;
import java.util.logging.Logger;

/**
 * @author bjorncs
 */
public class AthenzSslTrustStoreConfigurator implements SslTrustStoreConfigurator {

    private static final Logger log = Logger.getLogger(AthenzSslTrustStoreConfigurator.class.getName());
    private static final String CERTIFICATE_ALIAS = "cfgselfsigned";

    private final KeyStore trustStore;

    @Inject
    public AthenzSslTrustStoreConfigurator(KeyProvider keyProvider,
                                           ConfigserverConfig configserverConfig,
                                           AthenzProviderServiceConfig athenzProviderServiceConfig) {
        this.trustStore = createTrustStore(keyProvider, configserverConfig, athenzProviderServiceConfig);
    }

    @Override
    public void configure(SslTrustStoreContext sslTrustStoreContext) {
        sslTrustStoreContext.updateTrustStore(trustStore);
        log.log(LogLevel.INFO, "Configured JDisc trust store with self-signed certificate");
    }

    Instant getTrustStoreExpiry() throws KeyStoreException {
        X509Certificate certificate = (X509Certificate) trustStore.getCertificate(CERTIFICATE_ALIAS);
        return certificate.getNotAfter().toInstant();
    }

    private static KeyStore createTrustStore(KeyProvider keyProvider,
                                             ConfigserverConfig configserverConfig,
                                             AthenzProviderServiceConfig athenzProviderServiceConfig) {
        try {
            KeyPair keyPair = getKeyPair(keyProvider, configserverConfig, athenzProviderServiceConfig);
            X509Certificate selfSignedCertificate = createSelfSignedCertificate(keyPair, configserverConfig);
            log.log(LogLevel.FINE, "Generated self-signed certificate: " + selfSignedCertificate);
            return KeyStoreBuilder.withType(KeyStoreType.JKS)
                    .fromFile(new File(athenzProviderServiceConfig.athenzCaTrustStore()), "changeit".toCharArray())
                    .withCertificateEntry(CERTIFICATE_ALIAS, selfSignedCertificate)
                    .build();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static KeyPair getKeyPair(KeyProvider keyProvider,
                                      ConfigserverConfig configserverConfig,
                                      AthenzProviderServiceConfig athenzProviderServiceConfig) {
        String key = configserverConfig.environment() + "." + configserverConfig.region();
        AthenzProviderServiceConfig.Zones zoneConfig = athenzProviderServiceConfig.zones(key);
        return keyProvider.getKeyPair(zoneConfig.secretVersion());
    }

    private static X509Certificate createSelfSignedCertificate(KeyPair keyPair, ConfigserverConfig config)  {
        X500Principal subject = new X500Principal("CN="+ config.loadBalancerAddress());
        Instant now = Instant.now();
        X509CertificateBuilder builder = X509CertificateBuilder
                .fromKeypair(
                        keyPair,
                        subject,
                        now,
                        now.plus(Duration.ofDays(30)),
                        SignatureAlgorithm.SHA256_WITH_RSA,
                        now.toEpochMilli())
                .setBasicConstraints(true, true);
        config.zookeeperserver().forEach(server -> builder.addSubjectAlternativeName(server.hostname()));
        return builder.build();
    }

}

@@ -213,58 +213,8 @@
 ct;%0A
-import com.yahoo.cloud.config.ConfigserverConfig;%0A
 impo
@@ -326,39 +326,8 @@
 xt;%0A
-import com.yahoo.log.LogLevel;%0A
 impo
@@ -425,120 +425,8 @@
 pe;%0A
-import com.yahoo.vespa.athenz.tls.SignatureAlgorithm;%0Aimport com.yahoo.vespa.athenz.tls.X509CertificateBuilder;%0A
 impo
@@ -534,93 +534,16 @@
 java
-x.security.auth.x500.X500Principal;%0Aimport java.io.File;%0Aimport java.security.KeyPair
+.io.File
 ;%0Aim
@@ -675,80 +675,82 @@
 ime.
-Duration;%0Aimport java.time.Instant;%0Aimport java.util.logging.Logger;%0A%0A/*
+Instant;%0A%0A/**%0A * Programmatic configuration of configserver's truststore%0A 
 *%0A *
@@ -859,113 +859,8 @@
  %7B%0A%0A
-    private static final Logger log = Logger.getLogger(AthenzSslTrustStoreConfigurator.class.getName());%0A
     
@@ -1024,158 +1024,8 @@
 tor(
-KeyProvider keyProvider,%0A                                           ConfigserverConfig configserverConfig,%0A                                           
 Athe
@@ -1126,41 +1126,8 @@
 ore(
-keyProvider, configserverConfig, 
 athe
@@ -1307,101 +1307,8 @@
 e);%0A
-        log.log(LogLevel.INFO, %22Configured JDisc trust store with self-signed certificate%22);%0A
     
@@ -1583,547 +1583,80 @@
 ore(
-KeyProvider keyProvider,%0A                                             ConfigserverConfig configserverConfig,%0A                                             AthenzProviderServiceConfig athenzProviderServiceConfig) %7B%0A        try %7B%0A            KeyPair keyPair = getKeyPair(keyProvider, configserverConfig, athenzProviderServiceConfig);%0A            X509Certificate selfSignedCertificate = createSelfSignedCertificate(keyPair, configserverConfig);%0A            log.log(LogLevel.FINE, %22Generated self-signed certificate: %22 + selfSignedCertificate);
+AthenzProviderServiceConfig athenzProviderServiceConfig) %7B%0A        try %7B
 %0A   
@@ -1806,118 +1806,8 @@
 e())
-, %22changeit%22.toCharArray())%0A                    .withCertificateEntry(CERTIFICATE_ALIAS, selfSignedCertificate
 )%0A  
@@ -1930,1281 +1930,6 @@
  %7D%0A%0A
-    private static KeyPair getKeyPair(KeyProvider keyProvider,%0A                                      ConfigserverConfig configserverConfig,%0A                                      AthenzProviderServiceConfig athenzProviderServiceConfig) %7B%0A        String key = configserverConfig.environment() + %22.%22 + configserverConfig.region();%0A        AthenzProviderServiceConfig.Zones zoneConfig = athenzProviderServiceConfig.zones(key);%0A        return keyProvider.getKeyPair(zoneConfig.secretVersion());%0A    %7D%0A%0A    private static X509Certificate createSelfSignedCertificate(KeyPair keyPair, ConfigserverConfig config)  %7B%0A        X500Principal subject = new X500Principal(%22CN=%22+ config.loadBalancerAddress());%0A        Instant now = Instant.now();%0A        X509CertificateBuilder builder = X509CertificateBuilder%0A                .fromKeypair(%0A                        keyPair,%0A                        subject,%0A                        now,%0A                        now.plus(Duration.ofDays(30)),%0A                        SignatureAlgorithm.SHA256_WITH_RSA,%0A                        now.toEpochMilli())%0A                .setBasicConstraints(true, true);%0A        config.zookeeperserver().forEach(server -%3E builder.addSubjectAlternativeName(server.hostname()));%0A        return builder.build();%0A    %7D%0A%0A
 %7D%0A

Remove self-signed certificate from configserver truststore