/**
 * Copyright 2017 Yahoo Holdings Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.yahoo.athenz.auth.impl.aws;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

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

import com.amazonaws.services.kms.AWSKMS;
import com.amazonaws.services.kms.AWSKMSClientBuilder;
import com.amazonaws.services.kms.model.DecryptRequest;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectInputStream;
import com.yahoo.athenz.auth.PrivateKeyStore;

/**
 * Downloads encrypted secrete from private S3 bucket and returns decrypted secrete in plaintext
 * Assumes that S3 bucket contains data encrypted with KMS. 
 * Assumes that this runs on an instance with a policy set to allow kms decrypt and read access to private S3 bucket. But it can be a public S3 bucket.
 * Assumes aws config is setup
 * @author charlesk
 * See http://docs.aws.amazon.com/kms/latest/developerguide/programming-encryption.html
 * See http://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/examples-s3-objects.html
 */
public class AwsPrivateKeyStore implements PrivateKeyStore {

    private static final Logger LOG = LoggerFactory.getLogger(AwsPrivateKeyStore.class);
    private final AmazonS3 s3;
    private final AWSKMS kms;
    
    public AwsPrivateKeyStore() {
        this(AmazonS3ClientBuilder.defaultClient(), AWSKMSClientBuilder.defaultClient());
    }
    
    public AwsPrivateKeyStore(final AmazonS3 s3, final AWSKMS kms) {
        this.s3 = s3;
        this.kms = kms;
    }
    
    @Override
    public String getApplicationSecret(final String appName, final String keyName) {
        String keyValue = "";
        S3Object s3Object = s3.getObject(appName, keyName);
        if (LOG.isDebugEnabled()) {
            LOG.debug("retrieving appName {}, key {}", appName, keyName);
        }
        if (null == s3Object) {
            LOG.error("error retrieving key {}, from bucket {}", keyName, appName);
            return keyValue;
        }
        try (S3ObjectInputStream s3InputStream = s3Object.getObjectContent(); 
                ByteArrayOutputStream result = new ByteArrayOutputStream();) {
            byte[] buffer = new byte[1024];
            int length;
            while ((length = s3InputStream.read(buffer)) != -1) {
                result.write(buffer, 0, length);
            }
            //key should be encrypted. decrypt using aws KMS
            DecryptRequest req = new DecryptRequest().withCiphertextBlob(ByteBuffer.wrap(result.toByteArray()));
            ByteBuffer plainText = kms.decrypt(req).getPlaintext();
            keyValue = new String(plainText.array());
        } catch (IOException e) {
            LOG.error("error getting application secret.", e);
        }
        return keyValue.trim();
    }
}

@@ -1518,22 +1518,144 @@
 et.%0A * A
-ssumes
+mazonS3 lib defaults to reading from S3 buckets created under us-east-1 unless its explicitly specified using system property or
  aws con
@@ -1661,17 +1661,12 @@
 nfig
- is setup
+%0A * 
 %0A * 
@@ -2016,16 +2016,100 @@
 class);%0A
+    private static final String ATHENZ_PROP_AWS_S3_REGION = %22athenz.aws.s3.region%22;%0A
     priv
@@ -2161,20 +2161,17 @@
 MS kms;%0A
-    
+%0A
 %0A    pub
@@ -2208,22 +2208,25 @@
     
- 
 this(
+init
 AmazonS3
 Clie
@@ -2213,32 +2213,42 @@
 his(initAmazonS3
+(), AWSKMS
 ClientBuilder.de
@@ -2260,24 +2260,317 @@
 Client()
-, AWSKMS
+);%0A    %7D%0A    %0A    private static AmazonS3 initAmazonS3() %7B%0A        String s3Region = System.getProperty(ATHENZ_PROP_AWS_S3_REGION);%0A        if (null != s3Region && !s3Region.isEmpty()) %7B%0A            return AmazonS3ClientBuilder.standard().withRegion(s3Region).build();%0A       %7D%0A        return AmazonS3
 ClientBu
@@ -2582,33 +2582,32 @@
 .defaultClient()
-)
 ;%0A    %7D%0A    %0A   

add hooks to specify region in AwsPrivateKeyStore (#254)