From 0ed08613fbf5f1d026309b4ce8896bc380311759 Mon Sep 17 00:00:00 2001 From: Marek Goldmann <mgoldman@redhat.com> Date: Mon, 16 Apr 2012 18:23:15 +0200 Subject: [PATCH] Use properties in add-user AS7 module --- .../management/DomainManagementMessages.java | 51 +- .../management/security/AddPropertiesUser.java | 634 ++------------------- .../domain/management/security/ConsoleWrapper.java | 85 +++ .../as/domain/management/security/JavaConsole.java | 66 +++ .../management/security/PropertiesFileLoader.java | 60 +- .../domain/management/security/state/AddUser.java | 88 +++ .../security/state/ConfirmationChoice.java | 95 +++ .../security/state/DuplicateUserCheckState.java | 68 +++ .../management/security/state/ErrorState.java | 74 +++ .../security/state/PromptNewUserState.java | 114 ++++ .../security/state/PromptPasswordState.java | 86 +++ .../security/state/PropertyFileFinder.java | 190 ++++++ .../security/state/PropertyFilePrompt.java | 104 ++++ .../as/domain/management/security/state/State.java | 34 ++ .../management/security/state/StateValues.java | 142 +++++ .../security/state/UpdatePropertiesHandler.java | 111 ++++ .../management/security/state/UpdateUser.java | 88 +++ .../security/state/UserPropertiesFileHandler.java | 38 ++ .../management/security/state/WeakCheckState.java | 90 +++ 19 files changed, 1603 insertions(+), 615 deletions(-) create mode 100644 domain-management/src/main/java/org/jboss/as/domain/management/security/ConsoleWrapper.java create mode 100644 domain-management/src/main/java/org/jboss/as/domain/management/security/JavaConsole.java create mode 100644 domain-management/src/main/java/org/jboss/as/domain/management/security/state/AddUser.java create mode 100644 domain-management/src/main/java/org/jboss/as/domain/management/security/state/ConfirmationChoice.java create mode 100644 domain-management/src/main/java/org/jboss/as/domain/management/security/state/DuplicateUserCheckState.java create mode 100644 domain-management/src/main/java/org/jboss/as/domain/management/security/state/ErrorState.java create mode 100644 domain-management/src/main/java/org/jboss/as/domain/management/security/state/PromptNewUserState.java create mode 100644 domain-management/src/main/java/org/jboss/as/domain/management/security/state/PromptPasswordState.java create mode 100644 domain-management/src/main/java/org/jboss/as/domain/management/security/state/PropertyFileFinder.java create mode 100644 domain-management/src/main/java/org/jboss/as/domain/management/security/state/PropertyFilePrompt.java create mode 100644 domain-management/src/main/java/org/jboss/as/domain/management/security/state/State.java create mode 100644 domain-management/src/main/java/org/jboss/as/domain/management/security/state/StateValues.java create mode 100644 domain-management/src/main/java/org/jboss/as/domain/management/security/state/UpdatePropertiesHandler.java create mode 100644 domain-management/src/main/java/org/jboss/as/domain/management/security/state/UpdateUser.java create mode 100644 domain-management/src/main/java/org/jboss/as/domain/management/security/state/UserPropertiesFileHandler.java create mode 100644 domain-management/src/main/java/org/jboss/as/domain/management/security/state/WeakCheckState.java diff --git a/domain-management/src/main/java/org/jboss/as/domain/management/DomainManagementMessages.java b/domain-management/src/main/java/org/jboss/as/domain/management/DomainManagementMessages.java index c260e79..c9be048 100644 --- a/domain-management/src/main/java/org/jboss/as/domain/management/DomainManagementMessages.java +++ b/domain-management/src/main/java/org/jboss/as/domain/management/DomainManagementMessages.java @@ -488,9 +488,54 @@ public interface DomainManagementMessages { @Message(id = 15251, value = "Invalid response. (Valid responses are A, a, B, or b)") String invalidChoiceResponse(); - /* - * Logging IDs 15200 to 15299 are reserved for domain management, the file DomainManagementLogger also contains messages in - * this range commencing 15200. + /** + * Confirmation if the current user password and roles is about to be updated. + * + * @param user - The name of the user. + * + * @return a {@link String} for the message. + */ + @Message(value = "User '%s' already exits, would you like to update the existing user password and roles") + String aboutToUpdateUser(String user); + + /** + * Message to inform user that the user has been updated to the file identified. + * + * @param username - The new username. + * @param fileName - The file the user has been added to. + * + * @return a {@link String} for the message. */ + @Message(value = "Updated user '%s' to file '%s'") + String updateUser(String userName, String canonicalPath); + + + /** + * The error message if updating user to the file fails. + * + * @param file - The name of the file the add failed for. + * @param error - The failure message. + * + * @return a {@link String} for the message. + */ + @Message(id = 15254, value = "Unable to update user to %s due to error %s") + String unableToUpdateUser(String absolutePath, String message); + + /** + * Message to inform user that the user has been updated to the roles file identified. + * + * @param username - The new username. + * @param roles - The new roles. + * @param fileName - The file the user has been added to. + * + * @return a {@link String} for the message. + */ + @Message(value = "Updated user '%s' with roles %s to file '%s'") + String updatedRoles(String username, String roles, String fileName); + + /* + * Logging IDs 15200 to 15299 are reserved for domain management, the file DomainManagementLogger also contains messages in + * this range commencing 15200. + */ } diff --git a/domain-management/src/main/java/org/jboss/as/domain/management/security/AddPropertiesUser.java b/domain-management/src/main/java/org/jboss/as/domain/management/security/AddPropertiesUser.java index 405c80c..f58a799 100644 --- a/domain-management/src/main/java/org/jboss/as/domain/management/security/AddPropertiesUser.java +++ b/domain-management/src/main/java/org/jboss/as/domain/management/security/AddPropertiesUser.java @@ -22,27 +22,19 @@ package org.jboss.as.domain.management.security; -import static org.jboss.as.domain.management.DomainManagementMessages.MESSAGES; +import org.jboss.as.domain.management.security.state.PropertyFileFinder; +import org.jboss.as.domain.management.security.state.PropertyFilePrompt; +import org.jboss.as.domain.management.security.state.State; +import org.jboss.as.domain.management.security.state.StateValues; -import java.io.BufferedWriter; import java.io.Closeable; -import java.io.Console; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileReader; -import java.io.FileWriter; import java.io.IOException; import java.io.StringReader; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Properties; -import java.util.Set; -import org.jboss.sasl.util.UsernamePasswordHashUtil; +import static org.jboss.as.domain.management.DomainManagementMessages.MESSAGES; /** * A command line utility to add new users to the mgmt-users.properties files. @@ -51,39 +43,49 @@ import org.jboss.sasl.util.UsernamePasswordHashUtil; */ public class AddPropertiesUser { - private static final String[] BAD_USER_NAMES = {"admin", "administrator", "root"}; + public static final String[] BAD_USER_NAMES = {"admin", "administrator", "root"}; - private static final String DEFAULT_MANAGEMENT_REALM = "ManagementRealm"; - private static final String DEFAULT_APPLICATION_REALM = "ApplicationRealm"; + public static final String SERVER_BASE_DIR = "jboss.server.base.dir"; + public static final String SERVER_CONFIG_DIR = "jboss.server.config.dir"; + public static final String DOMAIN_BASE_DIR = "jboss.domain.base.dir"; + public static final String DOMAIN_CONFIG_DIR = "jboss.domain.config.dir"; + + public static final String DEFAULT_MANAGEMENT_REALM = "ManagementRealm"; + public static final String DEFAULT_APPLICATION_REALM = "ApplicationRealm"; public static final String MGMT_USERS_PROPERTIES = "mgmt-users.properties"; public static final String APPLICATION_USERS_PROPERTIES = "application-users.properties"; public static final String APPLICATION_ROLES_PROPERTIES = "application-roles.properties"; public static final String APPLICATION_USERS_SWITCH = "-a"; - private static final char NEW_LINE_CHAR = '\n'; private static final char CARRIAGE_RETURN_CHAR = '\r'; - private static final String NEW_LINE = "\n"; - private static final String SPACE = " "; + public static final String NEW_LINE = "\n"; + public static final String SPACE = " "; + private static final Properties argsCliProps = new Properties(); - private final Console theConsole = System.console(); - private List<File> propertiesFiles; - private List<File> roleFiles; - private Set<String> knownUsers; - private State nextState; + private ConsoleWrapper theConsole; - private AddPropertiesUser() { - if (theConsole == null) { + + protected State nextState; + + protected AddPropertiesUser() { + theConsole = new JavaConsole(); + if (theConsole.getConsole() == null) { throw MESSAGES.noConsoleAvailable(); } - nextState = new PropertyFilePrompt(); + nextState = new PropertyFilePrompt(theConsole); + } + + protected AddPropertiesUser(ConsoleWrapper console) { + this.theConsole = console; + nextState = new PropertyFilePrompt(theConsole); } private AddPropertiesUser(final boolean management, final String user, final char[] password, final String realm) { boolean silent = false; - Values values = new Values(); + StateValues stateValues = new StateValues(); String valueSilent = argsCliProps.getProperty("silent"); @@ -91,27 +93,27 @@ public class AddPropertiesUser { silent = Boolean.valueOf(valueSilent); } if (silent) { - values.howInteractive = Interactiveness.SILENT; + stateValues.setHowInteractive(Interactiveness.SILENT); } else { - values.howInteractive = Interactiveness.NON_INTERACTIVE; + stateValues.setHowInteractive(Interactiveness.NON_INTERACTIVE); } - if ((theConsole == null) && (values.isSilent() == false)) { + if ((theConsole == null) && (stateValues.isSilent() == false)) { throw MESSAGES.noConsoleAvailable(); } - values.userName = user; - values.password = password; - values.realm = realm; - values.management = management; + stateValues.setUserName(user); + stateValues.setPassword(password); + stateValues.setRealm(realm); + stateValues.setManagement(management); - nextState = new PropertyFileFinder(values); + nextState = new PropertyFileFinder(theConsole, stateValues); } private AddPropertiesUser(boolean management, final String user, final char[] password) { this(management, user, password, management ? DEFAULT_MANAGEMENT_REALM : DEFAULT_APPLICATION_REALM); } - private void run() { + protected void run() { while ((nextState = nextState.execute()) != null) { } } @@ -158,538 +160,6 @@ public class AddPropertiesUser { } } - - private class PropertyFilePrompt implements State { - - private static final int MANAGEMENT = 0; - private static final int APPLICATION = 1; - private static final int INVALID = 2; - - @Override - public State execute() { - - Values values = new Values(); - theConsole.printf(NEW_LINE); - theConsole.printf(MESSAGES.filePrompt()); - theConsole.printf(NEW_LINE); - - while (true) { - String temp = theConsole.readLine("(a): "); - if (temp == null) { - /* - * This will return user to the command prompt so add a new line to ensure the command prompt is on the next - * line. - */ - theConsole.printf(NEW_LINE); - return null; - } - - if (temp.length() > 0) { - switch (convertResponse(temp)) { - case MANAGEMENT: - values.management = true; - values.realm = DEFAULT_MANAGEMENT_REALM; - return new PropertyFileFinder(values); - case APPLICATION: - values.management = false; - values.realm = DEFAULT_APPLICATION_REALM; - return new PropertyFileFinder(values); - default: - return new ErrorState(MESSAGES.invalidChoiceResponse(), this); - } - } else { - values.management = true; - values.realm = DEFAULT_MANAGEMENT_REALM; - return new PropertyFileFinder(values); - } - } - } - - private int convertResponse(final String response) { - String temp = response.toLowerCase(); - if ("A".equals(temp) || "a".equals(temp)) { - return MANAGEMENT; - } - - if ("B".equals(temp) || "b".equals(temp)) { - return APPLICATION; - } - - return INVALID; - } - - } - - /** - * The first state executed, responsible for searching for the relevant properties files. - */ - private class PropertyFileFinder implements State { - - private final Values values; - - private PropertyFileFinder(final Values values) { - this.values = values; - } - - @Override - public State execute() { - String jbossHome = System.getenv("JBOSS_HOME"); - if (jbossHome == null) { - return new ErrorState(MESSAGES.jbossHomeNotSet(), null, values); - } - - List<File> foundFiles = new ArrayList<File>(2); - final String fileName = values.management ? MGMT_USERS_PROPERTIES : APPLICATION_USERS_PROPERTIES; - if (!findFiles(jbossHome, foundFiles, fileName)) { - return new ErrorState(MESSAGES.propertiesFileNotFound(fileName), null, values); - } - if(!values.management) { - List<File> foundRoleFiles = new ArrayList<File>(2); - if (!findFiles(jbossHome, foundRoleFiles, APPLICATION_ROLES_PROPERTIES)) { - return new ErrorState(MESSAGES.propertiesFileNotFound(APPLICATION_ROLES_PROPERTIES), null, values); - } - roleFiles = foundRoleFiles; - } - - propertiesFiles = foundFiles; - - Set<String> foundUsers = new HashSet<String>(); - for (File current : propertiesFiles) { - try { - foundUsers.addAll(loadUserNames(current)); - } catch (IOException e) { - return new ErrorState(MESSAGES.unableToLoadUsers(current.getAbsolutePath(), e.getMessage()), null, values); - } - } - knownUsers = foundUsers; - - if (values == null) { - return new PromptNewUserState(); - } else { - return new PromptNewUserState(values); - } - } - - private boolean findFiles(final String jbossHome, final List<File> foundFiles, final String fileName) { - File standaloneProps = new File(jbossHome + "/standalone/configuration/" + fileName); - if (standaloneProps.exists()) { - foundFiles.add(standaloneProps); - } - File domainProps = new File(jbossHome + "/domain/configuration/" + fileName); - if (domainProps.exists()) { - foundFiles.add(domainProps); - } - - if (foundFiles.size() == 0) { - return false; - } - return true; - } - - private Set<String> loadUserNames(final File file) throws IOException { - - FileInputStream fis = null; - try { - fis = new FileInputStream(file); - Properties tempProps = new Properties(); - tempProps.load(fis); - - return tempProps.stringPropertyNames(); - } finally { - safeClose(fis); - } - - } - - } - - /** - * State to prompt the user for the realm, username and password to use, this State can be called back to so allows for a - * pre-defined realm and username to be used. - */ - private class PromptNewUserState implements State { - private final Values values; - - PromptNewUserState() { - values = new Values(); - values.realm = DEFAULT_MANAGEMENT_REALM; - } - - PromptNewUserState(final Values values) { - this.values = values; - } - - @Override - public State execute() { - if (values.isSilentOrNonInteractive() == false) { - theConsole.printf(NEW_LINE); - theConsole.printf(MESSAGES.enterNewUserDetails()); - theConsole.printf(NEW_LINE); - values.password = null; // If interactive we want to be sure to capture this. - - /* - * Prompt for realm. - */ - theConsole.printf(MESSAGES.realmPrompt(values.realm)); - String temp = theConsole.readLine(" : "); - if (temp == null) { - /* - * This will return user to the command prompt so add a new line to - * ensure the command prompt is on the next line. - */ - theConsole.printf(NEW_LINE); - return null; - } - if (temp.length() > 0) { - values.realm = temp; - } - - /* - * Prompt for username. - */ - String existingUsername = values.userName; - String usernamePrompt = existingUsername == null ? MESSAGES.usernamePrompt() : - MESSAGES.usernamePrompt(existingUsername); - theConsole.printf(usernamePrompt); - temp = theConsole.readLine(" : "); - if (temp != null && temp.length() > 0) { - existingUsername = temp; - } - // The user could have pressed Ctrl-D, in which case we do not use the default value. - if (temp == null || existingUsername == null || existingUsername.length() == 0) { - return new ErrorState(MESSAGES.noUsernameExiting()); - } - values.userName = existingUsername; - - /* - * Prompt for password. - */ - theConsole.printf(MESSAGES.passwordPrompt()); - char[] tempChar = theConsole.readPassword(" : "); - if (tempChar == null || tempChar.length == 0) { - return new ErrorState(MESSAGES.noPasswordExiting()); - } - - theConsole.printf(MESSAGES.passwordConfirmationPrompt()); - char[] secondTempChar = theConsole.readPassword(" : "); - if (secondTempChar == null) { - secondTempChar = new char[0]; // If re-entry missed allow fall through to comparison. - } - - if (Arrays.equals(tempChar, secondTempChar) == false) { - return new ErrorState(MESSAGES.passwordMisMatch(), this); - } - values.password = tempChar; - - if(!values.management) { - theConsole.printf(MESSAGES.rolesPrompt()); - values.roles = theConsole.readLine(" : "); - } - } - - return new WeakCheckState(values); - } - - } - - /** - * State to check the strength of the values selected. - * <p/> - * TODO - Currently only very basic checks are performed, this could be updated to perform additional password strength - * checks. - */ - private class WeakCheckState implements State { - - private Values values; - - private WeakCheckState(Values values) { - this.values = values; - } - - @Override - public State execute() { - State retryState = values.isSilentOrNonInteractive() ? null : new PromptNewUserState(values); - - if (Arrays.equals(values.userName.toCharArray(), values.password)) { - return new ErrorState(MESSAGES.usernamePasswordMatch(), retryState); - } - - for (char currentChar : values.userName.toCharArray()) { - if ((Character.isLetter(currentChar) || Character.isDigit(currentChar)) == false) { - return new ErrorState(MESSAGES.usernameNotAlphaNumeric(), retryState); - } - } - - boolean weakUserName = false; - for (String current : BAD_USER_NAMES) { - if (current.equals(values.userName.toLowerCase())) { - weakUserName = true; - break; - } - } - - State continuingState = new DuplicateUserCheckState(values); - if (weakUserName && values.isSilentOrNonInteractive() == false) { - String message = MESSAGES.usernameEasyToGuess(values.userName); - String prompt = MESSAGES.sureToAddUser(values.userName); - State noState = new PromptNewUserState(values); - - return new ConfirmationChoice(message, prompt, continuingState, noState); - } - - return continuingState; - } - - } - - /** - * State to check that the user is not already defined in any of the resolved - * properties files. - */ - private class DuplicateUserCheckState implements State { - - private Values values; - - private DuplicateUserCheckState(final Values values) { - this.values = values; - } - - @Override - public State execute() { - if (knownUsers.contains(values.userName)) { - State continuing = values.isSilentOrNonInteractive() ? null : new PromptNewUserState(values); - - return new ErrorState(MESSAGES.duplicateUser(values.userName), continuing, values); - } - - State addState = new AddUser(values); - final State continuingState; - if (values.isSilentOrNonInteractive()) { - continuingState = addState; - } else { - String message = MESSAGES.aboutToAddUser(values.userName, values.realm); - String prompt = MESSAGES.isCorrectPrompt(); - - continuingState = new ConfirmationChoice(message, prompt, addState, new PromptNewUserState(values)); - } - - return continuingState; - } - - - } - - /** - * State to display a message to the user with option to confirm a choice. - * <p/> - * This state handles either a yes or no outcome and will loop with an error - * on invalid input. - */ - private class ConfirmationChoice implements State { - - private final String message; - private final String prompt; - private final State yesState; - private final State noState; - - private static final int YES = 0; - private static final int NO = 1; - private static final int INVALID = 2; - - private ConfirmationChoice(final String message, final String prompt, final State yesState, final State noState) { - this.message = message; - this.prompt = prompt; - this.yesState = yesState; - this.noState = noState; - } - - @Override - public State execute() { - if (message != null) { - theConsole.printf(message); - theConsole.printf(NEW_LINE); - } - - theConsole.printf(prompt); - String temp = theConsole.readLine(SPACE); - - switch (convertResponse(temp)) { - case YES: - return yesState; - case NO: - return noState; - default: - return new ErrorState(MESSAGES.invalidConfirmationResponse(), this); - } - } - - private int convertResponse(final String response) { - if (response != null) { - String temp = response.toLowerCase(); - if ("yes".equals(temp) || "y".equals(temp)) { - return YES; - } - - if ("no".equals(temp) || "n".equals(temp)) { - return NO; - } - } - - return INVALID; - } - - } - - /** - * State to perform the actual addition to the discovered properties files. - * <p/> - * By this time ALL validation should be complete, this State will only fail for IOExceptions encountered - * performing the actual writes. - */ - private class AddUser implements State { - - private final Values values; - - private AddUser(final Values values) { - this.values = values; - } - - @Override - public State execute() { - String entry; - - try { - String hash = new UsernamePasswordHashUtil().generateHashedHexURP(values.userName, values.realm, - values.password); - entry = values.userName + "=" + hash; - } catch (NoSuchAlgorithmException e) { - return new ErrorState(e.getMessage(), null, values); - } - - for (File current : propertiesFiles) { - try { - append(entry, current); - if (values.isSilent() == false) { - theConsole.printf(MESSAGES.addedUser(values.userName, current.getCanonicalPath())); - theConsole.printf(NEW_LINE); - } - } catch (IOException e) { - return new ErrorState(MESSAGES.unableToAddUser(current.getAbsolutePath(), e.getMessage()), null, values); - } - } - - if(!values.management && values.roles != null && values.roles.length() > 0) { - for (final File current : roleFiles) { - String role = values.userName + "=" + values.roles; - try { - append(role, current); - if (values.isSilent() == false) { - theConsole.printf(MESSAGES.addedRoles(values.userName, values.roles, current.getCanonicalPath())); - theConsole.printf(NEW_LINE); - } - } catch (IOException e) { - return new ErrorState(MESSAGES.unableToAddUser(current.getAbsolutePath(), e.getMessage()), null, values); - } - } - } - - /* - * At this point the files have been written and confirmation passed back so nothing else to do. - */ - return null; - } - - private boolean additionalNewLineNeeded(final File file) throws IOException { - FileReader fr = null; - - try { - fr = new FileReader(file); - char lastChar = 0x00; - char[] temp = new char[1024]; - - int read = -1; - while ((read = fr.read(temp)) > 0) { - lastChar = temp[read - 1]; - } - /* - * It is possible that the final line will also have some whitespace - in that case we want - * a new line otherwise the line we add could become indented. - * - * Depending on where the file was last written the character sequence for a new line can vary, - * if we see either of the characters used for a new line as the last character of the last line - * we assume a new line is already present in the file. - */ - return lastChar != NEW_LINE_CHAR && lastChar != CARRIAGE_RETURN_CHAR; - } finally { - safeClose(fr); - } - } - - private void append(final String entry, final File toFile) throws IOException { - FileWriter fw = null; - BufferedWriter bw = null; - - boolean additionalNewLineNeeded = additionalNewLineNeeded(toFile); - - try { - fw = new FileWriter(toFile, true); - bw = new BufferedWriter(fw); - - if (additionalNewLineNeeded) { - bw.newLine(); - } - - bw.append(entry); - bw.newLine(); - } finally { - safeClose(bw); - safeClose(fw); - } - } - } - - /** - * State to report an error to the user, optionally a nextState can be supplied so the process can continue even though an - * error has been reported. - */ - private class ErrorState implements State { - - private final State nextState; - private final String errorMessage; - private final Values values; - - private ErrorState(String errorMessage) { - this(errorMessage, null, null); - } - - private ErrorState(String errorMessage, State nextState) { - this(errorMessage, nextState, null); - } - - private ErrorState(String errorMessage, State nextState, Values values) { - this.errorMessage = errorMessage; - this.nextState = nextState; - this.values = values; - } - - @Override - public State execute() { - if ((values == null) || (values != null) && (values.isSilent() == false)) { - theConsole.printf(NEW_LINE); - theConsole.printf(" * "); - theConsole.printf(MESSAGES.errorHeader()); - theConsole.printf(" * "); - theConsole.printf(NEW_LINE); - - theConsole.printf(errorMessage); - theConsole.printf(NEW_LINE); - theConsole.printf(NEW_LINE); - } - return nextState; - } - - } - private static void safeClose(Closeable c) { if (c != null) { try { @@ -699,31 +169,7 @@ public class AddPropertiesUser { } } - private class Values { - private Interactiveness howInteractive = Interactiveness.INTERACTIVE; - private String realm; - private String userName; - private char[] password; - private boolean management; - private String roles; - - private boolean isSilentOrNonInteractive() { - return (howInteractive == Interactiveness.NON_INTERACTIVE) || isSilent(); - } - - private boolean isSilent() { - return (howInteractive == Interactiveness.SILENT); - } - } - - private enum Interactiveness { + public enum Interactiveness { SILENT, NON_INTERACTIVE, INTERACTIVE } - - private interface State { - - State execute(); - - } - } diff --git a/domain-management/src/main/java/org/jboss/as/domain/management/security/ConsoleWrapper.java b/domain-management/src/main/java/org/jboss/as/domain/management/security/ConsoleWrapper.java new file mode 100644 index 0000000..b4458fa --- /dev/null +++ b/domain-management/src/main/java/org/jboss/as/domain/management/security/ConsoleWrapper.java @@ -0,0 +1,85 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2012, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * 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 2.1 of + * the License, or (at your option) any later version. + * + * This software 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.as.domain.management.security; + +import java.io.IOError; +import java.util.IllegalFormatException; + +/** + * Wrap the console commands + * + * @author <a href="mailto:flemming.harms@gmail.com">Flemming Harms</a> + */ +public interface ConsoleWrapper<T> { + + /** + * Writes a formatted string to this console's output stream using + * the specified format string and arguments. + * see <a href="../util/Formatter.html#syntax">Format string syntax</a> + * @param fmt + * @param args + */ + T format(String fmt, Object ...args) throws IllegalFormatException; + + /** + * A convenience method to write a formatted string to this console's + * output stream using the specified format string and arguments. + * + * @param format + * @param args + * @throws IllegalStateException + */ + void printf(String format, Object ... args) throws IllegalFormatException; + + /** + * Provides a formatted prompt, then reads a single line of text from the + * console. + * + * @param fmt + * @param args + * @return A string containing the line read from the console, not + * including any line-termination characters, or <tt>null</tt> + * if an end of stream has been reached. + * @throws IOError + */ + String readLine(String fmt, Object ... args) throws IOError; + + /** + * Provides a formatted prompt, then reads a password or passphrase from + * the console with echoing disabled. + * + * @param fmt + * @param args + * @return A character array containing the password or passphrase read + * from the console. + * @throws IOError + */ + char[] readPassword(String fmt, Object ... args) throws IllegalFormatException, IOError; + + /** + * Return the console object + * + * @return Return the console object + */ + T getConsole(); +} diff --git a/domain-management/src/main/java/org/jboss/as/domain/management/security/JavaConsole.java b/domain-management/src/main/java/org/jboss/as/domain/management/security/JavaConsole.java new file mode 100644 index 0000000..964a26a --- /dev/null +++ b/domain-management/src/main/java/org/jboss/as/domain/management/security/JavaConsole.java @@ -0,0 +1,66 @@ +/* + * + * * JBoss, Home of Professional Open Source. + * * Copyright 2011, Red Hat, Inc., and individual contributors + * * as indicated by the @author tags. See the copyright.txt file in the + * * distribution for a full listing of individual contributors. + * * + * * 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 2.1 of + * * the License, or (at your option) any later version. + * * + * * This software 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. + * * + * * You should have received a copy of the GNU Lesser General Public + * * License along with this software; if not, write to the Free + * * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ + +package org.jboss.as.domain.management.security; + +import java.io.Console; +import java.io.IOError; +import java.util.IllegalFormatException; + +/** + * Describe the purpose + * + * @author <a href="mailto:flemming.harms@gmail.com">Flemming Harms</a> + */ +public class JavaConsole implements ConsoleWrapper<Console> { + + private Console theConsole = System.console(); + + public void JavaConsole() {} + + @Override + public Console format(String fmt, Object... args) throws IllegalFormatException { + return theConsole.format(fmt,args); + } + + @Override + public void printf(String format, Object... args) throws IllegalFormatException { + theConsole.printf(format,args); + } + + @Override + public String readLine(String fmt, Object... args) throws IOError { + return theConsole.readLine(fmt,args); + } + + @Override + public char[] readPassword(String fmt, Object... args) throws IllegalFormatException, IOError { + return theConsole.readPassword(fmt,args); + } + + @Override + public Console getConsole() { + return theConsole; + } +} diff --git a/domain-management/src/main/java/org/jboss/as/domain/management/security/PropertiesFileLoader.java b/domain-management/src/main/java/org/jboss/as/domain/management/security/PropertiesFileLoader.java index 97ca458..38737cb 100644 --- a/domain-management/src/main/java/org/jboss/as/domain/management/security/PropertiesFileLoader.java +++ b/domain-management/src/main/java/org/jboss/as/domain/management/security/PropertiesFileLoader.java @@ -22,24 +22,27 @@ package org.jboss.as.domain.management.security; -import static org.jboss.as.domain.management.DomainManagementLogger.ROOT_LOGGER; -import static org.jboss.as.domain.management.DomainManagementMessages.MESSAGES; +import org.jboss.msc.service.StartContext; +import org.jboss.msc.service.StartException; +import org.jboss.msc.service.StopContext; +import org.jboss.msc.value.InjectedValue; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.Closeable; import java.io.File; import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.FileReader; -import java.io.FileWriter; import java.io.IOException; -import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.nio.charset.Charset; +import java.util.Arrays; import java.util.Properties; -import org.jboss.msc.service.StartContext; -import org.jboss.msc.service.StartException; -import org.jboss.msc.service.StopContext; -import org.jboss.msc.value.InjectedValue; +import static org.jboss.as.domain.management.DomainManagementLogger.ROOT_LOGGER; +import static org.jboss.as.domain.management.DomainManagementMessages.MESSAGES; /** * The base class for services depending on loading a properties file, loads the properties on @@ -49,6 +52,7 @@ import org.jboss.msc.value.InjectedValue; */ public abstract class PropertiesFileLoader { + public static final char[] ESCAPE_ARRAY = new char[]{'='}; private final String path; private final InjectedValue<String> relativeTo = new InjectedValue<String>(); @@ -82,7 +86,7 @@ public abstract class PropertiesFileLoader { propertiesFile = null; } - protected Properties getProperties() throws IOException { + public Properties getProperties() throws IOException { /* * This method does attempt to minimise the effect of race conditions, however this is not overly critical as if you * have users attempting to authenticate at the exact point their details are added to the file there is also a chance @@ -99,7 +103,7 @@ public abstract class PropertiesFileLoader { if (loadReallyRequired) { ROOT_LOGGER.debugf("Reloading properties file '%s'", propertiesFile.getAbsolutePath()); Properties props = new Properties(); - InputStream is = new FileInputStream(propertiesFile); + InputStreamReader is = new InputStreamReader(new FileInputStream(propertiesFile), Charset.forName("UTF-8")); try { props.load(is); } finally { @@ -118,7 +122,7 @@ public abstract class PropertiesFileLoader { return properties; } - private synchronized void persistProperties() throws IOException { + public synchronized void persistProperties() throws IOException { Properties toSave = (Properties) properties.clone(); File backup = new File(propertiesFile.getCanonicalPath() + ".bak"); @@ -135,8 +139,7 @@ public abstract class PropertiesFileLoader { FileReader fr = new FileReader(backup); BufferedReader br = new BufferedReader(fr); - FileWriter fw = new FileWriter(propertiesFile); - BufferedWriter bw = new BufferedWriter(fw); + BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(propertiesFile),"UTF8")); try { String line; @@ -151,8 +154,9 @@ public abstract class PropertiesFileLoader { int equals = trimmed.indexOf('='); if (equals > 0) { String userName = trimmed.substring(0, equals); - if (toSave.contains(userName)) { - bw.append(userName + "=" + toSave.getProperty(userName)); + if (toSave.containsKey(userName)) { + String escapedUserName = escapeString(userName, ESCAPE_ARRAY); + bw.append(escapedUserName + "=" + toSave.getProperty(userName)); bw.newLine(); toSave.remove(userName); } @@ -162,18 +166,38 @@ public abstract class PropertiesFileLoader { // Append any additional users to the end of the file. for (Object currentKey : toSave.keySet()) { - bw.append(currentKey + "=" + toSave.getProperty((String) currentKey)); + String escapedUserName = escapeString((String) currentKey, ESCAPE_ARRAY); + bw.append(escapedUserName + "=" + toSave.getProperty((String) currentKey)); bw.newLine(); } - bw.newLine(); } finally { safeClose(bw); - safeClose(fw); safeClose(br); safeClose(fr); } } + + public static String escapeString(String name, char[] escapeArray) { + Arrays.sort(escapeArray); + for(int i = 0; i < name.length(); ++i) { + char ch = name.charAt(i); + if(Arrays.binarySearch(escapeArray,ch) >=0 ) { + StringBuilder builder = new StringBuilder(); + builder.append(name, 0, i); + builder.append('\\').append(ch); + for(int j = i + 1; j < name.length(); ++j) { + ch = name.charAt(j); + if(Arrays.binarySearch(escapeArray,ch)>0) { + builder.append('\\'); + } + builder.append(ch); + } + return builder.toString(); + } + } + return name; + } private void safeClose(final Closeable c) { try { c.close(); diff --git a/domain-management/src/main/java/org/jboss/as/domain/management/security/state/AddUser.java b/domain-management/src/main/java/org/jboss/as/domain/management/security/state/AddUser.java new file mode 100644 index 0000000..58e92ed --- /dev/null +++ b/domain-management/src/main/java/org/jboss/as/domain/management/security/state/AddUser.java @@ -0,0 +1,88 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2011, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * 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 2.1 of + * the License, or (at your option) any later version. + * + * This software 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + + +package org.jboss.as.domain.management.security.state; + +import org.jboss.as.domain.management.security.ConsoleWrapper; +import org.jboss.msc.service.StartException; + +import java.io.File; +import java.io.IOException; +import java.util.Properties; + +import static org.jboss.as.domain.management.DomainManagementMessages.MESSAGES; + +/** + * State to perform the actual addition to the discovered properties files. + * <p/> + * By this time ALL validation should be complete, this State will only fail for IOExceptions encountered + * performing the actual writes. + */ +public class AddUser extends UpdatePropertiesHandler implements State { + + private final StateValues stateValues; + + public AddUser(ConsoleWrapper theConsole, final StateValues stateValues) { + super(theConsole); + this.stateValues = stateValues; + } + + @Override + public State execute() { + /* + * At this point the files have been written and confirmation passed back so nothing else to do. + */ + return update(stateValues); + } + + @Override + void persist(String[] entry, File file) throws IOException { + UserPropertiesFileHandler propertiesHandler = new UserPropertiesFileHandler(file.getAbsolutePath()); + try { + propertiesHandler.start(null); + Properties prob = propertiesHandler.getProperties(); + prob.setProperty(entry[0], entry[1]); + propertiesHandler.persistProperties(); + } catch (StartException e) { + throw new IllegalStateException(MESSAGES.unableToAddUser(file.getAbsolutePath(), e.getMessage())); + } finally { + propertiesHandler.stop(null); + } + + } + + @Override + String consoleUserMessage(String filePath) { + return MESSAGES.addedUser(stateValues.getUserName(), filePath); + } + + @Override + String consoleRolesMessage(String filePath) { + return MESSAGES.addedRoles(stateValues.getUserName(), stateValues.getRoles(), filePath); + } + + @Override + String errorMessage(String filePath, Throwable e) { + return MESSAGES.unableToAddUser(filePath, e.getMessage()); + } +} diff --git a/domain-management/src/main/java/org/jboss/as/domain/management/security/state/ConfirmationChoice.java b/domain-management/src/main/java/org/jboss/as/domain/management/security/state/ConfirmationChoice.java new file mode 100644 index 0000000..fffa6cf --- /dev/null +++ b/domain-management/src/main/java/org/jboss/as/domain/management/security/state/ConfirmationChoice.java @@ -0,0 +1,95 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2011, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * 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 2.1 of + * the License, or (at your option) any later version. + * + * This software 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.as.domain.management.security.state; + +import org.jboss.as.domain.management.security.ConsoleWrapper; + +import static org.jboss.as.domain.management.DomainManagementMessages.MESSAGES; +import static org.jboss.as.domain.management.security.AddPropertiesUser.NEW_LINE; +import static org.jboss.as.domain.management.security.AddPropertiesUser.SPACE; + +/** + * State to display a message to the user with option to confirm a choice. + * <p/> + * This state handles either a yes or no outcome and will loop with an error + * on invalid input. + */ +public class ConfirmationChoice implements State { + + private ConsoleWrapper theConsole; + private final String message; + private final String prompt; + private final State yesState; + private final State noState; + + private static final int YES = 0; + private static final int NO = 1; + private static final int INVALID = 2; + + public ConfirmationChoice(ConsoleWrapper theConsole,final String message, final String prompt, final State yesState, final State noState) { + this.theConsole = theConsole; + this.message = message; + this.prompt = prompt; + this.yesState = yesState; + this.noState = noState; + if (theConsole.getConsole() == null) { + throw MESSAGES.noConsoleAvailable(); + } + } + + @Override + public State execute() { + if (message != null) { + theConsole.printf(message); + theConsole.printf(NEW_LINE); + } + + theConsole.printf(prompt); + String temp = theConsole.readLine(SPACE); + + switch (convertResponse(temp)) { + case YES: + return yesState; + case NO: + return noState; + default: + return new ErrorState(theConsole, MESSAGES.invalidConfirmationResponse(), this); + } + } + + private int convertResponse(final String response) { + if (response != null) { + String temp = response.toLowerCase(); + if ("yes".equals(temp) || "y".equals(temp)) { + return YES; + } + + if ("no".equals(temp) || "n".equals(temp)) { + return NO; + } + } + + return INVALID; + } + +} diff --git a/domain-management/src/main/java/org/jboss/as/domain/management/security/state/DuplicateUserCheckState.java b/domain-management/src/main/java/org/jboss/as/domain/management/security/state/DuplicateUserCheckState.java new file mode 100644 index 0000000..a222e15 --- /dev/null +++ b/domain-management/src/main/java/org/jboss/as/domain/management/security/state/DuplicateUserCheckState.java @@ -0,0 +1,68 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2011, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * 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 2.1 of + * the License, or (at your option) any later version. + * + * This software 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jboss.as.domain.management.security.state; + +import org.jboss.as.domain.management.security.ConsoleWrapper; + +import static org.jboss.as.domain.management.DomainManagementMessages.MESSAGES; + +/** + * State to check that the user is not already defined in any of the resolved + * properties files. + */ +public class DuplicateUserCheckState implements State { + + private final ConsoleWrapper theConsole; + private StateValues stateValues; + + public DuplicateUserCheckState(final ConsoleWrapper theConsole,final StateValues stateValues) { + this.theConsole = theConsole; + this.stateValues = stateValues; + if (theConsole.getConsole() == null) { + throw MESSAGES.noConsoleAvailable(); + } + } + + @Override + public State execute() { + final State continuingState; + if (stateValues.isExistingUser()) { + continuingState = new UpdateUser(theConsole, stateValues); + } else { + State addState = new AddUser(theConsole, stateValues); + + if (stateValues.isSilentOrNonInteractive()) { + continuingState = addState; + } else { + String message = MESSAGES.aboutToAddUser(stateValues.getUserName(), stateValues.getRealm()); + String prompt = MESSAGES.isCorrectPrompt(); + + continuingState = new ConfirmationChoice(theConsole,message, prompt, addState, new PromptNewUserState(theConsole, stateValues)); + } + } + + return continuingState; + } + + +} + diff --git a/domain-management/src/main/java/org/jboss/as/domain/management/security/state/ErrorState.java b/domain-management/src/main/java/org/jboss/as/domain/management/security/state/ErrorState.java new file mode 100644 index 0000000..6f875bc --- /dev/null +++ b/domain-management/src/main/java/org/jboss/as/domain/management/security/state/ErrorState.java @@ -0,0 +1,74 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2011, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * 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 2.1 of + * the License, or (at your option) any later version. + * + * This software 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.as.domain.management.security.state; + +import org.jboss.as.domain.management.security.ConsoleWrapper; +import static org.jboss.as.domain.management.security.AddPropertiesUser.NEW_LINE; +import static org.jboss.as.domain.management.DomainManagementMessages.MESSAGES; + +/** + * State to report an error to the user, optionally a nextState can be supplied so the process can continue even though an + * error has been reported. + */ +public class ErrorState implements State { + + private final State nextState; + private final String errorMessage; + private final StateValues stateValues; + private ConsoleWrapper theConsole; + + public ErrorState(ConsoleWrapper theConsole, String errorMessage) { + this(theConsole, errorMessage, null, null); + } + + public ErrorState(ConsoleWrapper theConsole, String errorMessage, State nextState) { + this(theConsole, errorMessage, nextState, null); + } + + public ErrorState(ConsoleWrapper theConsole, String errorMessage, State nextState, StateValues stateValues) { + this.errorMessage = errorMessage; + this.nextState = nextState; + this.stateValues = stateValues; + this.theConsole = theConsole; + if (theConsole.getConsole() == null) { + throw MESSAGES.noConsoleAvailable(); + } + } + + @Override + public State execute() { + if ((stateValues == null) || (stateValues != null) && (stateValues.isSilent() == false)) { + theConsole.printf(NEW_LINE); + theConsole.printf(" * "); + theConsole.printf(MESSAGES.errorHeader()); + theConsole.printf(" * "); + theConsole.printf(NEW_LINE); + + theConsole.printf(errorMessage); + theConsole.printf(NEW_LINE); + theConsole.printf(NEW_LINE); + } + return nextState; + } + +} diff --git a/domain-management/src/main/java/org/jboss/as/domain/management/security/state/PromptNewUserState.java b/domain-management/src/main/java/org/jboss/as/domain/management/security/state/PromptNewUserState.java new file mode 100644 index 0000000..f0b157a --- /dev/null +++ b/domain-management/src/main/java/org/jboss/as/domain/management/security/state/PromptNewUserState.java @@ -0,0 +1,114 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2011, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * 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 2.1 of + * the License, or (at your option) any later version. + * + * This software 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + + +package org.jboss.as.domain.management.security.state; + +import org.jboss.as.domain.management.security.ConsoleWrapper; + +import static org.jboss.as.domain.management.security.AddPropertiesUser.DEFAULT_MANAGEMENT_REALM; +import static org.jboss.as.domain.management.security.AddPropertiesUser.NEW_LINE; +import static org.jboss.as.domain.management.DomainManagementMessages.MESSAGES; + +/** + * State to prompt the user for the realm, username and password to use, this State can be called back to so allows for a + * pre-defined realm and username to be used. + */ +public class PromptNewUserState implements State { + private final StateValues stateValues; + private ConsoleWrapper theConsole; + + public PromptNewUserState(ConsoleWrapper theConsole) { + this.theConsole = theConsole; + stateValues = new StateValues(); + stateValues.setRealm(DEFAULT_MANAGEMENT_REALM); + } + + public PromptNewUserState(ConsoleWrapper theConsole, final StateValues stateValues) { + this.theConsole = theConsole; + this.stateValues = stateValues; + if (theConsole.getConsole() == null) { + throw MESSAGES.noConsoleAvailable(); + } + } + + @Override + public State execute() { + State continuingState = new PromptPasswordState(theConsole, stateValues); + if (stateValues.isSilentOrNonInteractive() == false) { + theConsole.printf(NEW_LINE); + theConsole.printf(MESSAGES.enterNewUserDetails()); + theConsole.printf(NEW_LINE); + stateValues.setPassword(null); // If interactive we want to be sure to capture this. + + /* + * Prompt for realm. + */ + theConsole.printf(MESSAGES.realmPrompt(stateValues.getRealm())); + String temp = theConsole.readLine(" : "); + if (temp == null) { + /* + * This will return user to the command prompt so add a new line to + * ensure the command prompt is on the next line. + */ + theConsole.printf(NEW_LINE); + return null; + } + if (temp.length() > 0) { + stateValues.setRealm(temp); + } + + /* + * Prompt for username. + */ + String existingUsername = stateValues.getUserName(); + String usernamePrompt = existingUsername == null ? MESSAGES.usernamePrompt() : + MESSAGES.usernamePrompt(existingUsername); + theConsole.printf(usernamePrompt); + temp = theConsole.readLine(" : "); + if (temp != null && temp.length() > 0) { + existingUsername = temp; + } + // The user could have pressed Ctrl-D, in which case we do not use the default value. + if (temp == null || existingUsername == null || existingUsername.length() == 0) { + return new ErrorState(theConsole,MESSAGES.noUsernameExiting()); + } + stateValues.setUserName(existingUsername); + + if (stateValues.getKnownUsers().contains(stateValues.getUserName())) { + State duplicateContinuing = stateValues.isSilentOrNonInteractive() ? null : new PromptNewUserState(theConsole, stateValues); + if (stateValues.isSilentOrNonInteractive()) { + continuingState = new ErrorState(theConsole, MESSAGES.duplicateUser(stateValues.getUserName()), duplicateContinuing, stateValues); + } else { + String message = MESSAGES.aboutToUpdateUser(stateValues.getUserName()); + String prompt = MESSAGES.isCorrectPrompt(); + + stateValues.setExistingUser(true); + continuingState = new ConfirmationChoice(theConsole,message, prompt, continuingState, duplicateContinuing); + } + } + } + + return continuingState; + } + +} \ No newline at end of file diff --git a/domain-management/src/main/java/org/jboss/as/domain/management/security/state/PromptPasswordState.java b/domain-management/src/main/java/org/jboss/as/domain/management/security/state/PromptPasswordState.java new file mode 100644 index 0000000..91ab15a --- /dev/null +++ b/domain-management/src/main/java/org/jboss/as/domain/management/security/state/PromptPasswordState.java @@ -0,0 +1,86 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2011, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * 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 2.1 of + * the License, or (at your option) any later version. + * + * This software 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.as.domain.management.security.state; + +import org.jboss.as.domain.management.security.ConsoleWrapper; + +import java.util.Arrays; + +import static org.jboss.as.domain.management.DomainManagementMessages.MESSAGES; + +/** + * State to prompt the user for a password + * <p/> + * This state handles password validation by let the user re-enter the password + * in case of the password mismatch the user will be present for an error + * and will re-enter the PromptPasswordState again + */ +public class PromptPasswordState implements State { + + private ConsoleWrapper theConsole; + private StateValues stateValues; + + public PromptPasswordState(ConsoleWrapper theConsole, StateValues stateValues) { + this.theConsole = theConsole; + this.stateValues = stateValues; + if (theConsole.getConsole() == null) { + throw MESSAGES.noConsoleAvailable(); + } + } + + @Override + public State execute() { + State continuingState = new WeakCheckState(theConsole, stateValues); + if (stateValues.isSilentOrNonInteractive() == false) { + /* + * Prompt for password. + */ + theConsole.printf(MESSAGES.passwordPrompt()); + char[] tempChar = theConsole.readPassword(" : "); + if (tempChar == null || tempChar.length == 0) { + return new ErrorState(theConsole,MESSAGES.noPasswordExiting()); + } + + theConsole.printf(MESSAGES.passwordConfirmationPrompt()); + char[] secondTempChar = theConsole.readPassword(" : "); + if (secondTempChar == null) { + secondTempChar = new char[0]; // If re-entry missed allow fall through to comparison. + } + + if (Arrays.equals(tempChar, secondTempChar) == false) { + return new ErrorState(theConsole, MESSAGES.passwordMisMatch(), this); + } + stateValues.setPassword(tempChar); + + if (!stateValues.isManagement()) { + theConsole.printf(MESSAGES.rolesPrompt()); + String userRoles = stateValues.getKnownRoles().get(stateValues.getUserName()); + stateValues.setRoles(theConsole.readLine("[%1$2s]: ", (userRoles == null?"":userRoles))); + } + } + + return continuingState; + + } +} + diff --git a/domain-management/src/main/java/org/jboss/as/domain/management/security/state/PropertyFileFinder.java b/domain-management/src/main/java/org/jboss/as/domain/management/security/state/PropertyFileFinder.java new file mode 100644 index 0000000..22f57f3 --- /dev/null +++ b/domain-management/src/main/java/org/jboss/as/domain/management/security/state/PropertyFileFinder.java @@ -0,0 +1,190 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2011, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * 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 2.1 of + * the License, or (at your option) any later version. + * + * This software 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.as.domain.management.security.state; + +/** + * Describe the purpose + * + * @author <a href="mailto:flemming.harms@gmail.com">Flemming Harms</a> + */ + +import org.jboss.as.domain.management.security.ConsoleWrapper; +import org.jboss.as.domain.management.security.PropertiesFileLoader; +import org.jboss.msc.service.StartException; + +import java.io.Closeable; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import static org.jboss.as.domain.management.security.AddPropertiesUser.MGMT_USERS_PROPERTIES; +import static org.jboss.as.domain.management.security.AddPropertiesUser.APPLICATION_USERS_PROPERTIES; +import static org.jboss.as.domain.management.security.AddPropertiesUser.APPLICATION_ROLES_PROPERTIES; +import static org.jboss.as.domain.management.security.AddPropertiesUser.SERVER_CONFIG_DIR; +import static org.jboss.as.domain.management.security.AddPropertiesUser.SERVER_BASE_DIR; +import static org.jboss.as.domain.management.security.AddPropertiesUser.DOMAIN_BASE_DIR; +import static org.jboss.as.domain.management.security.AddPropertiesUser.DOMAIN_CONFIG_DIR; + +import static org.jboss.as.domain.management.DomainManagementMessages.MESSAGES; + +/** + * The first state executed, responsible for searching for the relevant properties files. + */ +public class PropertyFileFinder implements State { + + private ConsoleWrapper theConsole; + private final StateValues stateValues; + + public PropertyFileFinder(ConsoleWrapper theConsole,final StateValues stateValues) { + this.theConsole = theConsole; + this.stateValues = stateValues; + if (theConsole.getConsole() == null) { + throw MESSAGES.noConsoleAvailable(); + } + } + + @Override + public State execute() { + stateValues.setKnownRoles(new HashMap<String, String>()); + String jbossHome = System.getenv("JBOSS_HOME"); + if (jbossHome == null) { + return new ErrorState(theConsole, MESSAGES.jbossHomeNotSet(), null, stateValues); + } + + List<File> foundFiles = new ArrayList<File>(2); + final String fileName = stateValues.isManagement() ? MGMT_USERS_PROPERTIES : APPLICATION_USERS_PROPERTIES; + if (!findFiles(jbossHome, foundFiles, fileName)) { + return new ErrorState(theConsole, MESSAGES.propertiesFileNotFound(fileName), null, stateValues); + } + if (!stateValues.isManagement()) { + List<File> foundRoleFiles = new ArrayList<File>(2); + if (!findFiles(jbossHome, foundRoleFiles, APPLICATION_ROLES_PROPERTIES)) { + return new ErrorState(theConsole, MESSAGES.propertiesFileNotFound(APPLICATION_ROLES_PROPERTIES), null, stateValues); + } + stateValues.setRoleFiles(foundRoleFiles); + try { + stateValues.setKnownRoles(loadAllRoles(foundRoleFiles)); + } catch (Exception e) { + return new ErrorState(theConsole, MESSAGES.propertiesFileNotFound(APPLICATION_ROLES_PROPERTIES), null, stateValues); + } + } + + stateValues.setPropertiesFiles(foundFiles); + + Set<String> foundUsers = new HashSet<String>(); + for (File current : stateValues.getPropertiesFiles()) { + try { + foundUsers.addAll(loadUserNames(current)); + } catch (IOException e) { + return new ErrorState(theConsole, MESSAGES.unableToLoadUsers(current.getAbsolutePath(), e.getMessage()), null, stateValues); + } + } + stateValues.setKnownUsers(foundUsers); + + if (stateValues == null) { + return new PromptNewUserState(theConsole); + } else { + return new PromptNewUserState(theConsole, stateValues); + } + } + + private Map<String,String> loadAllRoles(List<File> foundRoleFiles) throws StartException, IOException { + Map<String, String> loadedRoles = new HashMap<String, String>(); + for (File file : foundRoleFiles) { + PropertiesFileLoader propertiesLoad = null; + try { + propertiesLoad = new UserPropertiesFileHandler(file.getCanonicalPath()); + propertiesLoad.start(null); + loadedRoles.putAll((Map) propertiesLoad.getProperties()); + } finally { + if (propertiesLoad!=null) { + propertiesLoad.stop(null); + } + } + } + return loadedRoles; + } + + private boolean findFiles(final String jbossHome, final List<File> foundFiles, final String fileName) { + + File standaloneProps = buildFilePath(jbossHome, SERVER_CONFIG_DIR, SERVER_BASE_DIR, "standalone", fileName); + if (standaloneProps.exists()) { + foundFiles.add(standaloneProps); + } + File domainProps = buildFilePath(jbossHome, DOMAIN_CONFIG_DIR, DOMAIN_BASE_DIR, "domain", fileName); + if (domainProps.exists()) { + foundFiles.add(domainProps); + } + + if (foundFiles.size() == 0) { + return false; + } + return true; + } + + private File buildFilePath(final String jbossHome, final String serverConfigDirPropertyName, + final String serverBaseDirPropertyName, final String defaultBaseDir, final String fileName) { + + String configDirConfiguredPath = System.getProperty(serverConfigDirPropertyName); + File configDir = configDirConfiguredPath != null ? new File(configDirConfiguredPath) : null; + if(configDir == null) { + String baseDirConfiguredPath = System.getProperty(serverBaseDirPropertyName); + File baseDir = baseDirConfiguredPath != null ? new File(baseDirConfiguredPath) : new File(jbossHome, defaultBaseDir); + configDir = new File(baseDir, "configuration"); + } + return new File(configDir, fileName); + } + + private Set<String> loadUserNames(final File file) throws IOException { + + InputStreamReader fis = null; + try { + fis = new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8")); + Properties tempProps = new Properties(); + tempProps.load(fis); + + return tempProps.stringPropertyNames(); + } finally { + safeClose(fis); + } + + } + + private static void safeClose(Closeable c) { + if (c != null) { + try { + c.close(); + } catch (IOException e) { + } + } + } + +} diff --git a/domain-management/src/main/java/org/jboss/as/domain/management/security/state/PropertyFilePrompt.java b/domain-management/src/main/java/org/jboss/as/domain/management/security/state/PropertyFilePrompt.java new file mode 100644 index 0000000..df3bbed --- /dev/null +++ b/domain-management/src/main/java/org/jboss/as/domain/management/security/state/PropertyFilePrompt.java @@ -0,0 +1,104 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2011, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * 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 2.1 of + * the License, or (at your option) any later version. + * + * This software 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jboss.as.domain.management.security.state; + +import org.jboss.as.domain.management.security.AddPropertiesUser; +import org.jboss.as.domain.management.security.ConsoleWrapper; +import static org.jboss.as.domain.management.security.AddPropertiesUser.DEFAULT_MANAGEMENT_REALM; +import static org.jboss.as.domain.management.security.AddPropertiesUser.DEFAULT_APPLICATION_REALM; + +import static org.jboss.as.domain.management.DomainManagementMessages.MESSAGES; + +/** +* Describe the purpose +* +* @author <a href="mailto:flemming.harms@gmail.com">Flemming Harms</a> +*/ +public class PropertyFilePrompt implements State { + + private static final int MANAGEMENT = 0; + private static final int APPLICATION = 1; + private static final int INVALID = 2; + + private ConsoleWrapper theConsole; + + public PropertyFilePrompt(ConsoleWrapper theConsole) { + this.theConsole = theConsole; + if (theConsole.getConsole() == null) { + throw MESSAGES.noConsoleAvailable(); + } + } + + @Override + public State execute() { + StateValues stateValues = new StateValues(); + + theConsole.printf(AddPropertiesUser.NEW_LINE); + theConsole.printf(MESSAGES.filePrompt()); + theConsole.printf(AddPropertiesUser.NEW_LINE); + + while (true) { + String temp = theConsole.readLine("(a): "); + if (temp == null) { + /* + * This will return user to the command prompt so add a new line to ensure the command prompt is on the next + * line. + */ + theConsole.printf(AddPropertiesUser.NEW_LINE); + return null; + } + + if (temp.length() > 0) { + switch (convertResponse(temp)) { + case MANAGEMENT: + stateValues.setManagement(true); + stateValues.setRealm(DEFAULT_MANAGEMENT_REALM); + return new PropertyFileFinder(theConsole, stateValues); + case APPLICATION: + stateValues.setManagement(false); + stateValues.setRealm(DEFAULT_APPLICATION_REALM); + return new PropertyFileFinder(theConsole, stateValues); + default: + return new ErrorState(theConsole, MESSAGES.invalidChoiceResponse(), this); + } + } else { + stateValues.setManagement(true); + stateValues.setRealm(DEFAULT_MANAGEMENT_REALM); + return new PropertyFileFinder(theConsole, stateValues); + } + } + } + + private int convertResponse(final String response) { + String temp = response.toLowerCase(); + if ("A".equals(temp) || "a".equals(temp)) { + return MANAGEMENT; + } + + if ("B".equals(temp) || "b".equals(temp)) { + return APPLICATION; + } + + return INVALID; + } + +} diff --git a/domain-management/src/main/java/org/jboss/as/domain/management/security/state/State.java b/domain-management/src/main/java/org/jboss/as/domain/management/security/state/State.java new file mode 100644 index 0000000..5095ab3 --- /dev/null +++ b/domain-management/src/main/java/org/jboss/as/domain/management/security/state/State.java @@ -0,0 +1,34 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2011, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * 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 2.1 of + * the License, or (at your option) any later version. + * + * This software 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.as.domain.management.security.state; + +/** + * Describe the purpose + * + * @author <a href="mailto:flemming.harms@gmail.com">Flemming Harms</a> + */ +public interface State { + + State execute(); + +} diff --git a/domain-management/src/main/java/org/jboss/as/domain/management/security/state/StateValues.java b/domain-management/src/main/java/org/jboss/as/domain/management/security/state/StateValues.java new file mode 100644 index 0000000..27cfdff --- /dev/null +++ b/domain-management/src/main/java/org/jboss/as/domain/management/security/state/StateValues.java @@ -0,0 +1,142 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2011, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * 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 2.1 of + * the License, or (at your option) any later version. + * + * This software 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.as.domain.management.security.state; + +import org.jboss.as.domain.management.security.AddPropertiesUser; + +import java.io.File; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** +* Place holder object to pass between the state +* +* @author <a href="mailto:flemming.harms@gmail.com">Flemming Harms</a> +*/ +public class StateValues { + private AddPropertiesUser.Interactiveness howInteractive = AddPropertiesUser.Interactiveness.INTERACTIVE; + private String realm; + private String userName; + private char[] password; + private boolean management; + private String roles; + private boolean existingUser = false; + private List<File> propertiesFiles; + private List<File> roleFiles; + private Set<String> knownUsers; + private Map<String,String> knownRoles; + + public boolean isSilentOrNonInteractive() { + return (howInteractive == AddPropertiesUser.Interactiveness.NON_INTERACTIVE) || isSilent(); + } + + public void setHowInteractive(AddPropertiesUser.Interactiveness howInteractive) { + this.howInteractive = howInteractive; + } + + + public boolean isSilent() { + return (howInteractive == AddPropertiesUser.Interactiveness.SILENT); + } + + public boolean isExistingUser() { + return existingUser; + } + + public void setExistingUser(boolean existingUser) { + this.existingUser = existingUser; + } + + public String getRealm() { + return realm; + } + + public void setRealm(String realm) { + this.realm = realm; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public char[] getPassword() { + return password; + } + + public void setPassword(char[] password) { + this.password = password; + } + + public boolean isManagement() { + return management; + } + + public void setManagement(boolean management) { + this.management = management; + } + + public String getRoles() { + return roles; + } + + public void setRoles(String roles) { + this.roles = roles; + } + + public List<File> getPropertiesFiles() { + return propertiesFiles; + } + + public void setPropertiesFiles(List<File> propertiesFiles) { + this.propertiesFiles = propertiesFiles; + } + + public List<File> getRoleFiles() { + return roleFiles; + } + + public void setRoleFiles(List<File> roleFiles) { + this.roleFiles = roleFiles; + } + + public Set<String> getKnownUsers() { + return knownUsers; + } + + public void setKnownUsers(Set<String> knownUsers) { + this.knownUsers = knownUsers; + } + + public Map<String, String> getKnownRoles() { + return knownRoles; + } + + public void setKnownRoles(Map<String, String> knownRoles) { + this.knownRoles = knownRoles; + } +} diff --git a/domain-management/src/main/java/org/jboss/as/domain/management/security/state/UpdatePropertiesHandler.java b/domain-management/src/main/java/org/jboss/as/domain/management/security/state/UpdatePropertiesHandler.java new file mode 100644 index 0000000..bbe8bb0 --- /dev/null +++ b/domain-management/src/main/java/org/jboss/as/domain/management/security/state/UpdatePropertiesHandler.java @@ -0,0 +1,111 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2011, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * 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 2.1 of + * the License, or (at your option) any later version. + * + * This software 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + + +package org.jboss.as.domain.management.security.state; + +import org.jboss.as.domain.management.security.ConsoleWrapper; +import org.jboss.sasl.util.UsernamePasswordHashUtil; + +import java.io.File; +import java.io.IOException; +import java.security.NoSuchAlgorithmException; + +import static org.jboss.as.domain.management.security.AddPropertiesUser.NEW_LINE; + +public abstract class UpdatePropertiesHandler { + + private ConsoleWrapper theConsole; + + public UpdatePropertiesHandler(ConsoleWrapper theConsole) { + this.theConsole = theConsole; + } + + /** + * Implement the persistence handler for storing the properties + */ + abstract void persist(String[] entry, File file) throws IOException; + + /** + * Customize message for update or add users + * @param fileName - the filename of the updated property file; + * @return the console message that should be present to the user + */ + abstract String consoleUserMessage(String fileName); + + /** + * Customize message for update or add roles + * @param fileName - the filename of the updated property file; + * @return the console message that should be present to the user + */ + abstract String consoleRolesMessage(String fileName); + + /** + * Customize error message for update or add users roles / properties + * @param fileName - the filename of the updated property file; + * @return the console message that should be present to the user + */ + abstract String errorMessage(String fileName, Throwable e); + + State update(StateValues stateValues) { + String[] entry = new String[2]; + + try { + String hash = new UsernamePasswordHashUtil().generateHashedHexURP(stateValues.getUserName(), stateValues.getRealm(), + stateValues.getPassword()); + entry[0] = stateValues.getUserName(); + entry[1] = hash; + } catch (NoSuchAlgorithmException e) { + return new ErrorState(theConsole, e.getMessage(), null, stateValues); + } + + for (File current : stateValues.getPropertiesFiles()) { + try { + persist(entry, current); + if (stateValues.isSilent() == false) { + theConsole.printf(consoleUserMessage(current.getCanonicalPath())); + theConsole.printf(NEW_LINE); + } + } catch (Exception e) { + return new ErrorState(theConsole, errorMessage(current.getAbsolutePath(), e), null, stateValues); + } + } + + if (!stateValues.isManagement() && stateValues.getRoles() != null) { + for (final File current : stateValues.getRoleFiles()) { + String[] role = {stateValues.getUserName(), stateValues.getRoles()}; + try { + persist(role, current); + if (stateValues.isSilent() == false) { + theConsole.printf(consoleRolesMessage(current.getCanonicalPath())); + theConsole.printf(NEW_LINE); + } + } catch (IOException e) { + return new ErrorState(theConsole, errorMessage(current.getAbsolutePath(), e), null, stateValues); + } + } + } + + return null; + } + +} diff --git a/domain-management/src/main/java/org/jboss/as/domain/management/security/state/UpdateUser.java b/domain-management/src/main/java/org/jboss/as/domain/management/security/state/UpdateUser.java new file mode 100644 index 0000000..d652995 --- /dev/null +++ b/domain-management/src/main/java/org/jboss/as/domain/management/security/state/UpdateUser.java @@ -0,0 +1,88 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2011, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * 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 2.1 of + * the License, or (at your option) any later version. + * + * This software 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.as.domain.management.security.state; + +import org.jboss.as.domain.management.security.ConsoleWrapper; +import org.jboss.msc.service.StartException; + +import java.io.File; +import java.io.IOException; +import java.util.Properties; + +import static org.jboss.as.domain.management.DomainManagementMessages.MESSAGES; + +/** + * Describe the purpose + * + * @author <a href="mailto:flemming.harms@gmail.com">Flemming Harms</a> + */ +public class UpdateUser extends UpdatePropertiesHandler implements State { + + private final StateValues stateValues; + + public UpdateUser(ConsoleWrapper theConsole,final StateValues stateValues) { + super(theConsole); + this.stateValues = stateValues; + } + + @Override + public State execute() { + /* + * At this point the files have been written and confirmation passed back so nothing else to do. + */ + return update(stateValues); + } + + @Override + void persist(String[] entry, File file) throws IOException { + UserPropertiesFileHandler propertiesHandler = new UserPropertiesFileHandler(file.getAbsolutePath()); + try { + propertiesHandler.start(null); + Properties prob = propertiesHandler.getProperties(); + prob.setProperty(entry[0], entry[1]); + propertiesHandler.persistProperties(); + } catch (StartException e) { + throw new IllegalStateException(MESSAGES.unableToUpdateUser(file.getAbsolutePath(), e.getMessage())); + } finally { + propertiesHandler.stop(null); + } + + } + + @Override + String consoleUserMessage(String fileName) { + return MESSAGES.updateUser(stateValues.getUserName(), fileName); + } + + @Override + String consoleRolesMessage(String fileName) { + return MESSAGES.updatedRoles(stateValues.getUserName(), stateValues.getRoles(), fileName); + } + + @Override + String errorMessage(String fileName, Throwable e) { + return MESSAGES.unableToUpdateUser(fileName, e.getMessage()); + } +} + + diff --git a/domain-management/src/main/java/org/jboss/as/domain/management/security/state/UserPropertiesFileHandler.java b/domain-management/src/main/java/org/jboss/as/domain/management/security/state/UserPropertiesFileHandler.java new file mode 100644 index 0000000..a3fb0ca --- /dev/null +++ b/domain-management/src/main/java/org/jboss/as/domain/management/security/state/UserPropertiesFileHandler.java @@ -0,0 +1,38 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2012, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * 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 2.1 of + * the License, or (at your option) any later version. + * + * This software 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + + +package org.jboss.as.domain.management.security.state; + +import org.jboss.as.domain.management.security.PropertiesFileLoader; + +/** +* Property file handler for user properties and roles +* +* @author <a href="mailto:flemming.harms@gmail.com">Flemming Harms</a> +*/ +class UserPropertiesFileHandler extends PropertiesFileLoader { + + UserPropertiesFileHandler(final String path) { + super(path); + } +} diff --git a/domain-management/src/main/java/org/jboss/as/domain/management/security/state/WeakCheckState.java b/domain-management/src/main/java/org/jboss/as/domain/management/security/state/WeakCheckState.java new file mode 100644 index 0000000..4c7d094 --- /dev/null +++ b/domain-management/src/main/java/org/jboss/as/domain/management/security/state/WeakCheckState.java @@ -0,0 +1,90 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2011, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * 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 2.1 of + * the License, or (at your option) any later version. + * + * This software 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.as.domain.management.security.state; + +import org.jboss.as.domain.management.security.ConsoleWrapper; + +import java.util.Arrays; +import static org.jboss.as.domain.management.security.AddPropertiesUser.BAD_USER_NAMES; +import static org.jboss.as.domain.management.DomainManagementMessages.MESSAGES; + +/** + * State to check the strength of the stateValues selected. + * <p/> + * TODO - Currently only very basic checks are performed, this could be updated to perform additional password strength + * checks. + */ +public class WeakCheckState implements State { + + private ConsoleWrapper theConsole; + private StateValues stateValues; + private static char[] VALID_PUNCTUATION = {'.', '@', '\\', '=', ',','/'}; + + public WeakCheckState(ConsoleWrapper theConsole,StateValues stateValues) { + this.theConsole = theConsole; + this.stateValues = stateValues; + if (theConsole.getConsole() == null) { + throw MESSAGES.noConsoleAvailable(); + } + } + + private boolean isValidPunctuation(char currentChar) { + Arrays.sort(VALID_PUNCTUATION); + return (Arrays.binarySearch(VALID_PUNCTUATION,currentChar) >= 0); + } + + @Override + public State execute() { + State retryState = stateValues.isSilentOrNonInteractive() ? null : new PromptNewUserState(theConsole, stateValues); + + if (Arrays.equals(stateValues.getUserName().toCharArray(), stateValues.getPassword())) { + return new ErrorState(theConsole, MESSAGES.usernamePasswordMatch(), retryState); + } + + for (char currentChar : stateValues.getUserName().toCharArray()) { + if ((!isValidPunctuation(currentChar)) && (Character.isLetter(currentChar) || Character.isDigit(currentChar)) == false) { + return new ErrorState(theConsole, MESSAGES.usernameNotAlphaNumeric(), retryState); + } + } + + boolean weakUserName = false; + for (String current : BAD_USER_NAMES) { + if (current.equals(stateValues.getUserName().toLowerCase())) { + weakUserName = true; + break; + } + } + + State continuingState = new DuplicateUserCheckState(theConsole, stateValues); + if (weakUserName && stateValues.isSilentOrNonInteractive() == false) { + String message = MESSAGES.usernameEasyToGuess(stateValues.getUserName()); + String prompt = MESSAGES.sureToAddUser(stateValues.getUserName()); + State noState = new PromptNewUserState(theConsole, stateValues); + + return new ConfirmationChoice(theConsole,message, prompt, continuingState, noState); + } + + return continuingState; + } + +} \ No newline at end of file