org.apache.hadoop.gateway.services.security.impl.DefaultMasterService.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.gateway.services.security.impl.DefaultMasterService.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.hadoop.gateway.services.security.impl;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.apache.commons.net.ntp.TimeStamp;
import org.apache.hadoop.gateway.config.GatewayConfig;
import org.apache.hadoop.gateway.services.ServiceLifecycleException;
import org.apache.hadoop.gateway.services.security.EncryptionResult;
import org.apache.hadoop.gateway.services.security.MasterService;

import java.io.Console;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

public class DefaultMasterService implements MasterService {

    private static final String MASTER_PASSPHRASE = "masterpassphrase";
    private static final String MASTER_PERSISTENCE_TAG = "#1.0# " + TimeStamp.getCurrentTime().toDateString();
    private char[] master = null;
    private AESEncryptor aes = new AESEncryptor(MASTER_PASSPHRASE);

    /* (non-Javadoc)
     * @see org.apache.hadoop.gateway.services.security.impl.MasterService#getMasterSecret()
     */
    @Override
    public char[] getMasterSecret() {
        // TODO: check permission call here
        return this.master;
    }

    @Override
    public void init(GatewayConfig config, Map<String, String> options) throws ServiceLifecycleException {
        // for testing only
        if (options.containsKey("master")) {
            this.master = options.get("master").toCharArray();
        } else {
            File masterFile = new File(
                    config.getGatewayHomeDir() + File.separator + "conf" + File.separator + "security", "master");
            if (masterFile.exists()) {
                try {
                    initializeFromMaster(masterFile);
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    throw new ServiceLifecycleException("Unable to load the persisted master secret.", e);
                }
            } else {
                if (options.get("persist-master").equals("true")) {
                    displayWarning(true);
                } else {
                    displayWarning(false);
                }
                promptUser();
                if (options.get("persist-master").equals("true")) {
                    persistMaster(master, masterFile);
                }
            }
        }
    }

    private void promptUser() {
        Console c = System.console();
        if (c == null) {
            System.err.println("No console.");
            System.exit(1);
        }

        boolean noMatch;
        do {
            char[] newPassword1 = c.readPassword("Enter master secret: ");
            char[] newPassword2 = c.readPassword("Enter master secret again: ");
            noMatch = !Arrays.equals(newPassword1, newPassword2);
            if (noMatch) {
                c.format("Passwords don't match. Try again.%n");
            } else {
                this.master = Arrays.copyOf(newPassword1, newPassword1.length);
            }
            Arrays.fill(newPassword1, ' ');
            Arrays.fill(newPassword2, ' ');
        } while (noMatch);
    }

    private void displayWarning(boolean persisting) {
        Console c = System.console();
        if (c == null) {
            System.err.println("No console.");
            System.exit(1);
        }
        if (persisting) {
            c.printf(
                    "***************************************************************************************************\n");
            c.printf(
                    "You have indicated that you would like to persist the master secret for this gateway instance.\n");
            c.printf("Be aware that this is less secure than manually entering the secret on startup.\n");
            c.printf("The persisted file will be encrypted and primarily protected through OS permissions.\n");
            c.printf(
                    "***************************************************************************************************\n");
        } else {
            c.printf(
                    "***************************************************************************************************\n");
            c.printf(
                    "Be aware that you will need to enter your master secret for future starts exactly as you do here.\n");
            c.printf("This secret is needed to access protected resources for the gateway process.\n");
            c.printf("The master secret must be protected, kept secret and not stored in clear text anywhere.\n");
            c.printf(
                    "***************************************************************************************************\n");
        }
    }

    private void persistMaster(char[] master, File masterFile) {
        EncryptionResult atom = encryptMaster(master);
        try {
            ArrayList<String> lines = new ArrayList<String>();
            lines.add(MASTER_PERSISTENCE_TAG);

            String line = Base64.encodeBase64String(
                    (Base64.encodeBase64String(atom.salt) + "::" + Base64.encodeBase64String(atom.iv) + "::"
                            + Base64.encodeBase64String(atom.cipher)).getBytes("UTF8"));
            lines.add(line);
            FileUtils.writeLines(masterFile, "UTF8", lines);

            // restrict os permissions to only the user running this process
            chmod("600", masterFile);
        } catch (IOException e) {
            // TODO log appropriate message that the master secret has not been persisted
            e.printStackTrace();
        }
    }

    private EncryptionResult encryptMaster(char[] master) {
        // TODO Auto-generated method stub
        try {
            return aes.encrypt(new String(master));
        } catch (Exception e) {
            // TODO log failed encryption attempt
            // need to ensure that we don't persist now
            e.printStackTrace();
        }
        return null;
    }

    private void initializeFromMaster(File masterFile) throws Exception {
        try {
            List<String> lines = FileUtils.readLines(masterFile, "UTF8");
            String tag = lines.get(0);
            // TODO: log - if appropriate - at least at finest level
            System.out.println("Loading from persistent master: " + tag);
            String line = new String(Base64.decodeBase64(lines.get(1)));
            String[] parts = line.split("::");
            //System.out.println("salt: " + parts[0] + " : " + Base64.decodeBase64(parts[0]));
            //System.out.println("iv: " + parts[1]);
            //System.out.println("cipher: " + parts[2]);
            this.master = new String(aes.decrypt(Base64.decodeBase64(parts[0]), Base64.decodeBase64(parts[1]),
                    Base64.decodeBase64(parts[2])), "UTF8").toCharArray();
        } catch (IOException e) {
            e.printStackTrace();
            throw e;
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            throw e;
        }
    }

    @Override
    public void start() throws ServiceLifecycleException {
    }

    @Override
    public void stop() throws ServiceLifecycleException {
    }

    private void chmod(String args, File file) throws IOException {
        // TODO: move to Java 7 NIO support to add windows as well
        // TODO: look into the following for Windows: Runtime.getRuntime().exec("attrib -r myFile");
        if (isUnixEnv()) {
            //args and file should never be null.
            if (args == null || file == null)
                throw new IOException("nullArg");
            if (!file.exists())
                throw new IOException("fileNotFound");

            // " +" regular expression for 1 or more spaces
            final String[] argsString = args.split(" +");
            List<String> cmdList = new ArrayList<String>();
            cmdList.add("/bin/chmod");
            cmdList.addAll(Arrays.asList(argsString));
            cmdList.add(file.getAbsolutePath());
            new ProcessBuilder(cmdList).start();
        }
    }

    private boolean isUnixEnv() {
        return (File.separatorChar == '/');
    }

}