org.codice.ddf.commands.catalog.SubjectCommands.java Source code

Java tutorial

Introduction

Here is the source code for org.codice.ddf.commands.catalog.SubjectCommands.java

Source

/**
 * Copyright (c) Codice Foundation
 * <p/>
 * This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser
 * General Public License as published by the Free Software Foundation, either version 3 of the
 * License, or any later version.
 * <p/>
 * This program 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
 * Lesser General Public License for more details. A copy of the GNU Lesser General Public License
 * is distributed along with this program and can be found at
 * <http://www.gnu.org/licenses/lgpl.html>.
 */
package org.codice.ddf.commands.catalog;

import static org.apache.commons.lang.StringUtils.isNotBlank;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;

import org.apache.felix.gogo.commands.Option;
import org.apache.shiro.subject.ExecutionException;
import org.apache.shiro.subject.Subject;
import org.codice.ddf.security.common.Security;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ddf.security.service.SecurityServiceException;

/**
 * SubjectCommands provides the ability to change what subject (user) the extending command is run as.
 * If no user is specified and the user has the admin role, the command will execute as the system
 * user otherwise it will fail.
 */
public abstract class SubjectCommands extends CommandSupport {

    private static final Logger LOGGER = LoggerFactory.getLogger(SubjectCommands.class);

    private final Security security;

    @Option(name = "--user", required = false, aliases = {
            "-u" }, multiValued = false, description = "Run command as a different user")
    protected String user = null;

    protected SubjectCommands() {
        this(Security.getInstance());
    }

    SubjectCommands(Security security) {
        this.security = security;
    }

    /**
     * Executes the command once the user has been properly authorized.
     *
     * @return result of the command execution
     * @throws Exception if the command failed to run
     */
    protected abstract Object executeWithSubject() throws Exception;

    /**
     * Executes the command using the user name provided using the {@code --user} option and
     * prompts for a password. If no user name was provided, tries to run the command using the
     * current {@link Subject}, or elevates to the system subject is the user has permission to do
     * so.
     * <p/>
     * An error message will be printed out if the user name/password combination is invalid or
     * if the user doesn't have the permission to run the command.
     *
     * @return value returned by {@link #executeWithSubject()}, or {@code null} if the command failed
     * @throws InvocationTargetException thrown if an exception occurred while executing
     *                                   the command
     * @throws Exception                 if any other unexpected exception occurred
     */
    @Override
    protected Object doExecute() throws Exception {

        try {
            if (isNotBlank(user)) {
                return runWithUserName();
            } else {
                try {
                    return security.runWithSubjectOrElevate(this::executeWithSubject);
                } catch (SecurityServiceException e) {
                    printErrorMessage(e.getMessage());
                    return null;
                }
            }
        } catch (InvocationTargetException e) {
            printErrorMessage(e.getCause().getMessage());
            return null;
        }
    }

    private Object runWithUserName() throws InvocationTargetException {
        try {
            String password = getLine("Password for " + user + ": ", false);
            Subject subject = security.getSubject(user, password);

            if (subject == null) {
                printErrorMessage("Invalid username/password");
                return null;
            }

            return subject.execute(this::executeWithSubject);
        } catch (ExecutionException e) {
            LOGGER.error("Failed to run command: {}", e.getCause().getMessage(), e.getCause());
            throw new InvocationTargetException(e.getCause());
        } catch (IOException e) {
            LOGGER.error("Failed to run command", e);
            printErrorMessage("Failed to read password");
        }

        return null;
    }

    private String getLine(String text, boolean showCharacters) throws IOException {
        console.print(text);
        console.flush();
        StringBuilder buffer = new StringBuilder();

        while (true) {
            int byteOfData = session.getKeyboard().read();

            if (byteOfData < 0) {
                break;
            }

            if (showCharacters) {
                console.print((char) byteOfData);
                console.flush();
            } else {
                console.print("*");
                console.flush();
            }

            if (byteOfData == '\r' || byteOfData == '\n') {
                break;
            }

            buffer.append((char) byteOfData);
        }

        console.println();
        console.flush();
        return buffer.toString();
    }
}