content: /*
   *  Copyright (C) 2014-2015 Stichting Akvo (Akvo Foundation)
   *
   *  This file is part of Akvo FLOW.
   *
   *  Akvo FLOW is free software: you can redistribute it and modify it under the terms of
   *  the GNU Affero General Public License (AGPL) as published by the Free Software Foundation,
   *  either version 3 of the License or any later version.
   *
   *  Akvo FLOW is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
   *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
   *  See the GNU Affero General Public License included below for more details.
   *
   *  The full license text can also be seen at <http://www.gnu.org/licenses/agpl.html>.
   */
  
  package org.akvo.flow;
  
  import java.io.File;
  import java.io.FileWriter;
  import java.io.StringWriter;
  import java.text.SimpleDateFormat;
  import java.util.Date;
  import java.util.HashMap;
  import java.util.Map;
  import java.util.UUID;
  
  import org.apache.commons.cli.CommandLine;
  import org.apache.commons.cli.CommandLineParser;
  import org.apache.commons.cli.DefaultParser;
  import org.apache.commons.cli.HelpFormatter;
  import org.apache.commons.cli.Option;
  import org.apache.commons.cli.Options;
  
  import com.amazonaws.auth.AWSCredentials;
  import com.amazonaws.auth.BasicAWSCredentials;
  import com.amazonaws.auth.policy.Policy;
  import com.amazonaws.services.identitymanagement.AmazonIdentityManagementClient;
  import com.amazonaws.services.identitymanagement.model.AccessKey;
  import com.amazonaws.services.identitymanagement.model.CreateAccessKeyRequest;
  import com.amazonaws.services.identitymanagement.model.CreateAccessKeyResult;
  import com.amazonaws.services.identitymanagement.model.CreateUserRequest;
  import com.amazonaws.services.identitymanagement.model.GetUserRequest;
  import com.amazonaws.services.identitymanagement.model.NoSuchEntityException;
  import com.amazonaws.services.identitymanagement.model.PutUserPolicyRequest;
  import com.amazonaws.services.s3.AmazonS3Client;
  import com.amazonaws.services.s3.model.Region;
  
  import freemarker.template.Configuration;
  import freemarker.template.DefaultObjectWrapper;
  import freemarker.template.Template;
  
  public class InstanceConfigurator {
  
      private static final String GAE_SUFFIX = "-gae";
      private static final String APK_SUFFIX = "-apk";
  
      public static void main(String[] args) throws Exception {
  
          Options opts = getOptions();
          CommandLineParser parser = new DefaultParser();
          CommandLine cli = null;
  
          try {
              cli = parser.parse(opts, args);
          } catch (Exception e) {
              System.err.println(e.getMessage());
              HelpFormatter formatter = new HelpFormatter();
              formatter.printHelp(InstanceConfigurator.class.getName(), opts);
              System.exit(1);
          }
  
          String awsAccessKey = cli.getOptionValue("ak");
          String awsSecret = cli.getOptionValue("as");
          String bucketName = cli.getOptionValue("bn");
          String gaeId = cli.getOptionValue("gae");
          String outFolder = cli.getOptionValue("o");
          String flowServices = cli.getOptionValue("fs");
          String eventNotification = cli.getOptionValue("en");
          String enableChangeEvents = cli.getOptionValue("ce", "false");
          String mapsProvider = cli.getOptionValue("mapsProvider", "mapbox");
          String googleMapsRegionBias = cli.getOptionValue("rb", "");
          String cartodbApiKey = cli.getOptionValue("ck", "");
          String cartodbSqlApi = cli.getOptionValue("cs", "");
          String cartodbHost = cli.getOptionValue("ch", "");
          String hereMapsAppId = cli.getOptionValue("hmai", "");
          String hereMapsAppCode = cli.getOptionValue("hmac", "");
          String alias = cli.getOptionValue("a");
          String emailFrom = cli.getOptionValue("ef");
          String emailTo = cli.getOptionValue("et");
          String orgName = cli.getOptionValue("on");
          String signingKey = cli.getOptionValue("sk");
          String oidcDomain = cli.getOptionValue("oidcDomain");
          String oidcClientId = cli.getOptionValue("oidcClientId");
          String oidcClientSecret = cli.getOptionValue("oidcClientSecret");
          String emailHost = cli.getOptionValue("emailHost");
          String emailPort = cli.getOptionValue("emailPort");
          String emailUser = cli.getOptionValue("emailUser");
          String emailPassword = cli.getOptionValue("emailPassword");
          String sentryDSN = cli.getOptionValue("sentryDSN");
  
  
          File configFileHome = new File(outFolder);
  
          if (! configFileHome.exists()) { configFileHome.mkdirs(); }
  
          Map<String, AccessKey> accessKeys = new HashMap<String, AccessKey>();
          String apiKey = UUID.randomUUID().toString().replaceAll("-", "");
  
          AWSCredentials creds = new BasicAWSCredentials(awsAccessKey, awsSecret);
          AmazonIdentityManagementClient iamClient = new AmazonIdentityManagementClient(creds);
          AmazonS3Client s3Client = new AmazonS3Client(creds);
  
          // Creating bucket
  
          System.out.println("Creating bucket: " + bucketName);
  
          try {
              if (s3Client.doesBucketExist(bucketName)) {
                  System.out.println(bucketName
                          + " already exists, skipping creation");
              } else {
                  s3Client.createBucket(bucketName, Region.EU_Ireland);
              }
          } catch (Exception e) {
              System.err.println("Error trying to create bucket " + bucketName
                      + " : " + e.getMessage());
              System.exit(1);
          }
  
          // Creating users and groups
  
          String gaeUser = bucketName + GAE_SUFFIX;
          String apkUser = bucketName + APK_SUFFIX;
  
          // GAE
  
          System.out.println("Creating user: " + gaeUser);
  
          GetUserRequest gaeUserRequest = new GetUserRequest();
          gaeUserRequest.setUserName(gaeUser);
  
          try {
              iamClient.getUser(gaeUserRequest);
              System.out.println("User already exists, skipping creation");
          } catch (NoSuchEntityException e) {
              iamClient.createUser(new CreateUserRequest(gaeUser));
          }
  
          System.out.println("Requesting security credentials for " + gaeUser);
  
          CreateAccessKeyRequest gaeAccessRequest = new CreateAccessKeyRequest();
          gaeAccessRequest.setUserName(gaeUser);
  
          CreateAccessKeyResult gaeAccessResult = iamClient.createAccessKey(gaeAccessRequest);
          accessKeys.put(gaeUser, gaeAccessResult.getAccessKey());
  
          // APK
  
          System.out.println("Creating user: " + apkUser);
  
          GetUserRequest apkUserRequest = new GetUserRequest();
          apkUserRequest.setUserName(apkUser);
  
          try {
              iamClient.getUser(apkUserRequest);
              System.out.println("User already exists, skipping creation");
          } catch (NoSuchEntityException e) {
              iamClient.createUser(new CreateUserRequest(apkUser));
          }
  
          System.out.println("Requesting security credentials for " + apkUser);
  
          CreateAccessKeyRequest apkAccessRequest = new CreateAccessKeyRequest();
          apkAccessRequest.setUserName(apkUser);
  
          CreateAccessKeyResult apkAccessResult = iamClient
                  .createAccessKey(apkAccessRequest);
          accessKeys.put(apkUser, apkAccessResult.getAccessKey());
  
          System.out.println("Configuring security policies...");
  
          Configuration cfg = new Configuration();
          cfg.setClassForTemplateLoading(InstanceConfigurator.class, "/org/akvo/flow/templates");
          cfg.setObjectWrapper(new DefaultObjectWrapper());
          cfg.setDefaultEncoding("UTF-8");
  
          Map<String, Object> data = new HashMap<String, Object>();
          data.put("bucketName", bucketName);
          data.put("version", new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
          data.put("accessKey", accessKeys);
  
          Template t0 = cfg.getTemplate("bucket-policy.ftl");
          StringWriter bucketPolicy = new StringWriter();
          t0.process(data, bucketPolicy);
  
          Template t1 = cfg.getTemplate("apk-s3-policy.ftl");
          StringWriter apkPolicy = new StringWriter();
          t1.process(data, apkPolicy);
  
          Template t2 = cfg.getTemplate("gae-s3-policy.ftl");
          StringWriter gaePolicy = new StringWriter();
          t2.process(data, gaePolicy);
  
          s3Client.setBucketPolicy(bucketName, bucketPolicy.toString());
  
          iamClient.putUserPolicy(new PutUserPolicyRequest(apkUser, apkUser,
                  Policy.fromJson(apkPolicy.toString()).toJson()));
  
          iamClient.putUserPolicy(new PutUserPolicyRequest(gaeUser, gaeUser,
                  Policy.fromJson(gaePolicy.toString()).toJson()));
  
          System.out.println("Creating configuration files...");
  
          // survey.properties
          Map<String, Object> apkData = new HashMap<String, Object>();
          apkData.put("awsBucket", bucketName);
          apkData.put("awsAccessKeyId", accessKeys.get(apkUser).getAccessKeyId());
          apkData.put("awsSecretKey", accessKeys.get(apkUser).getSecretAccessKey());
          apkData.put("serverBase", "https://" + gaeId + ".appspot.com");
          apkData.put("instanceUrl", "https://" + alias);
          apkData.put("restApiKey", apiKey);
  
          Template t3 = cfg.getTemplate("survey.properties.ftl");
          t3.process(apkData, new FileWriter(new File(configFileHome, "/survey.properties")));
  
          // appengine-web.xml
          Map<String, Object> webData = new HashMap<String, Object>();
          webData.put("awsBucket", bucketName);
          webData.put("awsAccessKeyId", accessKeys.get(gaeUser).getAccessKeyId());
          webData.put("awsSecretAccessKey", accessKeys.get(gaeUser).getSecretAccessKey());
          webData.put("s3url", "https://" + bucketName + ".s3.amazonaws.com");
          webData.put("instanceId", gaeId);
          webData.put("alias", alias);
          webData.put("flowServices", flowServices);
          webData.put("eventNotification", eventNotification);
          webData.put("enableChangeEvents", enableChangeEvents);
          webData.put("mapsProvider", mapsProvider);
          webData.put("googleMapsRegionBias", googleMapsRegionBias);
          webData.put("cartodbApiKey", cartodbApiKey);
          webData.put("cartodbSqlApi", cartodbSqlApi);
          webData.put("cartodbHost", cartodbHost);
          webData.put("hereMapsAppId", hereMapsAppId);
          webData.put("hereMapsAppCode", hereMapsAppCode);
          webData.put("apiKey", apiKey);
          webData.put("emailFrom", emailFrom);
          webData.put("emailTo", emailTo);
          webData.put("organization", orgName);
          webData.put("signingKey", signingKey);
          webData.put("oidcDomain", oidcDomain);
          webData.put("oidcClientId", oidcClientId);
          webData.put("oidcClientSecret", oidcClientSecret);
          webData.put("emailHost", emailHost);
          webData.put("emailPort", emailPort);
          webData.put("emailUser", emailUser);
          webData.put("emailPassword", emailPassword);
          webData.put("sentryDSN", sentryDSN);
  
  
          Template t5 = cfg.getTemplate("appengine-web.xml.ftl");
          t5.process(webData, new FileWriter(new File(configFileHome, "/appengine-web.xml")));
  
          System.out.println("Done");
      }
  
      private static Options getOptions() {
  
          Options options = new Options();
  
          Option orgName = new Option("on", "Organzation name");
          orgName.setLongOpt("orgName");
          orgName.setArgs(1);
          orgName.setRequired(true);
  
          Option awsId = new Option("ak", "AWS Access Key");
          awsId.setLongOpt("awsKey");
          awsId.setArgs(1);
          awsId.setRequired(true);
  
          Option awsSecret = new Option("as", "AWS Access Secret");
          awsSecret.setLongOpt("awsSecret");
          awsSecret.setArgs(1);
          awsSecret.setRequired(true);
  
          Option bucketName = new Option("bn", "AWS S3 bucket name");
          bucketName.setLongOpt("bucketName");
          bucketName.setArgs(1);
          bucketName.setRequired(true);
  
          Option gaeServer = new Option("gae",
                  "GAE instance id - The `x` in https://x.appspot.com");
          gaeServer.setLongOpt("gaeId");
          gaeServer.setArgs(1);
          gaeServer.setRequired(true);
  
          Option emailFrom = new Option("ef",
                  "Sender email - NOTE: Must be developer in GAE instance");
          emailFrom.setLongOpt("emailFrom");
          emailFrom.setArgs(1);
          emailFrom.setRequired(false);
  
          Option emailTo = new Option("et",
                  "Recipient email of error notifications");
          emailTo.setLongOpt("emailTo");
          emailTo.setArgs(1);
          emailTo.setRequired(true);
  
          Option flowServices = new Option("fs",
                  "FLOW Services url, e.g. http://services.akvoflow.org");
          flowServices.setLongOpt("flowServices");
          flowServices.setArgs(1);
          flowServices.setRequired(true);
  
          Option eventNotification = new Option(
                  "en",
                  "FLOW Services event notification endpoint, e.g. http://services.akvoflow.org:3030/event_notification");
          eventNotification.setLongOpt("eventNotification");
          eventNotification.setArgs(1);
          eventNotification.setRequired(true);
  
          Option enableChangeEvents = new Option(
                  "ce",
                  "true if the instance should store change event data and notify FLOW services event notification endpoint of new events");
          enableChangeEvents.setLongOpt("enableChangeEvents");
          enableChangeEvents.setArgs(1);
          enableChangeEvents.setRequired(false);
  
          Option mapsProvider = new Option("mp",
                  "The maps provider to use. One of 'mapbox', 'google', 'cartodb'");
          mapsProvider.setLongOpt("mapsProvider");
          mapsProvider.setArgs(1);
          mapsProvider.setRequired(false);
  
          Option googleMapsRegionBias = new Option("rb",
                  "Region bias code (only available for google maps layers)");
          googleMapsRegionBias.setLongOpt("googleMapsRegionBias");
          googleMapsRegionBias.setArgs(1);
          googleMapsRegionBias.setRequired(false);
  
          Option cartodbApiKey = new Option("ck", "Cartodb api key");
          cartodbApiKey.setLongOpt("cartodbApiKey");
          cartodbApiKey.setArgs(1);
          cartodbApiKey.setRequired(false);
  
          Option cartodbSqlApi = new Option("cs",
                  "Url endpoint for the cartodb sql api");
          cartodbSqlApi.setLongOpt("cartodbSqlApi");
          cartodbSqlApi.setArgs(1);
          cartodbSqlApi.setRequired(false);
  
          Option cartodbHost = new Option("ch", "Cartodb host");
          cartodbHost.setLongOpt("cartodbHost");
          cartodbHost.setArgs(1);
          cartodbHost.setRequired(false);
  
          Option hereMapsAppId = new Option("hmai", "Here maps app id");
          hereMapsAppId.setLongOpt("hereMapsAppId");
          hereMapsAppId.setArgs(1);
          hereMapsAppId.setRequired(false);
  
          Option hereMapsAppCode = new Option("hmac", "Here maps app code");
          hereMapsAppCode.setLongOpt("hereMapsAppCode");
          hereMapsAppCode.setArgs(1);
          hereMapsAppCode.setRequired(false);
  
          Option outputFolder = new Option("o",
                  "Output folder for configuration files");
          outputFolder.setLongOpt("outFolder");
          outputFolder.setArgs(1);
          outputFolder.setRequired(true);
  
          Option alias = new Option("a",
                  "Instance alias, e.g. instance.akvoflow.org");
          alias.setLongOpt("alias");
          alias.setArgs(1);
          alias.setRequired(true);
  
          Option signingKey = new Option("sk", "Signing Key");
          signingKey.setLongOpt("signingKey");
          signingKey.setArgs(1);
          signingKey.setRequired(true);
  
          Option sentryDSN = new Option("sentryDSN", "Sentry DSN");
          sentryDSN.setArgs(1);
          sentryDSN.setRequired(true);
  
  
          Option oidcDomain = new Option("oidcDomain", "ODIC IdP domain");
          oidcDomain.setArgs(1);
          oidcDomain.setRequired(true);
  
          Option oidcClientId = new Option("oidcClientId", "OIDC Client ID");
          oidcClientId.setArgs(1);
          oidcClientId.setRequired(true);
  
          Option oidcClientSecret = new Option("oidcClientSecret", "OIDC Client Secret");
          oidcClientSecret.setArgs(1);
          oidcClientSecret.setRequired(true);
  
          Option emailHost = new Option("emailHost", "SMTP server host");
          emailHost.setArgs(1);
          emailHost.setRequired(true);
  
          Option emailPort = new Option("emailPort", "SMTP server port");
          emailPort.setArgs(1);
          emailPort.setRequired(true);
  
          Option emailUser = new Option("emailUser", "SMTP username");
          emailUser.setArgs(1);
          emailUser.setRequired(true);
  
          Option emailPassword = new Option("emailPassword", "SMTP password");
          emailPassword.setArgs(1);
          emailPassword.setRequired(true);
  
          options.addOption(orgName);
          options.addOption(awsId);
          options.addOption(awsSecret);
          options.addOption(bucketName);
          options.addOption(gaeServer);
          options.addOption(emailFrom);
          options.addOption(emailTo);
          options.addOption(outputFolder);
          options.addOption(flowServices);
          options.addOption(eventNotification);
          options.addOption(enableChangeEvents);
          options.addOption(mapsProvider);
          options.addOption(googleMapsRegionBias);
          options.addOption(cartodbApiKey);
          options.addOption(cartodbSqlApi);
          options.addOption(cartodbHost);
          options.addOption(hereMapsAppId);
          options.addOption(hereMapsAppCode);
          options.addOption(alias);
          options.addOption(signingKey);
          options.addOption(sentryDSN);
          options.addOption(oidcDomain);
          options.addOption(oidcClientId);
          options.addOption(oidcClientSecret);
          options.addOption(emailHost);
          options.addOption(emailPort);
          options.addOption(emailUser);
          options.addOption(emailPassword);
  
          return options;
      }
  }
  