From a5b1c5e3bb08f46938266b428f65d3b6d6bc1fa7 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
--
1.8.3.1