Mercurial > hg > release > thermostat-1.4
changeset 1755:5abd45b55267
Refactor thermostat setup model.
Changeset changed from HEAD in the following ways:
- No use of FileStorageCredentials (see PR2583 for details)
- Adapting MongodbUserSetup{,Test} so as to write/test correct
web.auth credentials format.
- Use libs/create-user.js rather than lib/create-user.js
Reviewed-by: neugens
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2015-August/015518.html
PR2581
line wrap: on
line diff
--- a/setup-command/command/pom.xml Fri Aug 21 15:52:23 2015 -0400 +++ b/setup-command/command/pom.xml Wed Sep 02 15:27:53 2015 +0200 @@ -63,6 +63,7 @@ <Private-Package> com.redhat.thermostat.setup.command, com.redhat.thermostat.setup.command.internal, + com.redhat.thermostat.setup.command.internal.model, com.redhat.thermostat.setup.command.locale, </Private-Package> <Bundle-Activator>com.redhat.thermostat.setup.command.internal.Activator</Bundle-Activator>
--- a/setup-command/command/src/main/java/com/redhat/thermostat/setup/command/SetupCommand.java Fri Aug 21 15:52:23 2015 -0400 +++ b/setup-command/command/src/main/java/com/redhat/thermostat/setup/command/SetupCommand.java Wed Sep 02 15:27:53 2015 +0200 @@ -47,8 +47,7 @@ import com.redhat.thermostat.internal.utils.laf.ThemeManager; import com.redhat.thermostat.launcher.Launcher; import com.redhat.thermostat.setup.command.internal.SetupWindow; -import com.redhat.thermostat.setup.command.internal.ThermostatSetup; -import com.redhat.thermostat.setup.command.internal.ThermostatSetupImpl; +import com.redhat.thermostat.setup.command.internal.model.ThermostatSetup; import com.redhat.thermostat.shared.config.CommonPaths; import com.redhat.thermostat.shared.locale.LocalizedString; @@ -98,7 +97,7 @@ //package-private for testing void createMainWindowAndRun() throws CommandException { - thermostatSetup = new ThermostatSetupImpl(launcher, paths, console); + thermostatSetup = ThermostatSetup.create(launcher, paths, console); mainWindow = new SetupWindow(thermostatSetup); mainWindow.run(); }
--- a/setup-command/command/src/main/java/com/redhat/thermostat/setup/command/internal/CredentialFinder.java Fri Aug 21 15:52:23 2015 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,80 +0,0 @@ -/* - * Copyright 2012-2015 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat 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 - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.setup.command.internal; - -import com.redhat.thermostat.shared.config.CommonPaths; - -import java.io.File; -import java.io.IOException; - -public class CredentialFinder { - - private final CommonPaths paths; - - public CredentialFinder(CommonPaths paths) { - this.paths = paths; - } - - public File getConfiguration(String name) throws IOException { - File systemFile = getConfigurationFile(paths.getSystemConfigurationDirectory(), name); - if (isUsable(systemFile)) { - return systemFile; - } - File userFile = getConfigurationFile(paths.getUserConfigurationDirectory(), name); - if (isUsable(userFile)) { - return userFile; - } - return null; - } - - //package-private for testing - File getConfigurationFile(File directory, String name) { - return new File(directory, name); - } - - //package-private for testing - boolean isUsable(File file) throws IOException { - if (file.exists()) { - return file.isFile() && file.canRead() && file.canWrite(); - } else { - //Check that the parent directory is not read-only, - //so file can be created - File canonicalFile = file.getCanonicalFile(); - return canonicalFile.getParentFile().canWrite(); - } - } -}
--- a/setup-command/command/src/main/java/com/redhat/thermostat/setup/command/internal/MongoUserSetupView.java Fri Aug 21 15:52:23 2015 -0400 +++ b/setup-command/command/src/main/java/com/redhat/thermostat/setup/command/internal/MongoUserSetupView.java Wed Sep 02 15:27:53 2015 +0200 @@ -307,8 +307,8 @@ return passwordField1.getPassword(); } - public void setPassword(String password) { - this.passwordField1.setText(password); - this.passwordField2.setText(password); + public void setPassword(char[] password) { + this.passwordField1.setText(String.valueOf(password)); + this.passwordField2.setText(String.valueOf(password)); } }
--- a/setup-command/command/src/main/java/com/redhat/thermostat/setup/command/internal/MongodbUserSetupException.java Fri Aug 21 15:52:23 2015 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,52 +0,0 @@ -/* - * Copyright 2012-2015 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat 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 - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.setup.command.internal; - -public class MongodbUserSetupException extends Exception { - - public MongodbUserSetupException(String message) { - super(message); - } - - public MongodbUserSetupException(Throwable cause) { - super(cause); - } - - public MongodbUserSetupException(String message, Throwable cause) { - super(message, cause); - } -}
--- a/setup-command/command/src/main/java/com/redhat/thermostat/setup/command/internal/SetupWindow.java Fri Aug 21 15:52:23 2015 -0400 +++ b/setup-command/command/src/main/java/com/redhat/thermostat/setup/command/internal/SetupWindow.java Wed Sep 02 15:27:53 2015 +0200 @@ -55,6 +55,9 @@ import javax.swing.SwingWorker; import com.redhat.thermostat.common.cli.CommandException; +import com.redhat.thermostat.setup.command.internal.model.MongodbUserSetupException; +import com.redhat.thermostat.setup.command.internal.model.ThermostatSetup; +import com.redhat.thermostat.setup.command.internal.model.UserRoles; import com.redhat.thermostat.setup.command.locale.LocaleResources; import com.redhat.thermostat.shared.locale.LocalizedString; import com.redhat.thermostat.shared.locale.Translate; @@ -77,9 +80,9 @@ private static final String DEFAULT_AGENT_USER = "agent-tester"; private static final String DEFAULT_CLIENT_USER = "client-tester"; - private static final String DEFAULT_USER_PASSWORD = "tester"; + private static final char[] DEFAULT_USER_PASSWORD = new char[] { 't', 'e', 's', 't', 'e', 'r' }; private static final String DEFAULT_STORAGE_USER = "mongodevuser"; - private static final String DEFAULT_STORAGE_PASSWORD = "mongodevpassword"; + private static final char[] DEFAULT_STORAGE_PASSWORD = new char[] { 'm', 'o', 'n', 'g', 'o', 'd', 'e', 'v', 'p', 'a', 's', 's', 'w', 'o', 'r', 'd' }; private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer(); public SetupWindow(ThermostatSetup thermostatSetup) { @@ -244,23 +247,19 @@ public Void doInBackground() { mongoUserSetupView.disableButtons(); userPropertiesView.disableButtons(); - + thermostatSetup.createMongodbUser(storageUsername, storagePassword); try { - runMongoSetup(); - } catch (MongodbUserSetupException e) { - setupFailed = true; - return null; + if (userPropertiesView.makeAgentUserSelected()) { + thermostatSetup.createAgentUser(DEFAULT_AGENT_USER, DEFAULT_USER_PASSWORD); + } + if (userPropertiesView.makeClientAdminSelected()) { + thermostatSetup.createClientAdminUser(DEFAULT_CLIENT_USER, DEFAULT_USER_PASSWORD); + } + thermostatSetup.commit(); + } catch (IOException e) { + e.printStackTrace(); + shutdown(); } - - if (thermostatSetup.isWebAppInstalled()) { - try { - runPropertiesSetup(); - } catch (IOException e) { - setupFailed = true; - return null; - } - } - return null; } @@ -274,47 +273,6 @@ worker.execute(); } - private void runMongoSetup() throws MongodbUserSetupException { - thermostatSetup.createMongodbUser(storageUsername, storagePassword); - } - - private void runPropertiesSetup() throws IOException { - String[] agentRoles = null; - String[] clientRoles = null; - if (userPropertiesView.makeAgentUserSelected()) { - agentRoles = new String[] { - UserRoles.CMD_CHANNEL_VERIFY, - UserRoles.LOGIN, - UserRoles.PREPARE_STATEMENT, - UserRoles.PURGE, - UserRoles.REGISTER_CATEGORY, - UserRoles.ACCESS_REALM, - UserRoles.SAVE_FILE, - UserRoles.WRITE, - UserRoles.GRANT_FILES_WRITE_ALL, - }; - } - if (userPropertiesView.makeClientAdminSelected()) { - clientRoles = new String[] { - UserRoles.GRANT_AGENTS_READ_ALL, - UserRoles.CMD_CHANNEL_GENERATE, - UserRoles.GRANT_HOSTS_READ_ALL, - UserRoles.LOAD_FILE, - UserRoles.LOGIN, - UserRoles.PREPARE_STATEMENT, - UserRoles.READ, - UserRoles.ACCESS_REALM, - UserRoles.REGISTER_CATEGORY, - UserRoles.GRANT_VMS_READ_BY_USERNAME_ALL, - UserRoles.GRANT_VMS_READ_BY_VM_ID_ALL, - UserRoles.GRANT_FILES_READ_ALL, - UserRoles.WRITE, - }; - } - thermostatSetup.createThermostatUser(DEFAULT_AGENT_USER, DEFAULT_USER_PASSWORD.toCharArray(), agentRoles); - thermostatSetup.createThermostatUser(DEFAULT_CLIENT_USER, DEFAULT_USER_PASSWORD.toCharArray(), clientRoles); - } - private void showView(SetupView view) { mainView.add(view.getUiComponent(), BorderLayout.CENTER); view.setTitleAndProgress(title, progress);
--- a/setup-command/command/src/main/java/com/redhat/thermostat/setup/command/internal/ThermostatSetup.java Fri Aug 21 15:52:23 2015 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,72 +0,0 @@ -/* - * Copyright 2012-2015 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat 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 - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.setup.command.internal; - -import java.io.IOException; - -public interface ThermostatSetup { - - /** - * Creates a Mongodb User and a web.auth - * file (if webapp is installed) for the - * provided username and password - * - * @param username - * @param password - * @throws MongodbUserSetupException - */ - void createMongodbUser(String username, char[] password) throws MongodbUserSetupException; - - /** - * Creates entries in the thermostat-users.properties - * and thermostat-roles.properties for the provided - * username, password and roles - * - * @param username - * @param password - * @param roles - * @throws IOException - */ - void createThermostatUser(String username, char[] password, String[] roles) throws IOException; - - /** - * Checks if webapp is installed on the system - * - * @return true if webapp is installed, false otherwise - */ - boolean isWebAppInstalled(); -}
--- a/setup-command/command/src/main/java/com/redhat/thermostat/setup/command/internal/ThermostatSetupImpl.java Fri Aug 21 15:52:23 2015 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,426 +0,0 @@ -/* - * Copyright 2012-2015 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat 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 - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.setup.command.internal; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.io.PrintWriter; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.attribute.PosixFilePermission; -import java.nio.file.attribute.PosixFilePermissions; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.EnumSet; -import java.util.List; -import java.util.Properties; -import java.util.Set; - -import com.redhat.thermostat.common.ActionEvent; -import com.redhat.thermostat.common.ActionListener; -import com.redhat.thermostat.common.cli.AbstractStateNotifyingCommand; -import com.redhat.thermostat.common.cli.Console; -import com.redhat.thermostat.common.tools.ApplicationState; -import com.redhat.thermostat.launcher.Launcher; -import com.redhat.thermostat.setup.command.locale.LocaleResources; -import com.redhat.thermostat.shared.config.CommonPaths; -import com.redhat.thermostat.shared.locale.Translate; - - -public class ThermostatSetupImpl implements ThermostatSetup { - private static final String WEB_AUTH_FILE = "web.auth"; - private static final String USERS_PROPERTIES = "thermostat-users.properties"; - private static final String ROLES_PROPERTIES = "thermostat-roles.properties"; - private static final String MONGO_INPUT_SCRIPT = "/tmp/mongo-input.js"; - private static final String THERMOSTAT_AGENT = "thermostat-agent"; - private static final String THERMOSTAT_CLIENT = "thermostat-client"; - private static final String THERMOSTAT_CMDC = "thermostat-cmdc"; - private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer(); - private static final String[] STORAGE_START_ARGS = {"storage", "--start", "--permitLocalhostException"}; - private static final String[] STORAGE_STOP_ARGS = {"storage", "--stop"}; - private static final Set<PosixFilePermission> credFilePermissions = EnumSet.of( - PosixFilePermission.OWNER_READ, - PosixFilePermission.OWNER_WRITE - ); - - private static boolean storageFailed = false; - private List<ActionListener<ApplicationState>> listeners; - private String setupTmpUnlockContent; - private String webApp; - private String setupUnlockContentRegular; - private String userDoneFile; - private String createUserScript; - private CredentialFinder finder; - private File setupCompleteFile; - private File agentAuthFile; - private Launcher launcher; - private Properties roleProps; - - public ThermostatSetupImpl(Launcher launcher, CommonPaths paths, Console console) { - this(launcher, paths, console, new CredentialFinder(paths)); - } - - //package-private constructor for testing - ThermostatSetupImpl(Launcher launcher, CommonPaths paths, Console console, CredentialFinder finder) { - this.launcher = launcher; - this.finder = finder; - listeners = new ArrayList<>(); - listeners.add(new StorageListener(console)); - setThermostatVars(paths); - } - - private void setThermostatVars(CommonPaths paths) { - //set thermostat environment - createUserScript = paths.getSystemThermostatHome().toString() + "/lib/create-user.js"; - userDoneFile = paths.getUserThermostatHome().toString() + "/data/mongodb-user-done.stamp"; - webApp = paths.getSystemThermostatHome() + "/webapp"; - String setupCompletePath = paths.getUserThermostatHome().toString() + "/data/setup-complete.stamp"; - agentAuthFile = paths.getUserAgentAuthConfigFile(); - setupCompleteFile = new File(setupCompletePath); - - //set stamp complete vars - DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss zzz"); - Date date = new Date(); - String timestamp = dateFormat.format(date); - String programName = "Thermostat Setup"; - setupTmpUnlockContent = "Temporarily unlocked thermostat via " + programName + " on " + timestamp; - setupUnlockContentRegular = "Created by " + programName + " on " + timestamp; - } - - //package-private for testing - void createCredentialFile(File file) throws IOException { - if (!file.exists()) { - //create file and set file permissions to 600 - Files.createFile(file.toPath(), PosixFilePermissions.asFileAttribute(credFilePermissions)); - } - } - - @Override - public void createMongodbUser(String username, char[] password) throws MongodbUserSetupException { - try { - unlockThermostat(); - - startStorage(); - - byte[] encoded = Files.readAllBytes(Paths.get(createUserScript)); - String mongoInput = new String(encoded); - mongoInput = mongoInput.replaceAll("\\$USERNAME", username); - mongoInput = mongoInput.replaceAll("\\$PASSWORD", String.valueOf(password)); - - File mongoInputFile = new File(MONGO_INPUT_SCRIPT); - mongoInputFile.deleteOnExit(); - Files.write(mongoInputFile.toPath(), mongoInput.getBytes()); - - int mongoRetVal = runMongo(); - if (mongoRetVal != 0) { - throw new MongodbUserSetupException("Mongodb user setup failed"); - } - - stopStorage(); - - if (isWebAppInstalled()) { - writeStorageCredentialsFile(username, password); - } - - File userDoneFile = new File(this.userDoneFile); - userDoneFile.createNewFile(); - - Files.write(setupCompleteFile.toPath(), setupUnlockContentRegular.getBytes()); - } catch (IOException | InterruptedException e) { - removeTempStampFile(); - throw new MongodbUserSetupException("Error creating Mongodb user", e); - } - } - - //package-private for testing - void unlockThermostat() throws IOException { - Files.write(setupCompleteFile.toPath(), setupTmpUnlockContent.getBytes()); - } - - private void startStorage() throws MongodbUserSetupException { - launcher.run(STORAGE_START_ARGS, listeners, false); - - if (storageFailed) { - throw new MongodbUserSetupException("Thermostat storage failed to start"); - } - } - - private void stopStorage() throws MongodbUserSetupException { - launcher.run(STORAGE_STOP_ARGS, listeners, false); - - if (storageFailed) { - throw new MongodbUserSetupException("Thermostat storage failed to stop"); - } - } - - //package-private for testing - int runMongo() throws IOException, InterruptedException { - ProcessBuilder mongoProcess = new ProcessBuilder("mongo", "127.0.0.1:27518/thermostat", MONGO_INPUT_SCRIPT); - return mongoProcess.start().waitFor(); - } - - private void writeStorageCredentialsFile(String username, char[] password) throws MongodbUserSetupException { - try { - Properties credentialProps = new Properties(); - File credentialsFile = finder.getConfiguration(WEB_AUTH_FILE); - createCredentialFile(credentialsFile); - credentialProps.load(new FileInputStream(credentialsFile)); - if (credentialProps.getProperty("username") == null) { - credentialProps = new Properties(); - credentialProps.setProperty("username", username); - credentialProps.setProperty("password", String.valueOf(password)); - credentialProps.store(new FileOutputStream(credentialsFile), "Storage Credentials"); - } - } catch (IOException e) { - throw new MongodbUserSetupException("Storing credentials to file " + WEB_AUTH_FILE + " failed!", e); - } - } - - private void removeTempStampFile() { - if (setupCompleteFile.exists()) { - setupCompleteFile.delete(); - } - } - - @Override - public void createThermostatUser(String username, char[] password, String[] roles) throws IOException { - List<String> rolesList = Arrays.asList(roles); - - if (rolesList.containsAll(Arrays.asList(UserRoles.CLIENT_ROLES))) { - createClientUser(username, password, roles); - } else if (rolesList.containsAll(Arrays.asList(UserRoles.AGENT_ROLES))) { - createAgentUser(username, password, roles); - } - } - - private void createClientUser(String username, char[] password, String[] roles) throws IOException { - Properties userProps = new Properties(); - File userPropsFile = finder.getConfiguration(USERS_PROPERTIES); - createCredentialFile(userPropsFile); - userProps.load(new FileInputStream(userPropsFile)); - if (userProps.getProperty(username) == null) { - userProps = new Properties(); - userProps.setProperty(username, String.valueOf(password)); - userProps.store(new FileOutputStream(userPropsFile, true), "Client User"); - } - - setClientRoles(username, roles); - } - - private void setClientRoles(String username, String[] clientRoles) throws IOException { - String[] clientUserRoles = new String[] { - THERMOSTAT_CLIENT, - THERMOSTAT_CMDC, - UserRoles.PURGE - }; - - String[] cmdcRoles = new String[] { - UserRoles.GRANT_CMD_CHANNEL_GARBAGE_COLLECT, - UserRoles.GRANT_CMD_CHANNEL_DUMP_HEAP, - UserRoles.GRANT_CMD_CHANNEL_GRANT_THREAD_HARVESTER, - UserRoles.GRANT_CMD_CHANNEL_KILLVM, - UserRoles.GRANT_CMD_PROFILE_VM, - UserRoles.GRANT_CMD_CHANNEL_PING, - UserRoles.GRANT_CMD_CHANNEL_JMX_TOGGLE_NOTIFICATION, - }; - - setRoleProperty(username, clientUserRoles); - setRoleProperty(THERMOSTAT_CLIENT, clientRoles); - setRoleProperty(THERMOSTAT_CMDC, cmdcRoles); - - File rolePropsFile = finder.getConfiguration(ROLES_PROPERTIES); - createCredentialFile(rolePropsFile); - if (roleProps.size() > 0) { - FileOutputStream roleStream = new FileOutputStream(rolePropsFile, true); - roleProps.store(new PropertiesWriter(roleStream), "Thermostat Client Roles"); - } - } - - private void createAgentUser(String username, char[] password, String[] roles) throws IOException { - Properties userProps = new Properties(); - File userPropsFile = finder.getConfiguration(USERS_PROPERTIES); - createCredentialFile(userPropsFile); - userProps.load(new FileInputStream(userPropsFile)); - if (userProps.getProperty(username) == null) { - userProps = new Properties(); - userProps.setProperty(username, String.valueOf(password)); - userProps.store(new FileOutputStream(userPropsFile, true), "Agent User"); - } - - //set agent credentials - Properties agentProps = new Properties(); - createCredentialFile(agentAuthFile); - agentProps.load(new FileInputStream(agentAuthFile)); - if (userProps.getProperty("username") == null) { - agentProps = new Properties(); - agentProps.setProperty("username", username); - agentProps.setProperty("password", String.valueOf(password)); - agentProps.store(new FileOutputStream(agentAuthFile), "Agent Credentials"); - } - - setAgentRoles(username, roles); - } - - private void setAgentRoles(String username, String[] agentRoles) throws IOException { - String[] agentUserRoles = new String[] { - THERMOSTAT_AGENT - }; - setRoleProperty(username, agentUserRoles); - setRoleProperty(THERMOSTAT_AGENT, agentRoles); - File rolePropsFile = finder.getConfiguration(ROLES_PROPERTIES); - createCredentialFile(rolePropsFile); - if (roleProps.size() > 0) { - FileOutputStream roleStream = new FileOutputStream(rolePropsFile, true); - roleProps.store(new PropertiesWriter(roleStream), "Thermostat Agent Roles"); - } - } - - private void setRoleProperty(String attribute, String[] roles) throws IOException { - Properties existingRoleProps = new Properties(); - File rolePropsFile = finder.getConfiguration(ROLES_PROPERTIES); - createCredentialFile(rolePropsFile); - existingRoleProps.load(new FileInputStream(rolePropsFile)); - if (roleProps == null) { - roleProps = new Properties(); - } - if (roles.length > 0 && existingRoleProps.getProperty(attribute) == null) { - StringBuilder rolesBuilder = new StringBuilder(); - for (int i = 0; i < roles.length - 1; i++) { - rolesBuilder.append(roles[i] + ", " + System.getProperty("line.separator")); - } - rolesBuilder.append(roles[roles.length - 1]); - roleProps.setProperty(attribute, rolesBuilder.toString()); - } - } - - @Override - public boolean isWebAppInstalled() { - Path webAppPath = Paths.get(webApp); - if (Files.exists(webAppPath)) { - return true; - } else { - return false; - } - } - - /** - * The Properties.store() method doesn't allow for new lines. This - * class is used so that when a property key has multiple associated - * values, they are written in a readable manner. - */ - public static class PropertiesWriter extends PrintWriter { - - private static final int INDENT_AMOUNT = 4; - - public PropertiesWriter(OutputStream out) { - super(out); - } - - @Override - public void write(char[] line, int startIdx, int len) { - for (int i = startIdx; i < len; i++) { - // interpret new lines as such - if (isNewLine(line, i)) { - i++; // skip 'n' in \n - try { - out.write('\\'); - out.write(System.getProperty("line.separator")); - // indent following lines - for (int j = 0; j < INDENT_AMOUNT; j++) { - out.write(' '); - } - } catch (IOException e) { - e.printStackTrace(System.err); - } - } else { - try { - out.write(line[i]); - } catch (IOException e) { - e.printStackTrace(System.err); - } - } - } - } - - private boolean isNewLine(char[] line, int j) { - if (j + 1 > line.length) { - return false; - } - if (line[j] == '\\' && line[j + 1] == 'n') { - return true; - } - return false; - } - } - - private static class StorageListener implements ActionListener<ApplicationState> { - - private final Console console; - - private StorageListener(Console console) { - this.console = console; - } - - @Override - public void actionPerformed(ActionEvent<ApplicationState> actionEvent) { - if (actionEvent.getSource() instanceof AbstractStateNotifyingCommand) { - AbstractStateNotifyingCommand storage = (AbstractStateNotifyingCommand) actionEvent.getSource(); - // Implementation detail: there is a single Storage instance registered - // as an OSGi service. We remove ourselves as listener so that we don't get - // notified in the case that the command is invoked by some other means later. - storage.getNotifier().removeActionListener(this); - - switch (actionEvent.getActionId()) { - case START: - storageFailed = false; - break; - case FAIL: - console.getOutput().println(translator.localize(LocaleResources.STORAGE_FAILED).getContents()); - storageFailed = true; - break; - } - } - } - } -}
--- a/setup-command/command/src/main/java/com/redhat/thermostat/setup/command/internal/UserRoles.java Fri Aug 21 15:52:23 2015 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,112 +0,0 @@ -/* - * Copyright 2012-2015 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat 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 - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.setup.command.internal; - -public interface UserRoles { - - /** - * Allows for a user to read records tied to any host. - */ - final String GRANT_HOSTS_READ_ALL = "thermostat-hosts-grant-read-hostname-ALL"; - /** - * Allows for a user to read records tied to any JVM id. - */ - final String GRANT_VMS_READ_BY_VM_ID_ALL = "thermostat-vms-grant-read-vmId-ALL"; - /** - * Allows for a user to read records tied to any username the JVM is running as. - */ - final String GRANT_VMS_READ_BY_USERNAME_ALL = "thermostat-vms-grant-read-username-ALL"; - /** - * Allows for a user to read any file from storage. - */ - final String GRANT_FILES_READ_ALL = "thermostat-files-grant-read-filename-ALL"; - /** - * Allows for a user to write any file to storage. - */ - final String GRANT_FILES_WRITE_ALL = "thermostat-files-grant-write-filename-ALL"; - /** - * Allows for a user to see records tied to any agent. - */ - final String GRANT_AGENTS_READ_ALL = "thermostat-agents-grant-read-agentId-ALL"; - - final String GRANT_CMD_CHANNEL_GARBAGE_COLLECT = "thermostat-cmdc-grant-garbage-collect"; - final String GRANT_CMD_CHANNEL_DUMP_HEAP = "thermostat-cmdc-grant-dump-heap"; - final String GRANT_CMD_CHANNEL_GRANT_THREAD_HARVESTER = "thermostat-cmdc-grant-thread-harvester"; - final String GRANT_CMD_CHANNEL_KILLVM = "thermostat-cmdc-grant-killvm"; - final String GRANT_CMD_CHANNEL_PING = "thermostat-cmdc-grant-ping"; - final String GRANT_CMD_CHANNEL_JMX_TOGGLE_NOTIFICATION = "thermostat-cmdc-grant-jmx-toggle-notifications"; - final String GRANT_CMD_PROFILE_VM = "thermostat-cmdc-grant-profile-vm"; - - final String PREPARE_STATEMENT = "thermostat-prepare-statement"; - final String READ = "thermostat-query"; - final String WRITE = "thermostat-write"; - final String LOAD_FILE = "thermostat-load-file"; - final String SAVE_FILE = "thermostat-save-file"; - final String PURGE = "thermostat-purge"; - final String REGISTER_CATEGORY = "thermostat-register-category"; - final String CMD_CHANNEL_VERIFY = "thermostat-cmdc-verify"; - final String CMD_CHANNEL_GENERATE = "thermostat-cmdc-generate"; - final String LOGIN = "thermostat-login"; - final String ACCESS_REALM = "thermostat-realm"; - - /** - * Basic role memberships for agent users - */ - final String[] AGENT_ROLES = { - UserRoles.PREPARE_STATEMENT, - UserRoles.WRITE, - UserRoles.CMD_CHANNEL_VERIFY, - UserRoles.LOGIN, - UserRoles.PURGE, - UserRoles.REGISTER_CATEGORY, - UserRoles.ACCESS_REALM, - UserRoles.SAVE_FILE, - }; - - /** - * Basic role memberships for client users - */ - final String[] CLIENT_ROLES = { - UserRoles.ACCESS_REALM, - UserRoles.LOGIN, - UserRoles.READ, - UserRoles.PREPARE_STATEMENT, - UserRoles.CMD_CHANNEL_GENERATE, - UserRoles.LOAD_FILE, - UserRoles.REGISTER_CATEGORY - }; -} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup-command/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/CredentialFinder.java Wed Sep 02 15:27:53 2015 +0200 @@ -0,0 +1,84 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.setup.command.internal.model; + +import com.redhat.thermostat.shared.config.CommonPaths; + +import java.io.File; +import java.io.IOException; + +public class CredentialFinder { + + private final CommonPaths paths; + + public CredentialFinder(CommonPaths paths) { + this.paths = paths; + } + + public File getConfiguration(String name) { + File systemFile = getConfigurationFile(paths.getSystemConfigurationDirectory(), name); + if (isUsable(systemFile)) { + return systemFile; + } + File userFile = getConfigurationFile(paths.getUserConfigurationDirectory(), name); + if (isUsable(userFile)) { + return userFile; + } + return null; + } + + //package-private for testing + File getConfigurationFile(File directory, String name) { + return new File(directory, name); + } + + //package-private for testing + boolean isUsable(File file) { + if (file.exists()) { + return file.isFile() && file.canRead() && file.canWrite(); + } else { + try { + //Check that the parent directory is not read-only, + //so file can be created + File canonicalFile = file.getCanonicalFile(); + return canonicalFile.getParentFile().canWrite(); + } catch (IOException e) { + return false; + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup-command/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/CredentialsFileCreator.java Wed Sep 02 15:27:53 2015 +0200 @@ -0,0 +1,60 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.setup.command.internal.model; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.PosixFilePermissions; +import java.util.EnumSet; +import java.util.Set; + +class CredentialsFileCreator { + + private static final Set<PosixFilePermission> CREDS_FILE_PERMISSIONS = EnumSet.of( + PosixFilePermission.OWNER_READ, + PosixFilePermission.OWNER_WRITE + ); + + void create(File file) throws IOException { + if (!file.exists()) { + //create file and set file permissions to 600 + Files.createFile(file.toPath(), PosixFilePermissions.asFileAttribute(CREDS_FILE_PERMISSIONS)); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup-command/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/MongodbUserSetup.java Wed Sep 02 15:27:53 2015 +0200 @@ -0,0 +1,242 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.setup.command.internal.model; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.lang.ProcessBuilder.Redirect; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Properties; +import java.util.Scanner; + +import com.redhat.thermostat.common.ActionEvent; +import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.common.cli.AbstractStateNotifyingCommand; +import com.redhat.thermostat.common.cli.Console; +import com.redhat.thermostat.common.tools.ApplicationState; +import com.redhat.thermostat.launcher.Launcher; +import com.redhat.thermostat.setup.command.locale.LocaleResources; +import com.redhat.thermostat.shared.config.CommonPaths; +import com.redhat.thermostat.shared.locale.Translate; + +class MongodbUserSetup implements UserSetup { + + static final String[] STORAGE_START_ARGS = {"storage", "--start", "--permitLocalhostException"}; + static final String[] STORAGE_STOP_ARGS = {"storage", "--stop"}; + private static final String WEB_AUTH_FILE = "web.auth"; + private static boolean storageFailed = false; + private final UserCredsValidator validator; + private final Launcher launcher; + private final CredentialFinder finder; + private final CredentialsFileCreator fileCreator; + private final List<ActionListener<ApplicationState>> listeners; + private final CommonPaths paths; + private final StampFiles stampFiles; + private final StructureInformation structureInfo; + private String username; + private char[] password; + private String userComment; + + MongodbUserSetup(UserCredsValidator validator, Launcher launcher, CredentialFinder finder, CredentialsFileCreator fileCreator, Console console, CommonPaths paths, StampFiles stampFiles, StructureInformation structureInfo) { + this.validator = validator; + this.launcher = launcher; + this.finder = finder; + this.fileCreator = fileCreator; + this.stampFiles = stampFiles; + this.listeners = new ArrayList<>(); + this.listeners.add(new StorageListener(console)); + this.paths = paths; + this.structureInfo = structureInfo; + } + + @Override + public void createUser(String username, char[] password, String comment) { + validator.validateUsername(username); + validator.validatePassword(password); + this.username = username; + this.password = password; + this.userComment = comment; + } + + @Override + public void commit() throws IOException { + try { + addMongodbUser(); + } catch (MongodbUserSetupException e) { + throw new IOException(e); + } + } + + private void addMongodbUser() throws MongodbUserSetupException { + try { + unlockThermostat(); + + startStorage(); + + int mongoRetVal = runMongo(); + if (mongoRetVal != 0) { + stampFiles.deleteSetupCompleteStamp(); + stampFiles.deleteMongodbUserStamp(); + throw new MongodbUserSetupException("Mongodb user setup failed"); + } + + if (isWebAppInstalled()) { + writeStorageCredentialsFile(username, password, userComment); + } + + stampFiles.createMongodbUserStamp(); + + if (!isWebAppInstalled()) { + String completeDate = ThermostatSetup.DATE_FORMAT.format(new Date()); + String regularContent = "Created by '" + ThermostatSetup.PROGRAM_NAME + "' on " + completeDate; + stampFiles.createSetupCompleteStamp(regularContent); + } + } catch (IOException | InterruptedException e) { + stampFiles.deleteSetupCompleteStamp(); + stampFiles.deleteMongodbUserStamp(); + throw new MongodbUserSetupException("Error creating Mongodb user", e); + } finally { + if (!storageFailed) { + stopStorage(); + } + Arrays.fill(password, '\0'); // clear the password + } + } + + //package-private for testing + void unlockThermostat() throws IOException { + Date date = new Date(); + String timestamp = ThermostatSetup.DATE_FORMAT.format(date); + String setupTmpUnlockContent = "Temporarily unlocked thermostat via '" + ThermostatSetup.PROGRAM_NAME + "' on " + timestamp + "\n"; + stampFiles.createSetupCompleteStamp(setupTmpUnlockContent); + } + + //package-private for testing + int runMongo() throws IOException, InterruptedException { + ProcessBuilder mongoProcessBuilder = new ProcessBuilder("mongo", "127.0.0.1:27518/thermostat"); + mongoProcessBuilder.redirectInput(Redirect.PIPE); + Process process = mongoProcessBuilder.start(); + File createUserTemplate = new File(paths.getSystemLibRoot(), "create-user.js"); + // Write to the forked processes stdIn replacing username/password + // on the fly. + try (OutputStream pOut = process.getOutputStream(); + PrintWriter outWriter = new PrintWriter(pOut); + FileInputStream fin = new FileInputStream(createUserTemplate); + Scanner inScanner = new Scanner(fin)) { + while (inScanner.hasNextLine()) { + String line = inScanner.nextLine(); + line = line.replaceAll("\\$USERNAME", username); + line = line.replaceAll("\\$PASSWORD", String.valueOf(password)); + line = line + "\n"; + outWriter.write(line); + } + } + return process.waitFor(); + } + + private void startStorage() throws MongodbUserSetupException { + launcher.run(STORAGE_START_ARGS, listeners, false); + + if (storageFailed) { + throw new MongodbUserSetupException("Thermostat storage failed to start"); + } + } + + private void stopStorage() throws MongodbUserSetupException { + launcher.run(STORAGE_STOP_ARGS, listeners, false); + + if (storageFailed) { + throw new MongodbUserSetupException("Thermostat storage failed to stop"); + } + } + + private boolean isWebAppInstalled() { + return structureInfo.isWebAppInstalled(); + } + + private void writeStorageCredentialsFile(String username, char[] password, String comment) throws MongodbUserSetupException { + try { + Properties credentialProps = new Properties(); + credentialProps.setProperty("storage.username", username); + credentialProps.setProperty("storage.password", String.valueOf(password)); + File credentialsFile = finder.getConfiguration(WEB_AUTH_FILE); + fileCreator.create(credentialsFile); + credentialProps.store(new FileOutputStream(credentialsFile), comment); + } catch (IOException e) { + throw new MongodbUserSetupException("Storing credentials to file " + WEB_AUTH_FILE + " failed!", e); + } + } + + private static class StorageListener implements ActionListener<ApplicationState> { + + private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer(); + private final Console console; + + private StorageListener(Console console) { + this.console = console; + } + + @Override + public void actionPerformed(ActionEvent<ApplicationState> actionEvent) { + if (actionEvent.getSource() instanceof AbstractStateNotifyingCommand) { + AbstractStateNotifyingCommand storage = (AbstractStateNotifyingCommand) actionEvent.getSource(); + // Implementation detail: there is a single Storage instance registered + // as an OSGi service. We remove ourselves as listener so that we don't get + // notified in the case that the command is invoked by some other means later. + storage.getNotifier().removeActionListener(this); + + switch (actionEvent.getActionId()) { + case START: + storageFailed = false; + break; + case FAIL: + console.getOutput().println(translator.localize(LocaleResources.STORAGE_FAILED).getContents()); + storageFailed = true; + break; + } + } + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup-command/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/MongodbUserSetupException.java Wed Sep 02 15:27:53 2015 +0200 @@ -0,0 +1,52 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.setup.command.internal.model; + +public class MongodbUserSetupException extends Exception { + + public MongodbUserSetupException(String message) { + super(message); + } + + public MongodbUserSetupException(Throwable cause) { + super(cause); + } + + public MongodbUserSetupException(String message, Throwable cause) { + super(message, cause); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup-command/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/PersistableSetup.java Wed Sep 02 15:27:53 2015 +0200 @@ -0,0 +1,49 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.setup.command.internal.model; + +import java.io.IOException; + +interface PersistableSetup { + + /** + * Persists setup info to disk. + * + * @throws IOException + */ + void commit() throws IOException; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup-command/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/PropertiesWriter.java Wed Sep 02 15:27:53 2015 +0200 @@ -0,0 +1,91 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.setup.command.internal.model; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; + +/** + * The Properties.store() method doesn't allow for new lines. This + * class is used so that when a property key has multiple associated + * values, they are written in a readable manner. + */ +public class PropertiesWriter extends PrintWriter { + + private static final int INDENT_AMOUNT = 4; + + public PropertiesWriter(OutputStream out) { + super(out); + } + + @Override + public void write(char[] line, int startIdx, int len) { + for (int i = startIdx; i < len; i++) { + // interpret new lines as such + if (isNewLine(line, i)) { + i++; // skip 'n' in \n + try { + out.write('\\'); + out.write(System.getProperty("line.separator")); + // indent following lines + for (int j = 0; j < INDENT_AMOUNT; j++) { + out.write(' '); + } + } catch (IOException e) { + e.printStackTrace(System.err); + } + } else { + try { + out.write(line[i]); + } catch (IOException e) { + e.printStackTrace(System.err); + } + } + } + } + + private boolean isNewLine(char[] line, int j) { + if (j + 1 > line.length) { + return false; + } + if (line[j] == '\\' && line[j + 1] == 'n') { + return true; + } + return false; + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup-command/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/RoleSetup.java Wed Sep 02 15:27:53 2015 +0200 @@ -0,0 +1,65 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.setup.command.internal.model; + +interface RoleSetup extends PersistableSetup { + + /** + * Assigns user {@code username} roles as given by {@code roles}. + * + * It is the responsibility of the user to first define {@code username} + * via {@link #createThermostatUser(String, char[])}. + * + * @param username + * @param roles + * @param comment An optional comment added to persistent files. May be + * {@code null} + */ + void assignRolesToUser(String username, String[] roles, String comment); + + /** + * Defines a new recursive role with name {@code name} and with role + * primitives as specified in {@code rolePrimitives} . Note elements in + * {@code rolePrimitives} might itself be a recursive roles. + * + * @param name + * @param rolePrimitives + * @param comment An optional comment added to persistent files. May be + * {@code null} + */ + void createRecursiveRole(String name, String[] rolePrimitives, String comment); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup-command/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/StampFiles.java Wed Sep 02 15:27:53 2015 +0200 @@ -0,0 +1,89 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.setup.command.internal.model; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + +import com.redhat.thermostat.shared.config.CommonPaths; + +class StampFiles { + + private final CommonPaths paths; + + StampFiles(CommonPaths paths) { + this.paths = paths; + } + + void createMongodbUserStamp() throws IOException { + File userDoneFile = getMongodbStampFile(); + userDoneFile.createNewFile(); + } + + void createSetupCompleteStamp(String contents) throws IOException { + File setupCompleteFile = getSetupCompleteStampFile(); + Files.write(setupCompleteFile.toPath(), contents.getBytes()); + } + + void deleteSetupCompleteStamp() { + File setupCompleteFile = getSetupCompleteStampFile(); + deleteFile(setupCompleteFile); + } + + void deleteMongodbUserStamp() { + File userDoneFile = getMongodbStampFile(); + deleteFile(userDoneFile); + } + + File getMongodbStampFile() { + return new File(paths.getUserPersistentDataDirectory(), "mongodb-user-done.stamp"); + } + + File getSetupCompleteStampFile() { + return new File(paths.getUserPersistentDataDirectory(), "setup-complete.stamp"); + } + + private void deleteFile(File file) { + try { + Files.delete(file.toPath()); + } catch (IOException e) { + // ignore, wanted it deleted. + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup-command/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/StructureInformation.java Wed Sep 02 15:27:53 2015 +0200 @@ -0,0 +1,61 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.setup.command.internal.model; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import com.redhat.thermostat.shared.config.CommonPaths; + +class StructureInformation { + + private final String webApp; + + StructureInformation(CommonPaths paths) { + webApp = paths.getSystemThermostatHome() + "/webapp"; + } + + boolean isWebAppInstalled() { + Path webAppPath = Paths.get(webApp); + if (Files.exists(webAppPath)) { + return true; + } else { + return false; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup-command/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/ThermostatSetup.java Wed Sep 02 15:27:53 2015 +0200 @@ -0,0 +1,147 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.setup.command.internal.model; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Properties; + +import com.redhat.thermostat.common.cli.Console; +import com.redhat.thermostat.launcher.Launcher; +import com.redhat.thermostat.shared.config.CommonPaths; + +public class ThermostatSetup implements PersistableSetup { + + static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss zzz"); + static final String PROGRAM_NAME = "thermostat setup"; + private static final String THERMOSTAT_AGENT_REC_ROLE_NAME = "thermostat-agent"; + private static final String THERMOSTAT_CLIENT_REC_ROLE_NAME = "thermostat-client"; + private static final String THERMOSTAT_GRANT_CMD_CHANNEL_ALL_REC_ROLE_NAME = "thermostat-cmdc"; + private static final String THERMOSTAT_ADMIN_READ_ALL_REC_ROLE_NAME = "thermostat-admin-read-all"; + private final ThermostatUserSetup userSetup; + private final MongodbUserSetup mongodbUserSetup; + private final StructureInformation structureInfo; + private final CredentialsFileCreator creator; + private final CommonPaths paths; + private String agentUserName; + private char[] agentPassword; + + ThermostatSetup(ThermostatUserSetup userSetup, MongodbUserSetup mongodbUserSetup, StructureInformation structureInfo, CommonPaths paths, CredentialsFileCreator creator) { + this.mongodbUserSetup = mongodbUserSetup; + this.userSetup = userSetup; + this.structureInfo = structureInfo; + this.paths = paths; + this.creator = creator; + } + + public void createMongodbUser(String username, char[] password) { + mongodbUserSetup.createUser(username, password, "Backing storage connection credentials."); + } + + public void createClientAdminUser(String username, char[] password) { + userSetup.createUser(username, password, "Thermostat client admin user"); + userSetup.createRecursiveRole(THERMOSTAT_GRANT_CMD_CHANNEL_ALL_REC_ROLE_NAME, + UserRoles.CMD_CHANNEL_GRANT_ALL_ACTIONS, + "Recursive role granting all CMD-channel actions."); + userSetup.createRecursiveRole(THERMOSTAT_CLIENT_REC_ROLE_NAME, + UserRoles.CLIENT_ROLES, + "Recursive role for Thermostat client users."); + userSetup.createRecursiveRole(THERMOSTAT_ADMIN_READ_ALL_REC_ROLE_NAME, + UserRoles.ADMIN_READALL, + "Recursive role allowing a user to read all records."); + userSetup.assignRolesToUser(username, new String[] { + THERMOSTAT_CLIENT_REC_ROLE_NAME, + THERMOSTAT_GRANT_CMD_CHANNEL_ALL_REC_ROLE_NAME, + THERMOSTAT_ADMIN_READ_ALL_REC_ROLE_NAME, + UserRoles.PURGE // Client needs purge for clean-data cmd. + }, + "Client admin user username => role assignment." + ); + } + + public void createAgentUser(String username, char[] password) { + userSetup.createUser(username, password, "Thermostat agent user"); + userSetup.createRecursiveRole(THERMOSTAT_AGENT_REC_ROLE_NAME, + UserRoles.AGENT_ROLES, + "Recursive role for Thermostat agent users."); + userSetup.assignRolesToUser(username, + new String[] { + THERMOSTAT_AGENT_REC_ROLE_NAME, + UserRoles.GRANT_FILES_WRITE_ALL // Agent needs perm to write files. + }, + "Agent user username => role assignment."); + // Hold on to creds for persistency later. + agentUserName = username; + agentPassword = password; + } + + @Override + public void commit() throws IOException { + // FIXME: report errors + mongodbUserSetup.commit(); + userSetup.commit(); + writeAgentAuthFile(); + } + + private void writeAgentAuthFile() throws IOException { + Properties credentialProps = new Properties(); + credentialProps.setProperty("username", agentUserName); + credentialProps.setProperty("password", String.valueOf(agentPassword)); + File credentialsFile = paths.getUserAgentAuthConfigFile(); + creator.create(credentialsFile); + try (FileOutputStream fout = new FileOutputStream(credentialsFile)) { + credentialProps.store(fout, "Credentials used for 'thermostat agent' connections."); + } + } + + public boolean isWebAppInstalled() { + return structureInfo.isWebAppInstalled(); + } + + public static ThermostatSetup create(Launcher launcher, CommonPaths paths, Console console) { + CredentialFinder finder = new CredentialFinder(paths); + CredentialsFileCreator creator = new CredentialsFileCreator(); + StampFiles stampFiles = new StampFiles(paths); + StructureInformation info = new StructureInformation(paths); + MongodbUserSetup mongoSetup = new MongodbUserSetup(new UserCredsValidator(), launcher, finder, creator , console, paths, stampFiles, info); + ThermostatUserSetup userSetup = new ThermostatUserSetup(new UserPropertiesFinder(finder), new UserCredsValidator(), creator, stampFiles); + return new ThermostatSetup(userSetup, mongoSetup, info, paths, creator); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup-command/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/ThermostatUserSetup.java Wed Sep 02 15:27:53 2015 +0200 @@ -0,0 +1,257 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.setup.command.internal.model; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.Writer; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.redhat.thermostat.common.utils.LoggingUtils; + +class ThermostatUserSetup implements UserSetup, RoleSetup { + + private static final Logger logger = LoggingUtils.getLogger(ThermostatUserSetup.class); + private final UserCredsValidator validator; + private final UserPropertiesFinder propsFileFinder; + private final CredentialsFileCreator fileCreator; + private final StampFiles stampFiles; + private final Map<String, String> roleComments = new HashMap<>(); + private final Map<String, String> userComments = new HashMap<>(); + private final Map<String, String[]> roles = new HashMap<>(); + private final Map<String, char[]> userCreds = new HashMap<>(); + + ThermostatUserSetup(UserPropertiesFinder propsFileFinder, UserCredsValidator validator, CredentialsFileCreator creator, StampFiles stampFiles) { + this.propsFileFinder = propsFileFinder; + this.validator = validator; + this.fileCreator = creator; + this.stampFiles = stampFiles; + } + + @Override + public void commit() throws IOException { + // FIXME: Don't persist users/role setup if webapp is not installed. + Map<String, CommentedRolePropertyValue> roleProps = buildRoleProperties(); + Map<String, CommentedCredsPropertyValue> userProps = buildUserProperties(); + writeUsers(userProps); + writeRoles(roleProps); + writeSetupCompleteFile(); + } + + private void writeSetupCompleteFile() throws IOException { + String completeTime = ThermostatSetup.DATE_FORMAT.format(new Date()); + String contents = "Created by '" + ThermostatSetup.PROGRAM_NAME + "' on " + completeTime + "\n"; + stampFiles.createSetupCompleteStamp(contents); + } + + // package-private for testing + void writeRoles(Map<String, CommentedRolePropertyValue> roleProps) throws IOException { + File rolePropsFile = propsFileFinder.getRolesProperties(); + fileCreator.create(rolePropsFile); + Properties existingProperties = readPropsFromFile(rolePropsFile); + try (FileOutputStream roleStream = new FileOutputStream(rolePropsFile, true); + Writer rolesWriter = new PropertiesWriter(roleStream)) { + for (String key : roleProps.keySet()) { + Properties p = new Properties(); + CommentedRolePropertyValue val = roleProps.get(key); + // prevent duplicate entries from being written + if (existingProperties.get(key) == null) { + p.put(key, val.getValue()); + p.store(rolesWriter, val.getComment()); + } else { + logger.info("Skipping already existing key '" + key + "' in file " + rolePropsFile.toString()); + } + } + } + + } + + // package-private for testing + Properties readPropsFromFile(File propsFile) { + Properties props = new Properties(); + try (FileInputStream inStream = new FileInputStream(propsFile)) { + props.load(inStream); + } catch (IOException e) { + logger.log(Level.INFO, "Failed to read existing properties file " + propsFile.toString()); + } + return props; + } + + // package-private for testing + void writeUsers(Map<String, CommentedCredsPropertyValue> userProps) throws IOException { + File userPropsFile = propsFileFinder.getUserProperties(); + fileCreator.create(userPropsFile); + Properties existingProperties = readPropsFromFile(userPropsFile); + try (FileOutputStream userStream = new FileOutputStream(userPropsFile, true)) { + for (String key : userProps.keySet()) { + Properties p = new Properties(); + CommentedCredsPropertyValue val = userProps.get(key); + // prevent duplicate entries from being written + if (existingProperties.get(key) == null) { + p.put(key, String.valueOf(val.getValue())); + p.store(userStream, val.getComment()); + } else { + logger.info("Skipping already existing key '" + key + "' in file " + userPropsFile.toString()); + } + } + } + } + + // package-private for testing + Map<String, CommentedCredsPropertyValue> buildUserProperties() { + Map<String, CommentedCredsPropertyValue> userProps = new HashMap<>(); + for (String user: userCreds.keySet()) { + userProps.put(user, new CommentedCredsPropertyValue(userCreds.get(user), userComments.get(user))); + } + return userProps; + } + + // package-private for testing + Map<String, CommentedRolePropertyValue> buildRoleProperties() { + Map<String, CommentedRolePropertyValue> roleProps = new HashMap<>(); + for (String roleKey: roles.keySet()) { + String value = roleSetToString(roles.get(roleKey)); + CommentedRolePropertyValue roleVal = new CommentedRolePropertyValue(value, roleComments.get(roleKey)); + roleProps.put(roleKey, roleVal); + } + return roleProps; + } + + @Override + public void assignRolesToUser(String username, String[] roles, String comment) { + validator.validateUsername(username); + validateRoles(roles); + this.roles.put(username, roles); + addRoleComment(username, comment); + } + + @Override + public void createRecursiveRole(String name, String[] rolePrimitives, String comment) { + validator.validateUsername(name); + validateRoles(rolePrimitives); + this.roles.put(name, rolePrimitives); + addRoleComment(name, comment); + } + + private void validateRoles(String[] roles) { + if (roles == null || roles.length == 0) { + throw new IllegalArgumentException("Roles must not be null or empty"); + } + } + + @Override + public void createUser(String username, char[] password, String comment) { + validator.validateUsername(username); + validator.validatePassword(password); + this.userCreds.put(username, password); + addUserComment(username, comment); + } + + void addRoleComment(String property, String comment) { + if (comment != null) { + roleComments.put(Objects.requireNonNull(property), comment); + } + } + + void addUserComment(String property, String comment) { + if (comment != null) { + userComments.put(Objects.requireNonNull(property), comment); + } + } + + String roleSetToString(String[] roles) { + if (roles.length == 0) { + return ""; + } + StringBuilder rolesBuilder = new StringBuilder(); + for (int i = 0; i < roles.length - 1; i++) { + rolesBuilder.append(roles[i] + ", " + System.getProperty("line.separator")); + } + rolesBuilder.append(roles[roles.length - 1]); + return rolesBuilder.toString(); + } + + static class CommentedPropertyValue { + private final String comment; + + CommentedPropertyValue(String comment) { + this.comment = comment; + } + + String getComment() { + return comment; + } + + } + + static class CommentedRolePropertyValue extends CommentedPropertyValue { + + private final String value; + + CommentedRolePropertyValue(String value, String comment) { + super(comment); + this.value = value; + } + + String getValue() { + return value; + } + } + + static class CommentedCredsPropertyValue extends CommentedPropertyValue { + + private final char[] value; + + CommentedCredsPropertyValue(char[] value, String comment) { + super(comment); + this.value = value; + } + + char[] getValue() { + return value; + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup-command/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/UserCredsValidator.java Wed Sep 02 15:27:53 2015 +0200 @@ -0,0 +1,53 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.setup.command.internal.model; + +class UserCredsValidator { + + void validateUsername(String username) { + if (username == null || username.isEmpty()) { + throw new IllegalArgumentException("Name must not be null or empty"); + } + } + + void validatePassword(char[] password) { + if (password == null || password.length == 0) { + throw new IllegalArgumentException("Password must not be null or empty"); + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup-command/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/UserPropertiesFinder.java Wed Sep 02 15:27:53 2015 +0200 @@ -0,0 +1,59 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.setup.command.internal.model; + +import java.io.File; + +class UserPropertiesFinder { + + private static final String USER_PROPERTIES = "thermostat-users.properties"; + private static final String ROLES_PROPERTIES = "thermostat-roles.properties"; + + private final CredentialFinder credsFinder; + + UserPropertiesFinder(CredentialFinder finder) { + this.credsFinder = finder; + } + + File getUserProperties() { + return credsFinder.getConfiguration(USER_PROPERTIES); + } + + File getRolesProperties() { + return credsFinder.getConfiguration(ROLES_PROPERTIES); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup-command/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/UserRoles.java Wed Sep 02 15:27:53 2015 +0200 @@ -0,0 +1,130 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.setup.command.internal.model; + +public interface UserRoles { + + /** + * Allows for a user to read records tied to any host. + */ + final String GRANT_HOSTS_READ_ALL = "thermostat-hosts-grant-read-hostname-ALL"; + /** + * Allows for a user to read records tied to any JVM id. + */ + final String GRANT_VMS_READ_BY_VM_ID_ALL = "thermostat-vms-grant-read-vmId-ALL"; + /** + * Allows for a user to read records tied to any username the JVM is running as. + */ + final String GRANT_VMS_READ_BY_USERNAME_ALL = "thermostat-vms-grant-read-username-ALL"; + /** + * Allows for a user to read any file from storage. + */ + final String GRANT_FILES_READ_ALL = "thermostat-files-grant-read-filename-ALL"; + /** + * Allows for a user to write any file to storage. + */ + final String GRANT_FILES_WRITE_ALL = "thermostat-files-grant-write-filename-ALL"; + /** + * Allows for a user to see records tied to any agent. + */ + final String GRANT_AGENTS_READ_ALL = "thermostat-agents-grant-read-agentId-ALL"; + + final String GRANT_CMD_CHANNEL_GARBAGE_COLLECT = "thermostat-cmdc-grant-garbage-collect"; + final String GRANT_CMD_CHANNEL_DUMP_HEAP = "thermostat-cmdc-grant-dump-heap"; + final String GRANT_CMD_CHANNEL_GRANT_THREAD_HARVESTER = "thermostat-cmdc-grant-thread-harvester"; + final String GRANT_CMD_CHANNEL_KILLVM = "thermostat-cmdc-grant-killvm"; + final String GRANT_CMD_CHANNEL_PING = "thermostat-cmdc-grant-ping"; + final String GRANT_CMD_CHANNEL_JMX_TOGGLE_NOTIFICATION = "thermostat-cmdc-grant-jmx-toggle-notifications"; + final String GRANT_CMD_PROFILE_VM = "thermostat-cmdc-grant-profile-vm"; + + final String PREPARE_STATEMENT = "thermostat-prepare-statement"; + final String READ = "thermostat-query"; + final String WRITE = "thermostat-write"; + final String LOAD_FILE = "thermostat-load-file"; + final String SAVE_FILE = "thermostat-save-file"; + final String PURGE = "thermostat-purge"; + final String REGISTER_CATEGORY = "thermostat-register-category"; + final String CMD_CHANNEL_VERIFY = "thermostat-cmdc-verify"; + final String CMD_CHANNEL_GENERATE = "thermostat-cmdc-generate"; + final String LOGIN = "thermostat-login"; + final String ACCESS_REALM = "thermostat-realm"; + + /** + * Basic role memberships for agent users + */ + final String[] AGENT_ROLES = { + UserRoles.PREPARE_STATEMENT, + UserRoles.WRITE, + UserRoles.CMD_CHANNEL_VERIFY, + UserRoles.LOGIN, + UserRoles.PURGE, + UserRoles.REGISTER_CATEGORY, + UserRoles.ACCESS_REALM, + UserRoles.SAVE_FILE, + }; + + /** + * Basic role memberships for client users + */ + final String[] CLIENT_ROLES = { + UserRoles.ACCESS_REALM, + UserRoles.LOGIN, + UserRoles.READ, + UserRoles.PREPARE_STATEMENT, + UserRoles.CMD_CHANNEL_GENERATE, + UserRoles.LOAD_FILE, + UserRoles.REGISTER_CATEGORY + }; + + final String[] ADMIN_READALL = new String[] { + UserRoles.GRANT_FILES_READ_ALL, + UserRoles.GRANT_HOSTS_READ_ALL, + UserRoles.GRANT_VMS_READ_BY_USERNAME_ALL, + UserRoles.GRANT_VMS_READ_BY_VM_ID_ALL, + UserRoles.GRANT_AGENTS_READ_ALL + }; + + final String[] CMD_CHANNEL_GRANT_ALL_ACTIONS = new String[] { + UserRoles.GRANT_CMD_CHANNEL_DUMP_HEAP, + UserRoles.GRANT_CMD_CHANNEL_GARBAGE_COLLECT, + UserRoles.GRANT_CMD_CHANNEL_GRANT_THREAD_HARVESTER, + UserRoles.GRANT_CMD_CHANNEL_JMX_TOGGLE_NOTIFICATION, + UserRoles.GRANT_CMD_CHANNEL_KILLVM, + UserRoles.GRANT_CMD_CHANNEL_PING, + UserRoles.GRANT_CMD_PROFILE_VM + }; +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup-command/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/UserSetup.java Wed Sep 02 15:27:53 2015 +0200 @@ -0,0 +1,52 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.setup.command.internal.model; + + +interface UserSetup extends PersistableSetup { + + /** + * Defines a user {@code username} with password {@code password}. + * + * @param username + * @param password + * @param comment An optional comment added to persistent files. May be + * {@code null} + */ + void createUser(String username, char[] password, String comment); + +}
--- a/setup-command/command/src/test/java/com/redhat/thermostat/setup/command/internal/CredentialFinderTest.java Fri Aug 21 15:52:23 2015 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,227 +0,0 @@ -/* - * Copyright 2012-2015 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat 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 - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.setup.command.internal; - -import com.redhat.thermostat.shared.config.CommonPaths; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class CredentialFinderTest { - private File systemConfigDir; - private File userConfigDir; - - private File realFile1; - private File realFile2; - private File fileToCreate1; - private File fileToCreate2; - - private CommonPaths paths; - - @Before - public void setUp() throws IOException { - systemConfigDir = Files.createTempDirectory("system-config-dir").toFile(); - userConfigDir = Files.createTempDirectory("user-config-dir").toFile(); - fileToCreate1 = new File(systemConfigDir.toString(), "does-not-exist"); - fileToCreate2 = new File(userConfigDir.toString(), "does-not-exist"); - - realFile1 = Files.createTempFile("credentialfinder-unit-test", null).toFile(); - realFile2 = Files.createTempFile("credentialfinder-unit-test", null).toFile(); - - paths = mock(CommonPaths.class); - when(paths.getSystemConfigurationDirectory()).thenReturn(systemConfigDir); - when(paths.getUserConfigurationDirectory()).thenReturn(userConfigDir); - } - - @After - public void tearDown() { - systemConfigDir.delete(); - userConfigDir.delete(); - realFile1.delete(); - realFile2.delete(); - } - - @Test - public void verifyFileFromUserHomeIsUsedIfSystemHomeIsNotUsable() throws IOException { - CredentialFinder finder = new CredentialFinder(paths) { - @Override - File getConfigurationFile(File directory, String name) { - if (directory == systemConfigDir) { - return new File("/does-not-exist/really-it-doesn't"); - } else if (directory == userConfigDir) { - return realFile1; - } - throw new AssertionError("Unknown test case"); - } - }; - - File config = finder.getConfiguration("web.auth"); - assertEquals(realFile1, config); - } - - @Test - public void verifyFileFromSystemHomeIsUsedIfBothAreUsable() throws IOException { - final File systemFile = realFile1; - final File userFile = realFile2; - - CredentialFinder finder = new CredentialFinder(paths) { - @Override - File getConfigurationFile(File directory, String name) { - if (directory == systemConfigDir) { - return systemFile; - } else if (directory == userConfigDir) { - return userFile; - } - throw new AssertionError("Unknown test case"); - } - }; - - File config = finder.getConfiguration("web.auth"); - assertEquals(systemFile, config); - } - - @Test - public void verifyFileFromSystemHomeIsUsedIfUserHomeNotUsable() throws IOException { - CredentialFinder finder = new CredentialFinder(paths) { - @Override - File getConfigurationFile(File directory, String name) { - if (directory == systemConfigDir) { - return realFile1; - } else if (directory == userConfigDir) { - return new File("/does-not-exist/really-it-doesn't"); - } - throw new AssertionError("Unknown test case"); - } - }; - - File config = finder.getConfiguration("web.auth"); - assertEquals(realFile1, config); - } - - - @Test - public void verifyFileFromSystemHomeIsUsedIfFileDoesNotExist() throws IOException { - CredentialFinder finder = new CredentialFinder(paths) { - @Override - File getConfigurationFile(File directory, String name) { - if (directory == systemConfigDir) { - return fileToCreate1; - } else if (directory == userConfigDir) { - return fileToCreate2; - } - throw new AssertionError("Unknown test case"); - } - }; - - File config = finder.getConfiguration("web.auth"); - assertEquals(fileToCreate1, config); - } - - @Test - public void verifyFileFromUserHomeIsUsedIfSystemHomeIsNotUsableAndFileDoesNotExist() throws IOException { - systemConfigDir.setReadOnly(); - - CredentialFinder finder = new CredentialFinder(paths) { - @Override - File getConfigurationFile(File directory, String name) { - if (directory == systemConfigDir) { - return fileToCreate1; - } else if (directory == userConfigDir) { - return fileToCreate2; - } - throw new AssertionError("Unknown test case"); - } - }; - - File config = finder.getConfiguration("web.auth"); - assertEquals(fileToCreate2, config); - } - - @Test - public void verifyIsNotUsableWhenNotIsFile() throws IOException { - File mockFile = mock(File.class); - when(mockFile.exists()).thenReturn(true); - when(mockFile.isFile()).thenReturn(false); - when(mockFile.canRead()).thenReturn(true); - when(mockFile.canWrite()).thenReturn(true); - CredentialFinder finder = new CredentialFinder(paths); - - assertFalse(finder.isUsable(mockFile)); - } - - @Test - public void verifyIsNotUsableWhenNotCanRead() throws IOException { - File mockFile = mock(File.class); - when(mockFile.exists()).thenReturn(true); - when(mockFile.isFile()).thenReturn(true); - when(mockFile.canRead()).thenReturn(false); - when(mockFile.canWrite()).thenReturn(true); - CredentialFinder finder = new CredentialFinder(paths); - - assertFalse(finder.isUsable(mockFile)); - } - - @Test - public void verifyIsNotUsableWhenNotCanWrite() throws IOException { - File mockFile = mock(File.class); - when(mockFile.exists()).thenReturn(true); - when(mockFile.isFile()).thenReturn(true); - when(mockFile.canRead()).thenReturn(true); - when(mockFile.canWrite()).thenReturn(false); - CredentialFinder finder = new CredentialFinder(paths); - - assertFalse(finder.isUsable(mockFile)); - } - - @Test - public void verifyIsUsableWhenFileDoesNotExistAndHasNoParent() throws IOException { - File fileToCreate = new File("does-not-exist"); - CredentialFinder finder = new CredentialFinder(paths); - - assertTrue(finder.isUsable(fileToCreate)); - } -}
--- a/setup-command/command/src/test/java/com/redhat/thermostat/setup/command/internal/ThermostatSetupImplTest.java Fri Aug 21 15:52:23 2015 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,479 +0,0 @@ -/* - * Copyright 2012-2015 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat 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 - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.setup.command.internal; - -import com.redhat.thermostat.common.ActionEvent; -import com.redhat.thermostat.common.ActionListener; -import com.redhat.thermostat.common.ActionNotifier; -import com.redhat.thermostat.common.cli.AbstractStateNotifyingCommand; -import com.redhat.thermostat.common.cli.Console; -import com.redhat.thermostat.common.tools.ApplicationState; -import com.redhat.thermostat.launcher.Launcher; -import com.redhat.thermostat.shared.config.CommonPaths; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.io.PrintStream; -import java.nio.file.FileVisitResult; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.Arrays; -import java.util.Collection; -import java.util.Properties; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.isA; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.mockito.Mockito.times; - -public class ThermostatSetupImplTest { - - private Path testRoot; - private Path thermostatSysHome; - private Path thermostatUserHome; - private Path sysConfigDir; - private Path userConfigDir; - private Path userDataDir; - private Path userAgentAuth; - private Path credentialsFile; - private Path userPropertiesFile; - private Path rolesPropertiesFile; - private static final String username = "mongodevuser"; - private static final String password = "mongodevpassword"; - private static final String[] STORAGE_START_ARGS = {"storage", "--start", "--permitLocalhostException"}; - private static final String[] STORAGE_STOP_ARGS = {"storage", "--stop"}; - private static final String WEB_AUTH_FILE = "web.auth"; - private static final String USERS_PROPERTIES = "thermostat-users.properties"; - private static final String ROLES_PROPERTIES = "thermostat-roles.properties"; - private static final String THERMOSTAT_AGENT = "thermostat-agent"; - - private ThermostatSetupImpl tSetup; - private CommonPaths paths; - private Launcher mockLauncher; - private ByteArrayOutputStream out; - private Console console; - private static ActionEvent<ApplicationState> mockActionEvent; - private static Collection<ActionListener<ApplicationState>> listeners; - private CredentialFinder mockCredentialFinder; - - private void makeTempFilesAndDirectories() throws IOException { - testRoot = Files.createTempDirectory("thermostat"); - - thermostatSysHome = testRoot.resolve("system"); - Files.createDirectory(thermostatSysHome); - thermostatUserHome = testRoot.resolve("user"); - Files.createDirectory(thermostatUserHome); - - sysConfigDir = thermostatSysHome.resolve("etc"); - Files.createDirectories(sysConfigDir); - Path sysLibDir = thermostatSysHome.resolve("lib"); - Files.createDirectories(sysLibDir); - - userConfigDir = thermostatUserHome.resolve("etc"); - Files.createDirectories(userConfigDir); - userDataDir = thermostatUserHome.resolve("data"); - Files.createDirectories(userDataDir); - - userAgentAuth = userConfigDir.resolve("agent.auth"); - credentialsFile = sysConfigDir.resolve(WEB_AUTH_FILE); - userPropertiesFile = sysConfigDir.resolve(USERS_PROPERTIES); - rolesPropertiesFile = sysConfigDir.resolve(ROLES_PROPERTIES); - - //create a dummy create-user.js - Path createUserScript = sysLibDir.resolve("create-user.js"); - Files.write(createUserScript, new byte[]{}); - } - - @Before - public void setup() throws IOException, InterruptedException { - makeTempFilesAndDirectories(); - - out = new ByteArrayOutputStream(); - console = mock(Console.class); - when(console.getOutput()).thenReturn(new PrintStream(out)); - - paths = mock(CommonPaths.class); - when(paths.getSystemThermostatHome()).thenReturn(thermostatSysHome.toFile()); - when(paths.getUserThermostatHome()).thenReturn(thermostatUserHome.toFile()); - when(paths.getUserAgentAuthConfigFile()).thenReturn(userAgentAuth.toFile()); - when(paths.getSystemConfigurationDirectory()).thenReturn(sysConfigDir.toFile()); - when(paths.getUserConfigurationDirectory()).thenReturn(userConfigDir.toFile()); - - mockLauncher = mock(Launcher.class); - mockActionEvent = mock(ActionEvent.class); - AbstractStateNotifyingCommand mockStorageCommand = mock(AbstractStateNotifyingCommand.class); - ActionNotifier<ApplicationState> mockNotifier = mock(ActionNotifier.class); - when(mockStorageCommand.getNotifier()).thenReturn(mockNotifier); - mockActionEvent = mock(ActionEvent.class); - when(mockActionEvent.getSource()).thenReturn(mockStorageCommand); - when(mockActionEvent.getPayload()).thenReturn(new String("Test String")); - mockCredentialFinder = mock(CredentialFinder.class); - when(mockCredentialFinder.getConfiguration(WEB_AUTH_FILE)).thenReturn(credentialsFile.toFile()); - when(mockCredentialFinder.getConfiguration(USERS_PROPERTIES)).thenReturn(userPropertiesFile.toFile()); - when(mockCredentialFinder.getConfiguration(ROLES_PROPERTIES)).thenReturn(rolesPropertiesFile.toFile()); - - tSetup = new ThermostatSetupImpl(mockLauncher, paths, console, mockCredentialFinder) { - @Override - int runMongo() { - //instead of running mongo through ProcessBuilder - //we need to always return 0 for success in tests - return 0; - } - }; - } - - @After - public void teardown() throws IOException { - paths = null; - mockLauncher = null; - - Files.walkFileTree(testRoot, new SimpleFileVisitor<Path>() { - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - Files.delete(file); - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { - if (exc == null) { - Files.delete(dir); - return FileVisitResult.CONTINUE; - } else { - throw exc; - } - } - }); - } - - @Test - public void testUnlockThermostatUnlockFileCreated() throws IOException { - String fileData; - File setupCompleteFile = new File(thermostatUserHome + "/data/setup-complete.stamp"); - Path setupCompleteFilePath = Paths.get(setupCompleteFile.toString()); - - Files.createDirectories(setupCompleteFilePath.getParent()); - tSetup.unlockThermostat(); - fileData = new String(Files.readAllBytes(setupCompleteFilePath)); - - assertTrue(setupCompleteFile.exists()); - assertTrue(fileData.contains("Temporarily unlocked")); - } - - @Test - public void testSetupMongodbUser() throws IOException { - File userDoneFile = new File(userDataDir.toString() + "/mongodb-user-done.stamp"); - File setupCompleteFile = new File(userDataDir.toString() + "/setup-complete.stamp"); - - //create path to webapp so web.auth creation is invoked - //when ThermostatSetup.createMongodbUser() is called - Path webAppPath = thermostatSysHome.resolve("webapp"); - Files.createDirectories(webAppPath); - - doAnswer(new Answer<Void>() { - @Override - public Void answer(InvocationOnMock invocation) throws Throwable { - Object[] args = invocation.getArguments(); - listeners = (Collection<ActionListener<ApplicationState>>) args[1]; - - when(mockActionEvent.getActionId()).thenReturn(ApplicationState.START); - - for (ActionListener<ApplicationState> listener : listeners) { - listener.actionPerformed(mockActionEvent); - } - return null; - } - }).when(mockLauncher).run(eq(STORAGE_START_ARGS), isA(Collection.class), anyBoolean()); - - boolean exTriggered = false; - try { - tSetup.createMongodbUser(username, password.toCharArray()); - } catch (MongodbUserSetupException e) { - exTriggered = true; - } - - assertFalse(exTriggered); - - verify(mockLauncher, times(1)).run(eq(STORAGE_START_ARGS), isA(Collection.class), anyBoolean()); - verify(mockLauncher, times(1)).run(eq(STORAGE_STOP_ARGS), isA(Collection.class), anyBoolean()); - verify(mockActionEvent, times(1)).getActionId(); - - assertTrue(userDoneFile.exists()); - assertTrue(setupCompleteFile.exists()); - - assertTrue(credentialsFile.toFile().exists()); - String credentialsData = new String(Files.readAllBytes(credentialsFile)); - assertTrue(credentialsData.contains("storage.username=" + username)); - assertTrue(credentialsData.contains("storage.password=" + password)); - - String setupCompleteData = new String(Files.readAllBytes(setupCompleteFile.toPath())); - assertTrue(setupCompleteData.contains("Created by Thermostat Setup")); - } - - @Test - public void testStorageStartFail() { - doAnswer(new Answer<Void>() { - @Override - public Void answer(InvocationOnMock invocation) throws Throwable { - Object[] args = invocation.getArguments(); - listeners = (Collection<ActionListener<ApplicationState>>) args[1]; - - when(mockActionEvent.getActionId()).thenReturn(ApplicationState.FAIL); - - for (ActionListener<ApplicationState> listener : listeners) { - listener.actionPerformed(mockActionEvent); - } - return null; - } - }).when(mockLauncher).run(eq(STORAGE_START_ARGS), isA(Collection.class), anyBoolean()); - - try { - tSetup.createMongodbUser(username, password.toCharArray()); - //shouldn't get here - fail(); - } catch (MongodbUserSetupException e) { - assertTrue(e.getMessage().contains("Thermostat storage failed to start")); - } - } - - @Test - public void testStorageStopFail() { - doAnswer(new Answer<Void>() { - @Override - public Void answer(InvocationOnMock invocation) throws Throwable { - Object[] args = invocation.getArguments(); - listeners = (Collection<ActionListener<ApplicationState>>) args[1]; - - when(mockActionEvent.getActionId()).thenReturn(ApplicationState.START); - - for (ActionListener<ApplicationState> listener : listeners) { - listener.actionPerformed(mockActionEvent); - } - return null; - } - }).when(mockLauncher).run(eq(STORAGE_START_ARGS), isA(Collection.class), anyBoolean()); - - doAnswer(new Answer<Void>() { - @Override - public Void answer(InvocationOnMock invocation) throws Throwable { - Object[] args = invocation.getArguments(); - listeners = (Collection<ActionListener<ApplicationState>>) args[1]; - - when(mockActionEvent.getActionId()).thenReturn(ApplicationState.FAIL); - - for (ActionListener<ApplicationState> listener : listeners) { - listener.actionPerformed(mockActionEvent); - } - return null; - } - }).when(mockLauncher).run(eq(STORAGE_STOP_ARGS), isA(Collection.class), anyBoolean()); - - try { - tSetup.createMongodbUser(username, password.toCharArray()); - //shouldn't get here - fail(); - } catch (MongodbUserSetupException e) { - assertTrue(e.getMessage().contains("Thermostat storage failed to stop")); - } - } - - @Test - public void testCreateMongodbUserFail() { - tSetup = new ThermostatSetupImpl(mockLauncher, paths, console, mockCredentialFinder) { - @Override - int runMongo() { - //return non-zero val to test failure - return 1; - } - }; - - doAnswer(new Answer<Void>() { - @Override - public Void answer(InvocationOnMock invocation) throws Throwable { - Object[] args = invocation.getArguments(); - listeners = (Collection<ActionListener<ApplicationState>>) args[1]; - - when(mockActionEvent.getActionId()).thenReturn(ApplicationState.START); - - for (ActionListener<ApplicationState> listener : listeners) { - listener.actionPerformed(mockActionEvent); - } - return null; - } - }).when(mockLauncher).run(eq(STORAGE_START_ARGS), isA(Collection.class), anyBoolean()); - - try { - tSetup.createMongodbUser(username, password.toCharArray()); - //shouldn't get here - fail(); - } catch (MongodbUserSetupException e) { - assertTrue(e.getMessage().contains("Mongodb user setup failed")); - } - } - - @Test - public void testSetupThermostatUser() throws IOException { - String clientUser = "client-tester"; - String agentUser = "agent-tester"; - String userPassword = "tester"; - - String[] agentRoles = new String[] { - UserRoles.CMD_CHANNEL_VERIFY, - UserRoles.LOGIN, - UserRoles.PREPARE_STATEMENT, - UserRoles.PURGE, - UserRoles.REGISTER_CATEGORY, - UserRoles.ACCESS_REALM, - UserRoles.SAVE_FILE, - UserRoles.WRITE, - UserRoles.GRANT_FILES_WRITE_ALL, - }; - String[] clientRoles = new String[] { - UserRoles.GRANT_AGENTS_READ_ALL, - UserRoles.CMD_CHANNEL_GENERATE, - UserRoles.GRANT_HOSTS_READ_ALL, - UserRoles.LOAD_FILE, - UserRoles.LOGIN, - UserRoles.PREPARE_STATEMENT, - UserRoles.READ, - UserRoles.ACCESS_REALM, - UserRoles.REGISTER_CATEGORY, - UserRoles.GRANT_VMS_READ_BY_USERNAME_ALL, - UserRoles.GRANT_VMS_READ_BY_VM_ID_ALL, - UserRoles.GRANT_FILES_READ_ALL, - UserRoles.WRITE, - }; - - tSetup.createThermostatUser(agentUser, userPassword.toCharArray(), agentRoles); - tSetup.createThermostatUser(clientUser, userPassword.toCharArray(), clientRoles); - - //check agent credentials file - assertTrue(userAgentAuth.toFile().exists()); - String userAgentAuthData = new String(Files.readAllBytes(userAgentAuth)); - assertTrue(userAgentAuthData.contains("username=agent-tester")); - assertTrue(userAgentAuthData.contains("password=tester")); - - //check AgentUser file - assertTrue(userPropertiesFile.toFile().exists()); - - //check clientAdmin file - assertTrue(rolesPropertiesFile.toFile().exists()); - } - - @Test - public void testWebAppInstalledSuccess() throws IOException { - Path webAppPath = thermostatSysHome.resolve("webapp"); - Files.createDirectories(webAppPath); - assertTrue(tSetup.isWebAppInstalled()); - } - - @Test - public void testWebAppInstalledFail() throws IOException { - //Call isWebAppInstalled() without creating - //a THERMOSTAT_SYS_HOME/webapp directory - assertFalse(tSetup.isWebAppInstalled()); - } - - @Test - public void testPropertiesWriter() throws IOException { - String key = THERMOSTAT_AGENT; - String[] roles = new String[] { - UserRoles.LOGIN, - UserRoles.PREPARE_STATEMENT, - UserRoles.PURGE, - UserRoles.REGISTER_CATEGORY, - }; - StringBuilder rolesBuilder = new StringBuilder(); - for (int i = 0; i < roles.length - 1; i++) { - rolesBuilder.append(roles[i] + ", " + System.getProperty("line.separator")); - } - rolesBuilder.append(roles[roles.length - 1]); - String value = rolesBuilder.toString(); - - Properties propsToStore = new Properties(); - propsToStore.setProperty(key, value); - FileOutputStream roleStream = new FileOutputStream(rolesPropertiesFile.toFile()); - propsToStore.store(new ThermostatSetupImpl.PropertiesWriter(roleStream), null); - - Properties propsToLoad = new Properties(); - propsToLoad.load(new FileInputStream(rolesPropertiesFile.toFile())); - String[] loadedRoles = propsToLoad.getProperty(key).split(",\\s+"); - - assertTrue(Arrays.asList(roles).containsAll(Arrays.asList(loadedRoles))); - } - - @Test - public void testCrendentialFileCreatedIfNotExists() throws IOException { - File agentAuthFile = mock(File.class); - when(agentAuthFile.exists()).thenReturn(false); - when(agentAuthFile.toPath()).thenReturn(userAgentAuth); - - tSetup.createCredentialFile(agentAuthFile); - - assertTrue(userAgentAuth.toFile().exists()); - } - - @Test - public void testCrendentialFileNotCreatedIfAlreadyExists() throws IOException { - File agentAuthFile = mock(File.class); - when(agentAuthFile.exists()).thenReturn(true); - when(agentAuthFile.toPath()).thenReturn(userAgentAuth); - - tSetup.createCredentialFile(agentAuthFile); - - assertFalse(userAgentAuth.toFile().exists()); - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup-command/command/src/test/java/com/redhat/thermostat/setup/command/internal/model/CredentialFinderTest.java Wed Sep 02 15:27:53 2015 +0200 @@ -0,0 +1,229 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.setup.command.internal.model; + +import com.redhat.thermostat.setup.command.internal.model.CredentialFinder; +import com.redhat.thermostat.shared.config.CommonPaths; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class CredentialFinderTest { + private File systemConfigDir; + private File userConfigDir; + + private File realFile1; + private File realFile2; + private File fileToCreate1; + private File fileToCreate2; + + private CommonPaths paths; + + @Before + public void setUp() throws IOException { + systemConfigDir = Files.createTempDirectory("system-config-dir").toFile(); + userConfigDir = Files.createTempDirectory("user-config-dir").toFile(); + fileToCreate1 = new File(systemConfigDir.toString(), "does-not-exist"); + fileToCreate2 = new File(userConfigDir.toString(), "does-not-exist"); + + realFile1 = Files.createTempFile("credentialfinder-unit-test", null).toFile(); + realFile2 = Files.createTempFile("credentialfinder-unit-test", null).toFile(); + + paths = mock(CommonPaths.class); + when(paths.getSystemConfigurationDirectory()).thenReturn(systemConfigDir); + when(paths.getUserConfigurationDirectory()).thenReturn(userConfigDir); + } + + @After + public void tearDown() { + systemConfigDir.delete(); + userConfigDir.delete(); + realFile1.delete(); + realFile2.delete(); + } + + @Test + public void verifyFileFromUserHomeIsUsedIfSystemHomeIsNotUsable() throws IOException { + CredentialFinder finder = new CredentialFinder(paths) { + @Override + File getConfigurationFile(File directory, String name) { + if (directory == systemConfigDir) { + return new File("/does-not-exist/really-it-doesn't"); + } else if (directory == userConfigDir) { + return realFile1; + } + throw new AssertionError("Unknown test case"); + } + }; + + File config = finder.getConfiguration("web.auth"); + assertEquals(realFile1, config); + } + + @Test + public void verifyFileFromSystemHomeIsUsedIfBothAreUsable() throws IOException { + final File systemFile = realFile1; + final File userFile = realFile2; + + CredentialFinder finder = new CredentialFinder(paths) { + @Override + File getConfigurationFile(File directory, String name) { + if (directory == systemConfigDir) { + return systemFile; + } else if (directory == userConfigDir) { + return userFile; + } + throw new AssertionError("Unknown test case"); + } + }; + + File config = finder.getConfiguration("web.auth"); + assertEquals(systemFile, config); + } + + @Test + public void verifyFileFromSystemHomeIsUsedIfUserHomeNotUsable() throws IOException { + CredentialFinder finder = new CredentialFinder(paths) { + @Override + File getConfigurationFile(File directory, String name) { + if (directory == systemConfigDir) { + return realFile1; + } else if (directory == userConfigDir) { + return new File("/does-not-exist/really-it-doesn't"); + } + throw new AssertionError("Unknown test case"); + } + }; + + File config = finder.getConfiguration("web.auth"); + assertEquals(realFile1, config); + } + + + @Test + public void verifyFileFromSystemHomeIsUsedIfFileDoesNotExist() throws IOException { + CredentialFinder finder = new CredentialFinder(paths) { + @Override + File getConfigurationFile(File directory, String name) { + if (directory == systemConfigDir) { + return fileToCreate1; + } else if (directory == userConfigDir) { + return fileToCreate2; + } + throw new AssertionError("Unknown test case"); + } + }; + + File config = finder.getConfiguration("web.auth"); + assertEquals(fileToCreate1, config); + } + + @Test + public void verifyFileFromUserHomeIsUsedIfSystemHomeIsNotUsableAndFileDoesNotExist() throws IOException { + systemConfigDir.setReadOnly(); + + CredentialFinder finder = new CredentialFinder(paths) { + @Override + File getConfigurationFile(File directory, String name) { + if (directory == systemConfigDir) { + return fileToCreate1; + } else if (directory == userConfigDir) { + return fileToCreate2; + } + throw new AssertionError("Unknown test case"); + } + }; + + File config = finder.getConfiguration("web.auth"); + assertEquals(fileToCreate2, config); + } + + @Test + public void verifyIsNotUsableWhenNotIsFile() throws IOException { + File mockFile = mock(File.class); + when(mockFile.exists()).thenReturn(true); + when(mockFile.isFile()).thenReturn(false); + when(mockFile.canRead()).thenReturn(true); + when(mockFile.canWrite()).thenReturn(true); + CredentialFinder finder = new CredentialFinder(paths); + + assertFalse(finder.isUsable(mockFile)); + } + + @Test + public void verifyIsNotUsableWhenNotCanRead() throws IOException { + File mockFile = mock(File.class); + when(mockFile.exists()).thenReturn(true); + when(mockFile.isFile()).thenReturn(true); + when(mockFile.canRead()).thenReturn(false); + when(mockFile.canWrite()).thenReturn(true); + CredentialFinder finder = new CredentialFinder(paths); + + assertFalse(finder.isUsable(mockFile)); + } + + @Test + public void verifyIsNotUsableWhenNotCanWrite() throws IOException { + File mockFile = mock(File.class); + when(mockFile.exists()).thenReturn(true); + when(mockFile.isFile()).thenReturn(true); + when(mockFile.canRead()).thenReturn(true); + when(mockFile.canWrite()).thenReturn(false); + CredentialFinder finder = new CredentialFinder(paths); + + assertFalse(finder.isUsable(mockFile)); + } + + @Test + public void verifyIsUsableWhenFileDoesNotExistAndHasNoParent() throws IOException { + File fileToCreate = new File("does-not-exist"); + CredentialFinder finder = new CredentialFinder(paths); + + assertTrue(finder.isUsable(fileToCreate)); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup-command/command/src/test/java/com/redhat/thermostat/setup/command/internal/model/CredentialsFileCreatorTest.java Wed Sep 02 15:27:53 2015 +0200 @@ -0,0 +1,91 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.setup.command.internal.model; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class CredentialsFileCreatorTest { + + private Path testRoot; + private File testFile; + private CredentialsFileCreator creator; + + @Before + public void setup() throws IOException { + creator = new CredentialsFileCreator(); + testRoot = TestRootHelper.createTestRootDirectory(getClass().getName()); + testFile = new File(testRoot.toFile(), "foobar.auth"); + } + + @After + public void tearDown() throws IOException { + TestRootHelper.recursivelyRemoveTestRootDirectory(testRoot); + } + + @Test + public void testCrendentialFileCreatedIfNotExists() throws IOException { + File agentAuthFile = mock(File.class); + when(agentAuthFile.exists()).thenReturn(false); + when(agentAuthFile.toPath()).thenReturn(testFile.toPath()); + + creator.create(agentAuthFile); + + assertTrue(testFile.exists()); + } + + @Test + public void testCrendentialFileNotCreatedIfAlreadyExists() throws IOException { + File agentAuthFile = mock(File.class); + when(agentAuthFile.exists()).thenReturn(true); + when(agentAuthFile.toPath()).thenReturn(testFile.toPath()); + + creator.create(agentAuthFile); + + assertFalse(testFile.exists()); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup-command/command/src/test/java/com/redhat/thermostat/setup/command/internal/model/MongodbUserSetupTest.java Wed Sep 02 15:27:53 2015 +0200 @@ -0,0 +1,355 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.setup.command.internal.model; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isA; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collection; +import java.util.Properties; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import com.redhat.thermostat.common.ActionEvent; +import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.common.ActionNotifier; +import com.redhat.thermostat.common.cli.AbstractStateNotifyingCommand; +import com.redhat.thermostat.common.cli.Console; +import com.redhat.thermostat.common.tools.ApplicationState; +import com.redhat.thermostat.launcher.Launcher; +import com.redhat.thermostat.shared.config.CommonPaths; + +public class MongodbUserSetupTest { + + private MongodbUserSetup mongoSetup; + private StampFiles stampFiles; + private Launcher mockLauncher; + private CredentialFinder finder; + private CredentialsFileCreator fileCreator; + private Console console; + private CommonPaths paths; + private StructureInformation info; + + @Before + public void setup() { + paths = mock(CommonPaths.class); + finder = new CredentialFinder(paths); + console = mock(Console.class); + when(console.getOutput()).thenReturn(mock(PrintStream.class)); + fileCreator = mock(CredentialsFileCreator.class); + stampFiles = mock(StampFiles.class); + info = mock(StructureInformation.class); + mockLauncher = mock(Launcher.class); + mongoSetup = new MongodbUserSetup(new UserCredsValidator(), mockLauncher, finder, fileCreator, console, paths, stampFiles, info) { + @Override + int runMongo() { + //instead of running mongo through ProcessBuilder + //we need to always return 0 for success in tests + return 0; + } + }; + } + + @Test(expected = IllegalArgumentException.class) + public void emptyUsernameDisallowed() { + mongoSetup.createUser("", new char[] { 't' }, null); + } + + @Test(expected = IllegalArgumentException.class) + public void nullUsernameDisallowed() { + mongoSetup.createUser(null, new char[] { 't' }, null); + } + + @Test(expected = IllegalArgumentException.class) + public void nullPasswordDisallowed() { + mongoSetup.createUser("somebody", null, null); + } + + @Test(expected = IllegalArgumentException.class) + public void emptyPasswordDisallowed() { + mongoSetup.createUser("somebody", new char[] {}, null); + } + + @Test + public void testUnlockThermostat() throws IOException { + mongoSetup.unlockThermostat(); + ArgumentCaptor<String> argCaptor = ArgumentCaptor.forClass(String.class); + verify(stampFiles).createSetupCompleteStamp(argCaptor.capture()); + String contentValue = argCaptor.getValue(); + assertTrue(contentValue.startsWith("Temporarily unlocked thermostat")); + assertTrue(contentValue.contains(ThermostatSetup.PROGRAM_NAME)); + } + + @SuppressWarnings("unchecked") + @Test + public void testStorageStartFail() { + final ActionEvent<ApplicationState> mockActionEvent = mock(ActionEvent.class); + AbstractStateNotifyingCommand mockStorage = mock(AbstractStateNotifyingCommand.class); + when(mockActionEvent.getSource()).thenReturn(mockStorage); + when(mockStorage.getNotifier()).thenReturn(mock(ActionNotifier.class)); + final Collection<ActionListener<ApplicationState>> listeners[] = new Collection[1]; + doAnswer(new Answer<Void>() { + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + Object[] args = invocation.getArguments(); + listeners[0] = (Collection<ActionListener<ApplicationState>>) args[1]; + + when(mockActionEvent.getActionId()).thenReturn(ApplicationState.FAIL); + + for (ActionListener<ApplicationState> listener : listeners[0]) { + listener.actionPerformed(mockActionEvent); + } + return null; + } + }).when(mockLauncher).run(eq(MongodbUserSetup.STORAGE_START_ARGS), isA(Collection.class), anyBoolean()); + + try { + mongoSetup.createUser("foo-user", new char[] { 't' }, "bar comment"); + mongoSetup.commit(); + fail("mongosetup should have failed"); + } catch (IOException e) { + assertTrue(e.getMessage().contains("Thermostat storage failed to start")); + } + } + + @SuppressWarnings("unchecked") + @Test + public void testStorageStopFail() { + final ActionEvent<ApplicationState> mockActionEvent = mock(ActionEvent.class); + AbstractStateNotifyingCommand mockStorage = mock(AbstractStateNotifyingCommand.class); + when(mockActionEvent.getSource()).thenReturn(mockStorage); + when(mockStorage.getNotifier()).thenReturn(mock(ActionNotifier.class)); + final Collection<ActionListener<ApplicationState>> listeners[] = new Collection[1]; + doAnswer(new Answer<Void>() { + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + Object[] args = invocation.getArguments(); + listeners[0] = (Collection<ActionListener<ApplicationState>>) args[1]; + + when(mockActionEvent.getActionId()).thenReturn(ApplicationState.START); + + for (ActionListener<ApplicationState> listener : listeners[0]) { + listener.actionPerformed(mockActionEvent); + } + return null; + } + }).when(mockLauncher).run(eq(MongodbUserSetup.STORAGE_START_ARGS), isA(Collection.class), anyBoolean()); + + doAnswer(new Answer<Void>() { + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + Object[] args = invocation.getArguments(); + listeners[0] = (Collection<ActionListener<ApplicationState>>) args[1]; + + when(mockActionEvent.getActionId()).thenReturn(ApplicationState.FAIL); + + for (ActionListener<ApplicationState> listener : listeners[0]) { + listener.actionPerformed(mockActionEvent); + } + return null; + } + }).when(mockLauncher).run(eq(MongodbUserSetup.STORAGE_STOP_ARGS), isA(Collection.class), anyBoolean()); + + try { + mongoSetup.createUser("foo-user", new char[] { 't' }, "bar comment"); + mongoSetup.commit(); + fail("mongosetup should have failed"); + } catch (IOException e) { + assertTrue(e.getMessage().contains("Thermostat storage failed to stop")); + } + } + + @SuppressWarnings("unchecked") + @Test + public void testCreateMongodbUserFail() throws IOException { + final ActionEvent<ApplicationState> mockActionEvent = mock(ActionEvent.class); + AbstractStateNotifyingCommand mockStorage = mock(AbstractStateNotifyingCommand.class); + when(mockActionEvent.getSource()).thenReturn(mockStorage); + when(mockStorage.getNotifier()).thenReturn(mock(ActionNotifier.class)); + final Collection<ActionListener<ApplicationState>> listeners[] = new Collection[1]; + Path testRoot = TestRootHelper.createTestRootDirectory(getClass().getName()); + when(paths.getSystemThermostatHome()).thenReturn(testRoot.toFile()); + try { + mongoSetup = new MongodbUserSetup(new UserCredsValidator(), mockLauncher, finder, fileCreator, console, paths, stampFiles, info) { + @Override + int runMongo() { + //return non-zero val to test failure + return 1; + } + }; + + doAnswer(new Answer<Void>() { + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + Object[] args = invocation.getArguments(); + listeners[0] = (Collection<ActionListener<ApplicationState>>) args[1]; + + when(mockActionEvent.getActionId()).thenReturn(ApplicationState.START); + + for (ActionListener<ApplicationState> listener : listeners[0]) { + listener.actionPerformed(mockActionEvent); + } + return null; + } + }).when(mockLauncher).run(eq(MongodbUserSetup.STORAGE_START_ARGS), isA(Collection.class), anyBoolean()); + + try { + mongoSetup.createUser("foo-user", new char[] { 't' }, "bar comment"); + mongoSetup.commit(); + fail("mongosetup should have failed"); + } catch (IOException e) { + assertTrue(e.getMessage().contains("Mongodb user setup failed")); + } + verify(stampFiles).deleteMongodbUserStamp(); + verify(stampFiles).deleteSetupCompleteStamp(); + } finally { + TestRootHelper.recursivelyRemoveTestRootDirectory(testRoot); + } + } + + @SuppressWarnings("unchecked") + @Test + public void testSetupMongodbUser() throws IOException { + Path testRoot = TestRootHelper.createTestRootDirectory(getClass().getName()); + when(paths.getSystemThermostatHome()).thenReturn(testRoot.toFile()); + try { + File userDoneFile = File.createTempFile("thermostat", getClass().getName()); + userDoneFile.deleteOnExit(); + File setupCompleteFile = File.createTempFile("thermostat", getClass().getName()); + setupCompleteFile.deleteOnExit(); + when(stampFiles.getMongodbStampFile()).thenReturn(userDoneFile); + when(stampFiles.getSetupCompleteStampFile()).thenReturn(setupCompleteFile); + + // Fake webapp is installed. + when(info.isWebAppInstalled()).thenReturn(true); + + File mockWebAuthFile = File.createTempFile("thermostat", getClass().getName()); + mockWebAuthFile.deleteOnExit(); + finder = mock(CredentialFinder.class); + when(finder.getConfiguration("web.auth")).thenReturn(mockWebAuthFile); + + final ActionEvent<ApplicationState> mockActionEvent = mock(ActionEvent.class); + AbstractStateNotifyingCommand mockStorage = mock(AbstractStateNotifyingCommand.class); + when(mockActionEvent.getSource()).thenReturn(mockStorage); + when(mockStorage.getNotifier()).thenReturn(mock(ActionNotifier.class)); + final Collection<ActionListener<ApplicationState>> listeners[] = new Collection[1]; + doAnswer(new Answer<Void>() { + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + Object[] args = invocation.getArguments(); + listeners[0] = (Collection<ActionListener<ApplicationState>>) args[1]; + + when(mockActionEvent.getActionId()).thenReturn(ApplicationState.START); + + for (ActionListener<ApplicationState> listener : listeners[0]) { + listener.actionPerformed(mockActionEvent); + } + return null; + } + }).when(mockLauncher).run(eq(MongodbUserSetup.STORAGE_START_ARGS), isA(Collection.class), anyBoolean()); + + mongoSetup = new MongodbUserSetup(new UserCredsValidator(), mockLauncher, finder, fileCreator, console, paths, stampFiles, info) { + @Override + int runMongo() { + //instead of running mongo through ProcessBuilder + //we need to always return 0 for success in tests + return 0; + } + }; + String username = "foo-user"; + char[] password = new char[] { 't', 'e', 's', 't' }; + try { + mongoSetup.createUser(username, password, "bar comment"); + mongoSetup.commit(); + // pass + } catch (IOException e) { + e.printStackTrace(); + fail("did not expect exception"); + } + + verify(mockLauncher, times(1)).run(eq(MongodbUserSetup.STORAGE_START_ARGS), isA(Collection.class), anyBoolean()); + verify(mockLauncher, times(1)).run(eq(MongodbUserSetup.STORAGE_STOP_ARGS), isA(Collection.class), anyBoolean()); + verify(mockActionEvent, times(1)).getActionId(); + verify(fileCreator).create(mockWebAuthFile); + verify(stampFiles).createMongodbUserStamp(); + // temp unlocking calls this + verify(stampFiles, times(1)).createSetupCompleteStamp(any(String.class)); + + + + assertTrue(mockWebAuthFile.exists()); + Properties webauthProps = new Properties(); + try (FileInputStream fin = new FileInputStream(mockWebAuthFile)) { + webauthProps.load(fin); + } + assertEquals(username, webauthProps.getProperty("storage.username")); + assertEquals("test", webauthProps.getProperty("storage.password")); + // Passed in password array is expected to be cleared. + assertArrayEquals(new char[] { '\0', '\0', '\0', '\0'}, password); + } finally { + TestRootHelper.recursivelyRemoveTestRootDirectory(testRoot); + } + } + + private void assertArrayEquals(char[] expected, char[] actual) { + assertTrue(expected.length == actual.length); + for (int i = 0; i < expected.length; i++) { + assertEquals(expected[i], actual[i]); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup-command/command/src/test/java/com/redhat/thermostat/setup/command/internal/model/PropertiesWriterTest.java Wed Sep 02 15:27:53 2015 +0200 @@ -0,0 +1,101 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.setup.command.internal.model; + +import static org.junit.Assert.assertTrue; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Properties; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class PropertiesWriterTest { + + private static final String ROLES_PROPERTIES = "thermostat-roles.properties"; + private Path testRoot; + private Path rolesPropertiesFile; + + @Before + public void setup() throws IOException { + testRoot = TestRootHelper.createTestRootDirectory(getClass().getName()); + Path thermostatSysHome = testRoot.resolve("system"); + Files.createDirectory(thermostatSysHome); + Path sysConfigDir = thermostatSysHome.resolve("etc"); + Files.createDirectories(sysConfigDir); + rolesPropertiesFile = sysConfigDir.resolve(ROLES_PROPERTIES); + } + + @After + public void tearDown() throws IOException { + TestRootHelper.recursivelyRemoveTestRootDirectory(testRoot); + } + + @Test + public void testPropertiesWriter() throws IOException { + String key = "thermostat-agent"; + String[] roles = new String[] { + UserRoles.LOGIN, + UserRoles.PREPARE_STATEMENT, + UserRoles.PURGE, + UserRoles.REGISTER_CATEGORY, + }; + StringBuilder rolesBuilder = new StringBuilder(); + for (int i = 0; i < roles.length - 1; i++) { + rolesBuilder.append(roles[i] + ", " + System.getProperty("line.separator")); + } + rolesBuilder.append(roles[roles.length - 1]); + String value = rolesBuilder.toString(); + + Properties propsToStore = new Properties(); + propsToStore.setProperty(key, value); + FileOutputStream roleStream = new FileOutputStream(rolesPropertiesFile.toFile()); + propsToStore.store(new PropertiesWriter(roleStream), null); + + Properties propsToLoad = new Properties(); + propsToLoad.load(new FileInputStream(rolesPropertiesFile.toFile())); + String[] loadedRoles = propsToLoad.getProperty(key).split(",\\s+"); + + assertTrue(Arrays.asList(roles).containsAll(Arrays.asList(loadedRoles))); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup-command/command/src/test/java/com/redhat/thermostat/setup/command/internal/model/StampFilesTest.java Wed Sep 02 15:27:53 2015 +0200 @@ -0,0 +1,153 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.setup.command.internal.model; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.redhat.thermostat.shared.config.CommonPaths; + +public class StampFilesTest { + + private Path testRoot; + + @Before + public void setup() throws IOException { + testRoot = TestRootHelper.createTestRootDirectory(getClass().getName()); + } + + @After + public void teardown() throws IOException { + TestRootHelper.recursivelyRemoveTestRootDirectory(testRoot); + } + + @Test + public void testCreateDeleteSetupComplete() throws IOException { + File setupCompleteFile = getStampFileRef("setup-complete.stamp"); + + CommonPaths paths = mock(CommonPaths.class); + when(paths.getUserPersistentDataDirectory()).thenReturn(setupCompleteFile.getParentFile()); + StampFiles stampFiles = new StampFiles(paths); + + assertFalse(setupCompleteFile.exists()); + stampFiles.createSetupCompleteStamp("Temporarily unlocked"); + String fileData = new String(Files.readAllBytes(setupCompleteFile.toPath())); + assertTrue(setupCompleteFile.exists()); + assertEquals("Temporarily unlocked", fileData); + stampFiles.createSetupCompleteStamp("Other content"); + fileData = new String(Files.readAllBytes(setupCompleteFile.toPath())); + assertTrue(setupCompleteFile.exists()); + assertEquals("Other content", fileData); + + stampFiles.deleteSetupCompleteStamp(); + assertFalse(setupCompleteFile.exists()); + } + + @Test + public void testCreateDeleteMongodbUserStamp() throws IOException { + File mongodbUserDoneFile = getStampFileRef("mongodb-user-done.stamp"); + + CommonPaths paths = mock(CommonPaths.class); + when(paths.getUserPersistentDataDirectory()).thenReturn(mongodbUserDoneFile.getParentFile()); + StampFiles stampFiles = new StampFiles(paths); + + assertFalse(mongodbUserDoneFile.exists()); + stampFiles.createMongodbUserStamp(); + assertTrue(mongodbUserDoneFile.exists()); + + stampFiles.deleteMongodbUserStamp(); + assertFalse(mongodbUserDoneFile.exists()); + } + + @Test + public void deleteNonExistingMongodbCompleteStamp() throws IOException { + File mongodbUserDoneFile = getStampFileRef("mongodb-user-done.stamp"); + CommonPaths paths = mock(CommonPaths.class); + when(paths.getUserPersistentDataDirectory()).thenReturn(mongodbUserDoneFile.getParentFile()); + StampFiles stampFiles = new StampFiles(paths); + + assertFalse(mongodbUserDoneFile.exists()); + try { + stampFiles.deleteMongodbUserStamp(); + // pass + } catch (Exception e) { + fail("Did not expect exception for deleting non-existent file."); + } + assertFalse(mongodbUserDoneFile.exists()); + } + + @Test + public void deleteNonExistingSetupCompleteStamp() throws IOException { + File setupCompleteFile = getStampFileRef("setup-complete.stamp"); + CommonPaths paths = mock(CommonPaths.class); + when(paths.getUserPersistentDataDirectory()).thenReturn(setupCompleteFile.getParentFile()); + StampFiles stampFiles = new StampFiles(paths); + + assertFalse(setupCompleteFile.exists()); + try { + stampFiles.deleteSetupCompleteStamp(); + // pass + } catch (Exception e) { + fail("Did not expect exception for deleting non-existent file."); + } + assertFalse(setupCompleteFile.exists()); + } + + private File getStampFileRef(String name) throws IOException { + Path thermostatUserHome = testRoot.resolve("user"); + Path thermostatUserData = thermostatUserHome.resolve("data"); + File file = new File(thermostatUserData.toFile(), name); + Files.createDirectories(thermostatUserData); + return file; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup-command/command/src/test/java/com/redhat/thermostat/setup/command/internal/model/StructureInformationTest.java Wed Sep 02 15:27:53 2015 +0200 @@ -0,0 +1,93 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.setup.command.internal.model; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.redhat.thermostat.shared.config.CommonPaths; + +public class StructureInformationTest { + + private Path testRoot; + private Path thermostatSysHome; + private StructureInformation structureInfo; + + @Before + public void setup() throws IOException { + testRoot = TestRootHelper.createTestRootDirectory(getClass().getName()); + + thermostatSysHome = testRoot.resolve("system"); + Files.createDirectory(thermostatSysHome); + CommonPaths mockPaths = mock(CommonPaths.class); + when(mockPaths.getSystemThermostatHome()).thenReturn(thermostatSysHome.toFile()); + structureInfo = new StructureInformation(mockPaths); + } + + @After + public void teardown() throws IOException { + TestRootHelper.recursivelyRemoveTestRootDirectory(testRoot); + } + + @Test + public void testWebAppInstalledSuccess() throws IOException { + Path webAppPath = thermostatSysHome.resolve("webapp"); + Files.createDirectories(webAppPath); + assertTrue(structureInfo.isWebAppInstalled()); + } + + @Test + public void testWebAppInstalledFail() throws IOException { + //Call isWebAppInstalled() without creating + //a THERMOSTAT_SYS_HOME/webapp directory + assertFalse(structureInfo.isWebAppInstalled()); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup-command/command/src/test/java/com/redhat/thermostat/setup/command/internal/model/TestRootHelper.java Wed Sep 02 15:27:53 2015 +0200 @@ -0,0 +1,72 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.setup.command.internal.model; + +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; + +class TestRootHelper { + + static Path createTestRootDirectory(String name) throws IOException { + Path testRoot = Files.createTempDirectory("thermostat-" + name); + return testRoot; + } + + static void recursivelyRemoveTestRootDirectory(Path dir) throws IOException { + Files.walkFileTree(dir, new SimpleFileVisitor<Path>() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Files.delete(file); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + if (exc == null) { + Files.delete(dir); + return FileVisitResult.CONTINUE; + } else { + throw exc; + } + } + }); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup-command/command/src/test/java/com/redhat/thermostat/setup/command/internal/model/ThermostatSetupTest.java Wed Sep 02 15:27:53 2015 +0200 @@ -0,0 +1,152 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.setup.command.internal.model; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.argThat; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.junit.Before; +import org.junit.Test; + +import com.redhat.thermostat.shared.config.CommonPaths; + +public class ThermostatSetupTest { + + private ThermostatUserSetup userSetup; + private MongodbUserSetup mongoUserSetup; + + @Before + public void setup() { + userSetup = mock(ThermostatUserSetup.class); + mongoUserSetup = mock(MongodbUserSetup.class); + } + + @Test + public void testIsWebAppInstalledDelegates() { + StructureInformation structureInfo = mock(StructureInformation.class); + ThermostatSetup setup = new ThermostatSetup(userSetup, mongoUserSetup, structureInfo, mock(CommonPaths.class), mock(CredentialsFileCreator.class)); + when(structureInfo.isWebAppInstalled()).thenReturn(true); + assertTrue(setup.isWebAppInstalled()); + verify(structureInfo).isWebAppInstalled(); + } + + @Test + public void testCreateAgentUser() { + ThermostatSetup setup = new ThermostatSetup(userSetup, mongoUserSetup, mock(StructureInformation.class), mock(CommonPaths.class), mock(CredentialsFileCreator.class)); + setup.createAgentUser("foo-agent", new char[] { 't' }); + verify(userSetup).createRecursiveRole(eq("thermostat-agent"), argThat(new RoleMatcher(UserRoles.AGENT_ROLES)), any(String.class)); + verify(userSetup).assignRolesToUser(eq("foo-agent"), argThat(new RoleMatcher(new String[] { "thermostat-agent", UserRoles.GRANT_FILES_WRITE_ALL })), any(String.class)); + } + + @Test + public void testCreateClientAdminUser() { + ThermostatSetup setup = new ThermostatSetup(userSetup, mongoUserSetup, mock(StructureInformation.class), mock(CommonPaths.class), mock(CredentialsFileCreator.class)); + setup.createClientAdminUser("foo-client", new char[] { 't' }); + verify(userSetup).createRecursiveRole(eq("thermostat-client"), argThat(new RoleMatcher(UserRoles.CLIENT_ROLES)), any(String.class)); + verify(userSetup).createRecursiveRole(eq("thermostat-cmdc"), argThat(new RoleMatcher(UserRoles.CMD_CHANNEL_GRANT_ALL_ACTIONS)), any(String.class)); + verify(userSetup).createRecursiveRole(eq("thermostat-admin-read-all"), argThat(new RoleMatcher(UserRoles.ADMIN_READALL)), any(String.class)); + String[] clientAllRoles = new String[] { "thermostat-client", "thermostat-cmdc", "thermostat-admin-read-all", UserRoles.PURGE }; + verify(userSetup).assignRolesToUser(eq("foo-client"), argThat(new RoleMatcher(clientAllRoles)), any(String.class)); + } + + @Test + public void flushCreatesAgentAuthFile() throws IOException { + CommonPaths paths = mock(CommonPaths.class); + File mockAgentAuthFile = File.createTempFile("thermostat-test-", getClass().getName()); + try { + when(paths.getUserAgentAuthConfigFile()).thenReturn(mockAgentAuthFile); + ThermostatSetup setup = new ThermostatSetup(userSetup, mongoUserSetup, mock(StructureInformation.class), paths, mock(CredentialsFileCreator.class)); + List<String> contents = Files.readAllLines(mockAgentAuthFile.toPath(), Charset.forName("UTF-8")); + assertEquals(0, contents.size()); + setup.createAgentUser("damian", new char[] { 't', 'e', 's', 't' }); + setup.commit(); + verify(userSetup).commit(); + verify(mongoUserSetup).commit(); + contents = Files.readAllLines(mockAgentAuthFile.toPath(), Charset.forName("UTF-8")); + assertTrue("username and password must be present", contents.size() > 2); + assertTrue("username=damian expected to be found in agent.auth file", contents.contains("username=damian")); + assertTrue("password=test expected to be found in agent.auth file", contents.contains("password=test")); + } finally { + Files.delete(mockAgentAuthFile.toPath()); + } + } + + private class RoleMatcher extends BaseMatcher<String[]> { + + final String[] expected; + private RoleMatcher(String[] expected) { + this.expected = expected; + } + + @Override + public void describeTo(Description arg0) { + arg0.appendText(Arrays.asList(expected).toString()); + } + + @Override + public boolean matches(Object arg0) { + if (arg0.getClass() != String[].class) { + return false; + } + String[] other = (String[])arg0; + if (other.length != expected.length) { + return false; + } + boolean match = true; + for (int i = 0; i < expected.length; i++) { + match = match && Objects.equals(expected[i], other[i]); + } + return match; + } + + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup-command/command/src/test/java/com/redhat/thermostat/setup/command/internal/model/ThermostatUserSetupTest.java Wed Sep 02 15:27:53 2015 +0200 @@ -0,0 +1,331 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.setup.command.internal.model; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +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.Scanner; +import java.util.Set; + +import org.junit.Before; +import org.junit.Test; + +import com.redhat.thermostat.setup.command.internal.model.ThermostatUserSetup.CommentedCredsPropertyValue; +import com.redhat.thermostat.setup.command.internal.model.ThermostatUserSetup.CommentedRolePropertyValue; + +public class ThermostatUserSetupTest { + + private ThermostatUserSetup tSetup; + + @Before + public void setup() throws IOException { + tSetup = new ThermostatUserSetup(mock(UserPropertiesFinder.class), mock(UserCredsValidator.class), mock(CredentialsFileCreator.class), mock(StampFiles.class)); + } + + @Test + public void canCreateCommentedUser() { + char[] fooUserPwd = new char[] { 't', 'e', 's', 't'}; + tSetup.createUser("foo-user", fooUserPwd, "some-comment"); + char[] otherBarPwd = new char[] { 'o', 't', 'h', 'e', 'r'}; + tSetup.createUser("other-bar", otherBarPwd, "comment for bar"); + char[] thirdUserPwd = new char[] { 'n', 'o', 'm', 'a', 't', 't', 'e', 'r' }; + tSetup.createUser("third-user", thirdUserPwd, null); + Map<String, CommentedCredsPropertyValue> props = tSetup.buildUserProperties(); + + CommentedCredsPropertyValue fooUser = props.get("foo-user"); + assertSame(fooUserPwd, fooUser.getValue()); + assertEquals("some-comment", fooUser.getComment()); + + CommentedCredsPropertyValue otherBar = props.get("other-bar"); + assertSame(otherBarPwd, otherBar.getValue()); + assertEquals("comment for bar", otherBar.getComment()); + + CommentedCredsPropertyValue thirdValue = props.get("third-user"); + assertSame(thirdUserPwd, thirdValue.getValue()); + assertNull(thirdValue.getComment()); + + assertNull(props.get("i-wasn't-created-user")); + } + + @Test + public void canCreateCommentedRecursiveRole() { + String[] primitives = new String[] { + "role1", + "role2" + }; + tSetup.createRecursiveRole("recursive-role", primitives, "comment for recursive role"); + Map<String, CommentedRolePropertyValue> roles = tSetup.buildRoleProperties(); + CommentedRolePropertyValue recRoleValue = roles.get("recursive-role"); + assertNotNull(recRoleValue); + String expectedValue = "role1, " + System.lineSeparator() + + "role2"; + assertEquals(expectedValue, recRoleValue.getValue()); + assertEquals("comment for recursive role", recRoleValue.getComment()); + } + + @Test + public void canAssignRolesToUser() { + String[] primitives = new String[] { + "role1", + "role2" + }; + tSetup.assignRolesToUser("some-user", primitives, null); + Map<String, CommentedRolePropertyValue> roleProps = tSetup.buildRoleProperties(); + CommentedRolePropertyValue val = roleProps.get("some-user"); + assertNotNull(val); + assertNull(val.getComment()); + String expectedRolesStr = "role1, " + System.lineSeparator() + "role2"; + assertEquals(expectedRolesStr, val.getValue()); + } + + @Test + public void testRoleSetToString() { + String[] roleSet = new String[] { + "foo-one", + "foo-two" + }; + String expectedString = "foo-one, " + System.lineSeparator() + "foo-two"; + assertEquals(expectedString, tSetup.roleSetToString(roleSet)); + assertTrue(tSetup.roleSetToString(new String[] {}).isEmpty()); + } + + @Test(expected = IllegalArgumentException.class) + public void assigningRolesValidatesNullValue() { + tSetup.assignRolesToUser("something", null, null); + } + + @Test(expected = IllegalArgumentException.class) + public void assigningRolesValidatesEmptyRoles() { + tSetup.assignRolesToUser("something", new String[] {}, null); + } + + @Test(expected = IllegalArgumentException.class) + public void creatingRecursiveRolesValidatesNullValue() { + tSetup.createRecursiveRole("something", null, null); + } + + @Test(expected = IllegalArgumentException.class) + public void creatingRecursiveRolesValidatesEmptyRoles() { + tSetup.createRecursiveRole("something", new String[] {}, null); + } + + @Test + public void canWriteRoles() throws IOException { + Map<String, CommentedRolePropertyValue> roleProps = new HashMap<>(); + String roleValue = "thermostat-login, \nthermostat-test"; + CommentedRolePropertyValue thermostatAgent = new CommentedRolePropertyValue(roleValue, "some comment"); + roleProps.put("thermostat-agent", thermostatAgent); + CommentedRolePropertyValue agentUser = new CommentedRolePropertyValue("thermostat-agent", "agent user"); + roleProps.put("foo-agent-user", agentUser); + + UserPropertiesFinder propsFinder = mock(UserPropertiesFinder.class); + File tmpRolesPropsFile = File.createTempFile("thermostat", getClass().getName()); + try { + when(propsFinder.getRolesProperties()).thenReturn(tmpRolesPropsFile); + ThermostatUserSetup userSetup = new ThermostatUserSetup(propsFinder, mock(UserCredsValidator.class), mock(CredentialsFileCreator.class), mock(StampFiles.class)) { + + @Override + Properties readPropsFromFile(File propsFile) { + // simulate empty existing properties + return new Properties(); + } + }; + userSetup.writeRoles(roleProps); + List<String> expectedList = new ArrayList<>(); + expectedList.add("#some comment"); + expectedList.add("thermostat-agent=thermostat-login, \\"); + expectedList.add("foo-agent-user=thermostat-agent"); + expectedList.add("#agent user"); + verifyFileContainsLines(tmpRolesPropsFile, expectedList); + } finally { + Files.delete(tmpRolesPropsFile.toPath()); + } + } + + @Test + public void canWriteUsers() throws IOException { + Map<String, CommentedCredsPropertyValue> userCreds = new HashMap<>(); + CommentedCredsPropertyValue user1Val = new CommentedCredsPropertyValue(new char[] { 't' }, "user1"); + userCreds.put("testuser1", user1Val); + CommentedCredsPropertyValue user2Val = new CommentedCredsPropertyValue(new char[] { 'b', 'a', 'r' }, "speedy"); + userCreds.put("speedy-user", user2Val); + + UserPropertiesFinder propsFinder = mock(UserPropertiesFinder.class); + File tmpUsersPropsFile = File.createTempFile("thermostat", getClass().getName()); + try { + when(propsFinder.getUserProperties()).thenReturn(tmpUsersPropsFile); + ThermostatUserSetup userSetup = new ThermostatUserSetup(propsFinder, mock(UserCredsValidator.class), mock(CredentialsFileCreator.class), mock(StampFiles.class)) { + + @Override + Properties readPropsFromFile(File propsFile) { + return new Properties(); + } + }; + userSetup.writeUsers(userCreds); + List<String> expectedList = new ArrayList<>(); + expectedList.add("#user1"); + expectedList.add("testuser1=t"); + expectedList.add("speedy-user=bar"); + expectedList.add("#speedy"); + verifyFileContainsLines(tmpUsersPropsFile, expectedList); + } finally { + Files.delete(tmpUsersPropsFile.toPath()); + } + } + + @Test + public void writingUsersDoesnotProduceDuplicates() throws IOException { + Map<String, CommentedCredsPropertyValue> userCreds = new HashMap<>(); + CommentedCredsPropertyValue user1Val = new CommentedCredsPropertyValue(new char[] { 't' }, "user1"); + userCreds.put("testuser1", user1Val); + CommentedCredsPropertyValue user2Val = new CommentedCredsPropertyValue(new char[] { 'b', 'a', 'r' }, "speedy"); + userCreds.put("speedy-user", user2Val); + + UserPropertiesFinder propsFinder = mock(UserPropertiesFinder.class); + File tmpUsersPropsFile = File.createTempFile("thermostat", getClass().getName()); + try { + when(propsFinder.getUserProperties()).thenReturn(tmpUsersPropsFile); + ThermostatUserSetup userSetup = new ThermostatUserSetup(propsFinder, mock(UserCredsValidator.class), mock(CredentialsFileCreator.class), mock(StampFiles.class)) { + + @Override + Properties readPropsFromFile(File propsFile) { + return new Properties(); + } + }; + userSetup.writeUsers(userCreds); + userSetup = new ThermostatUserSetup(propsFinder, mock(UserCredsValidator.class), mock(CredentialsFileCreator.class), mock(StampFiles.class)); + // write same users again + userSetup.writeUsers(userCreds); + List<String> expectedLines = new ArrayList<>(); + expectedLines.add("testuser1=t"); + expectedLines.add("speedy-user=bar"); + verifyFileContainsLines(tmpUsersPropsFile, expectedLines); + assertEquals("expected testuser1=t only once in file.", 1, getNumOccurance(expectedLines.get(0), getLinesAsList(tmpUsersPropsFile))); + assertEquals("expected speedy-user=bar only once in file.", 1, getNumOccurance(expectedLines.get(1), getLinesAsList(tmpUsersPropsFile))); + } finally { + Files.delete(tmpUsersPropsFile.toPath()); + } + } + + @Test + public void writingRolesDoesnotProduceDuplicates() throws IOException { + Map<String, CommentedRolePropertyValue> roleProps = new HashMap<>(); + String roleValue = "thermostat-login, \nthermostat-test"; + CommentedRolePropertyValue thermostatAgent = new CommentedRolePropertyValue(roleValue, "some comment"); + roleProps.put("thermostat-agent", thermostatAgent); + CommentedRolePropertyValue agentUser = new CommentedRolePropertyValue("thermostat-agent", "agent user"); + roleProps.put("foo-agent-user", agentUser); + + UserPropertiesFinder propsFinder = mock(UserPropertiesFinder.class); + File tmpRolesPropsFile = File.createTempFile("thermostat", getClass().getName()); + try { + when(propsFinder.getRolesProperties()).thenReturn(tmpRolesPropsFile); + ThermostatUserSetup userSetup = new ThermostatUserSetup(propsFinder, mock(UserCredsValidator.class), mock(CredentialsFileCreator.class), mock(StampFiles.class)) { + + @Override + Properties readPropsFromFile(File propsFile) { + // simulate empty existing properties + return new Properties(); + } + }; + userSetup.writeRoles(roleProps); + userSetup = new ThermostatUserSetup(propsFinder, mock(UserCredsValidator.class), mock(CredentialsFileCreator.class), mock(StampFiles.class)); + // write users again + userSetup.writeRoles(roleProps); + List<String> expectedList = new ArrayList<>(); + expectedList.add("thermostat-agent=thermostat-login, \\"); + expectedList.add("foo-agent-user=thermostat-agent"); + verifyFileContainsLines(tmpRolesPropsFile, expectedList); + assertEquals("expected 'thermostat-agent=thermostat-login, \\' only once in file.", 1, getNumOccurance(expectedList.get(0), getLinesAsList(tmpRolesPropsFile))); + assertEquals("expected 'foo-agent-user=thermostat-agent' only once in file.", 1, getNumOccurance(expectedList.get(1), getLinesAsList(tmpRolesPropsFile))); + } finally { + Files.delete(tmpRolesPropsFile.toPath()); + } + } + + private int getNumOccurance(String itemToSearch, List<String> list) { + int count = 0; + for (String line: list) { + if (line.equals(itemToSearch)) { + count++; + } + } + return count; + } + + private void verifyFileContainsLines(File file, List<String> expectedList) throws FileNotFoundException, IOException { + Set<String> fileContents = getLinesAsSet(file); + for (String line: expectedList) { + assertTrue("Expected " + fileContents + " to contain line ' " + line + "'.", fileContents.contains(line)); + } + } + + private Set<String> getLinesAsSet(File file) throws IOException, + FileNotFoundException { + Set<String> fileContents = new HashSet<>(); + try (FileInputStream fin = new FileInputStream(file); + Scanner fileScanner = new Scanner(fin)) { + while (fileScanner.hasNextLine()) { + String line = fileScanner.nextLine(); + fileContents.add(line); + } + } + return fileContents; + } + + private List<String> getLinesAsList(File file) throws IOException { + return Files.readAllLines(file.toPath(), Charset.forName("UTF-8")); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup-command/command/src/test/java/com/redhat/thermostat/setup/command/internal/model/UserCredsValidatorTest.java Wed Sep 02 15:27:53 2015 +0200 @@ -0,0 +1,90 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.setup.command.internal.model; + +import static org.junit.Assert.fail; + +import org.junit.Before; +import org.junit.Test; + +public class UserCredsValidatorTest { + + private UserCredsValidator validator; + + @Before + public void setup() { + validator = new UserCredsValidator(); + } + + @Test(expected = IllegalArgumentException.class) + public void testValidateUsernameNull() { + validator.validateUsername(null); + } + + @Test(expected = IllegalArgumentException.class) + public void testValidateUsernameEmpty() { + validator.validateUsername(""); + } + + @Test(expected = IllegalArgumentException.class) + public void testValidatePasswordNull() { + validator.validatePassword(null); + } + + @Test(expected = IllegalArgumentException.class) + public void testValidatePasswordEmpty() { + validator.validatePassword(new char[] {}); + } + + @Test + public void validateUsernamePassesOnValid() { + try { + validator.validateUsername("some-non-empty-username"); + } catch (IllegalArgumentException e) { + fail("username expected to be valid"); + } + } + + @Test + public void validatePasswordPassesOnValid() { + try { + validator.validatePassword(new char[] { 't', 'e', 's', 't' }); + } catch (IllegalArgumentException e) { + fail("password expected to be valid"); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup-command/command/src/test/java/com/redhat/thermostat/setup/command/internal/model/UserPropertiesFinderTest.java Wed Sep 02 15:27:53 2015 +0200 @@ -0,0 +1,73 @@ +/* + * Copyright 2012-2015 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.setup.command.internal.model; + +import java.io.File; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertSame; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class UserPropertiesFinderTest { + + private UserPropertiesFinder propFinder; + private File userPropFile = mock(File.class); + private File rolesPropFile = mock(File.class); + + @Before + public void setup() { + CredentialFinder finder = mock(CredentialFinder.class); + when(finder.getConfiguration("thermostat-users.properties")).thenReturn(userPropFile); + when(finder.getConfiguration("thermostat-roles.properties")).thenReturn(rolesPropFile); + propFinder = new UserPropertiesFinder(finder); + } + + @Test + public void userPropertiesDelegatesToCredsFinder() { + File result = propFinder.getUserProperties(); + assertSame(result, userPropFile); + } + + @Test + public void rolesPropertiesDelegatesToCredsFinder() { + File result = propFinder.getRolesProperties(); + assertSame(result, rolesPropFile); + } +}