Mercurial > hg > release > thermostat-1.4
changeset 1776:36c9ffed683c
Make setup work if webapp is not installed.
Reviewed-by: neugens
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2015-September/015812.html
PR2581
line wrap: on
line diff
--- a/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/MongoUserSetupView.java Fri Sep 04 14:57:14 2015 -0400 +++ b/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/MongoUserSetupView.java Mon Sep 07 16:44:52 2015 +0200 @@ -36,7 +36,9 @@ package com.redhat.thermostat.setup.command.internal; +import com.redhat.thermostat.setup.command.internal.model.ThermostatSetup; import com.redhat.thermostat.setup.command.locale.LocaleResources; +import com.redhat.thermostat.shared.locale.LocalizedString; import com.redhat.thermostat.shared.locale.Translate; import javax.swing.BorderFactory; @@ -48,6 +50,7 @@ import javax.swing.JPanel; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; + import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; @@ -56,6 +59,7 @@ public class MongoUserSetupView extends JPanel implements SetupView { + private final ThermostatSetup thermostatSetup; private JButton backBtn; private JButton nextBtn; private JButton cancelBtn; @@ -65,12 +69,13 @@ private InputCredentialPanel credentialPanel; private static final String THERMOSTAT_LOGO = "thermostat.png"; - private static final String PROGRESS = "Step 2 of 3"; + private static final String PROGRESS_FORMAT = "Step 2 of %s"; private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer(); - public MongoUserSetupView(LayoutManager layout) { + public MongoUserSetupView(LayoutManager layout, ThermostatSetup setup) { super(layout); + this.thermostatSetup = setup; createMidPanel(); createToolbarPanel(); @@ -85,7 +90,11 @@ @Override public void setProgress(JLabel progress) { - progress.setText(PROGRESS); + int totalSteps = 2; + if (thermostatSetup.isWebAppInstalled()) { + totalSteps++; + } + progress.setText(String.format(PROGRESS_FORMAT, totalSteps)); } private void createMidPanel() { @@ -128,7 +137,11 @@ backBtn = new JButton(translator.localize(LocaleResources.BACK).getContents()); backBtn.setPreferredSize(new Dimension(70, 30)); - nextBtn = new JButton(translator.localize(LocaleResources.NEXT).getContents()); + LocalizedString nextButtonLabel = translator.localize(LocaleResources.NEXT); + if (!thermostatSetup.isWebAppInstalled()) { + nextButtonLabel = translator.localize(LocaleResources.FINISH); + } + nextBtn = new JButton(nextButtonLabel.getContents()); nextBtn.setPreferredSize(new Dimension(70, 30)); nextBtn.setEnabled(false); cancelBtn = new JButton(translator.localize(LocaleResources.CANCEL).getContents());
--- a/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/SetupWindow.java Fri Sep 04 14:57:14 2015 -0400 +++ b/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/SetupWindow.java Mon Sep 07 16:44:52 2015 +0200 @@ -214,8 +214,8 @@ mainView.add(topPanel, BorderLayout.NORTH); frame.add(mainView); - startView = new StartView(new BorderLayout()); - mongoUserSetupView = new MongoUserSetupView(new BorderLayout()); + startView = new StartView(new BorderLayout(), thermostatSetup); + mongoUserSetupView = new MongoUserSetupView(new BorderLayout(), thermostatSetup); userPropertiesView = new UserPropertiesView(new BorderLayout()); setupCompleteView = new SetupCompleteView(new BorderLayout()); } @@ -353,8 +353,10 @@ userPropertiesView.disableButtons(); thermostatSetup.createMongodbUser(storageUsername, storagePassword); try { - thermostatSetup.createAgentUser(agentUsername, agentPassword); - thermostatSetup.createClientAdminUser(clientUsername, clientPassword); + if (thermostatSetup.isWebAppInstalled()) { + thermostatSetup.createAgentUser(agentUsername, agentPassword); + thermostatSetup.createClientAdminUser(clientUsername, clientPassword); + } thermostatSetup.commit(); return null; } catch (IOException e) {
--- a/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/StartView.java Fri Sep 04 14:57:14 2015 -0400 +++ b/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/StartView.java Mon Sep 07 16:44:52 2015 +0200 @@ -37,10 +37,11 @@ package com.redhat.thermostat.setup.command.internal; import com.redhat.thermostat.client.swing.components.ThermostatScrollPane; +import com.redhat.thermostat.setup.command.internal.model.ThermostatSetup; import com.redhat.thermostat.common.ApplicationInfo; import com.redhat.thermostat.setup.command.locale.LocaleResources; import com.redhat.thermostat.shared.locale.Translate; - + import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; @@ -67,6 +68,7 @@ public class StartView extends JPanel implements SetupView { + private final ThermostatSetup thermostatSetup; private JButton nextBtn; private JButton cancelBtn; private JButton moreInfoBtn; @@ -81,12 +83,12 @@ private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer(); private static final String SHOW_MORE = "Show More"; private static final String SHOW_LESS = "Show Less"; - private static final String PROGRESS = "Step 1 of 3"; - private static final String FAST_TRACK_PROGRESS = "Step 1 of 2"; + private static final String PROGRESS_FORMAT = "Step 1 of %s"; - public StartView(LayoutManager layout) { + public StartView(LayoutManager layout, ThermostatSetup thermostatSetup) { super(layout); + this.thermostatSetup = thermostatSetup; createToolbarPanel(); createMidPanel(); } @@ -98,10 +100,14 @@ @Override public void setProgress(JLabel progress) { + int totalSteps = 2; if (quickSetupBtn.isSelected()) { - progress.setText(FAST_TRACK_PROGRESS); + progress.setText(String.format(PROGRESS_FORMAT, totalSteps)); } else { - progress.setText(PROGRESS); + if (thermostatSetup.isWebAppInstalled()) { + totalSteps++; + } + progress.setText(String.format(PROGRESS_FORMAT, totalSteps)); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/AuthFileWriter.java Mon Sep 07 16:44:52 2015 +0200 @@ -0,0 +1,74 @@ +/* + * 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.util.Arrays; +import java.util.Objects; +import java.util.Properties; + +import com.redhat.thermostat.shared.config.CommonPaths; + +class AuthFileWriter extends CredentialsWriter { + + private final CommonPaths paths; + private final CredentialsFileCreator creator; + + AuthFileWriter(CommonPaths paths, CredentialsFileCreator creator) { + this.paths = paths; + this.creator = creator; + } + + public void write() throws IOException { + char[] password = getPassword(); + String username = getUsername(); + Objects.requireNonNull(password); + Objects.requireNonNull(username); + Properties credentialProps = new Properties(); + credentialProps.setProperty("username", username); + credentialProps.setProperty("password", String.valueOf(password)); + File credentialsFile = paths.getUserAgentAuthConfigFile(); + creator.create(credentialsFile); + try (FileOutputStream fout = new FileOutputStream(credentialsFile)) { + credentialProps.store(fout, "Credentials used for 'thermostat agent' connections."); + } finally { + Arrays.fill(password, '\0'); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/CredentialsWriter.java Mon Sep 07 16:44:52 2015 +0200 @@ -0,0 +1,63 @@ +/* + * 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; + +abstract class CredentialsWriter { + + private String username; + private char[] password; + + public void setCredentials(String username, char[] password) { + this.username = username; + this.password = password; + } + + protected String getUsername() { + return username; + } + + protected char[] getPassword() { + return password; + } + + /** + * persists storage credentials. + */ + public abstract void write() throws IOException; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/KeyringWriter.java Mon Sep 07 16:44:52 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 java.io.IOException; +import java.util.Arrays; +import java.util.Objects; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.redhat.thermostat.common.config.ClientPreferences; +import com.redhat.thermostat.common.utils.LoggingUtils; +import com.redhat.thermostat.utils.keyring.Keyring; +import com.redhat.thermostat.utils.keyring.KeyringException; + +class KeyringWriter extends CredentialsWriter { + + private static final Logger logger = LoggingUtils.getLogger(KeyringWriter.class); + private final ClientPreferences prefs; + private final Keyring keyring; + private String storageUrl; + + KeyringWriter(ClientPreferences prefs, Keyring keyring) { + this.prefs = prefs; + this.keyring = keyring; + } + + void setStorageUrl(String customStorageUrl) { + this.storageUrl = customStorageUrl; + } + + public void write() throws IOException { + String username = getUsername(); + char[] password = getPassword(); + Objects.requireNonNull(username); + Objects.requireNonNull(password); + Objects.requireNonNull(storageUrl); + try { + prefs.setSaveEntitlements(true); // force writing on flush() + prefs.setConnectionUrl(storageUrl); + prefs.setUserName(username); + // Unconditionally save credentials for the chosen username. If setup + // runs again it will overwrite existing credentials. + prefs.flush(); + keyring.savePassword(storageUrl, username, password); + } catch (KeyringException e) { + // Don't fail setup if storing to keyring fails. After all this is + // for convenience only (so that thermostat gui works out of the box + // after setup). If we failed, one would have to have a working + // keyring setup which isn't always the case. Think thermostat agent. + logger.log(Level.INFO, "Failed to store client credentials to keyring. Usability of client commands might suffer.", e); + } finally { + Arrays.fill(password, '\0'); + } + } +}
--- a/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/MongodbUserSetup.java Fri Sep 04 14:57:14 2015 -0400 +++ b/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/MongodbUserSetup.java Mon Sep 07 16:44:52 2015 +0200 @@ -71,11 +71,13 @@ private final CommonPaths paths; private final StampFiles stampFiles; private final StructureInformation structureInfo; + private final AuthFileWriter authFileWriter; + private final KeyringWriter keyringWriter; private String username; private char[] password; private String userComment; - MongodbUserSetup(UserCredsValidator validator, Launcher launcher, CredentialFinder finder, CredentialsFileCreator fileCreator, CommonPaths paths, StampFiles stampFiles, StructureInformation structureInfo) { + MongodbUserSetup(UserCredsValidator validator, Launcher launcher, CredentialFinder finder, CredentialsFileCreator fileCreator, CommonPaths paths, StampFiles stampFiles, StructureInformation structureInfo, AuthFileWriter authWriter, KeyringWriter keyringWriter) { this.validator = validator; this.launcher = launcher; this.finder = finder; @@ -83,6 +85,8 @@ this.stampFiles = stampFiles; this.paths = paths; this.structureInfo = structureInfo; + this.authFileWriter = authWriter; + this.keyringWriter = keyringWriter; } @Override @@ -132,6 +136,9 @@ // started successfully. stopStorage(); if (!isWebAppInstalled()) { + // Allow for gui/agent to work out of the box by using mongodb + // URLs. + setupForDirectMongodbUrls(); String completeDate = ThermostatSetup.DATE_FORMAT.format(new Date()); String regularContent = "Created by '" + ThermostatSetup.PROGRAM_NAME + "' on " + completeDate; stampFiles.createSetupCompleteStamp(regularContent); @@ -146,6 +153,18 @@ } } + private void setupForDirectMongodbUrls() throws MongodbUserSetupException { + try { + authFileWriter.setCredentials(username, Arrays.copyOf(password, password.length)); + authFileWriter.write(); + keyringWriter.setCredentials(username, Arrays.copyOf(password, password.length)); + keyringWriter.setStorageUrl(ThermostatSetup.MONGODB_STORAGE_URL); + keyringWriter.write(); + } catch (IOException e) { + throw new MongodbUserSetupException("Error creating agent.auth file or persisting keyring prefs", e); + } + } + private void cleanupAndReThrow(boolean storageStarted, MongodbUserSetupException e) throws MongodbUserSetupException { if (storageStarted) { stopStorage();
--- a/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/ThermostatSetup.java Fri Sep 04 14:57:14 2015 -0400 +++ b/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/ThermostatSetup.java Mon Sep 07 16:44:52 2015 +0200 @@ -36,27 +36,19 @@ 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.Arrays; -import java.util.Objects; -import java.util.Properties; -import java.util.logging.Level; -import java.util.logging.Logger; import com.redhat.thermostat.common.config.ClientPreferences; -import com.redhat.thermostat.common.utils.LoggingUtils; import com.redhat.thermostat.launcher.Launcher; import com.redhat.thermostat.shared.config.CommonPaths; import com.redhat.thermostat.utils.keyring.Keyring; -import com.redhat.thermostat.utils.keyring.KeyringException; public class ThermostatSetup implements PersistableSetup { - private static final Logger logger = LoggingUtils.getLogger(ThermostatSetup.class); + static final String WEB_STORAGE_URL = "http://127.0.0.1:8999/thermostat/storage"; + static final String MONGODB_STORAGE_URL = "mongodb://127.0.0.1:27518"; 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"; @@ -66,23 +58,15 @@ private final ThermostatUserSetup userSetup; private final MongodbUserSetup mongodbUserSetup; private final StructureInformation structureInfo; - private final CredentialsFileCreator creator; - private final CommonPaths paths; - private final Keyring keyring; - private final ClientPreferences prefs; - private String agentUserName; - private char[] agentPassword; - private String clientUsername; - private char[] clientPassword; + private final AuthFileWriter authWriter; + private final KeyringWriter keyringWriter; - ThermostatSetup(ThermostatUserSetup userSetup, MongodbUserSetup mongodbUserSetup, StructureInformation structureInfo, CommonPaths paths, CredentialsFileCreator creator, Keyring keyring, ClientPreferences prefs) { + ThermostatSetup(ThermostatUserSetup userSetup, MongodbUserSetup mongodbUserSetup, StructureInformation structureInfo, AuthFileWriter authWriter, KeyringWriter keyringWriter) { this.mongodbUserSetup = mongodbUserSetup; this.userSetup = userSetup; this.structureInfo = structureInfo; - this.paths = paths; - this.creator = creator; - this.keyring = keyring; - this.prefs = prefs; + this.authWriter = authWriter; + this.keyringWriter = keyringWriter; } public void createMongodbUser(String username, char[] password) { @@ -108,10 +92,7 @@ }, "Client admin user username => role assignment." ); - // Hold on to these credentials so that they can be written to keyring - // on commit(). This makes gui work out of the box after setup has run. - this.clientUsername = username; - this.clientPassword = password; + keyringWriter.setCredentials(username, password); } public void createAgentUser(String username, char[] password) { @@ -125,54 +106,17 @@ 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; + authWriter.setCredentials(username, password); } @Override public void commit() throws IOException { - // FIXME: report errors mongodbUserSetup.commit(); - userSetup.commit(); - writeAgentAuthFile(); - storeClientCredsToKeyring(); - } - - private void storeClientCredsToKeyring() throws IOException { - Objects.requireNonNull(clientUsername); - Objects.requireNonNull(clientPassword); - try { - prefs.setSaveEntitlements(true); // force writing on flush() - String url = prefs.getConnectionUrl(); - prefs.setUserName(clientUsername); - // Unconditionally save credentials for the chosen username. If setup - // runs again it will overwrite existing credentials. - prefs.flush(); - keyring.savePassword(url, clientUsername, clientPassword); - } catch (KeyringException e) { - // Don't fail setup if storing to keyring fails. After all this is - // for convenience only (so that thermostat gui works out of the box - // after setup). If we failed, one would have to have a working - // keyring setup which isn't always the case. Think thermostat agent. - logger.log(Level.INFO, "Failed to store client credentials to keyring. Usability of client commands might suffer.", e); - } finally { - Arrays.fill(clientPassword, '\0'); - } - } - - private void writeAgentAuthFile() throws IOException { - Objects.requireNonNull(agentPassword); - Objects.requireNonNull(agentUserName); - 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."); - } finally { - Arrays.fill(agentPassword, '\0'); + if (isWebAppInstalled()) { + userSetup.commit(); + authWriter.write(); + keyringWriter.setStorageUrl(WEB_STORAGE_URL); + keyringWriter.write(); } } @@ -185,9 +129,11 @@ CredentialsFileCreator creator = new CredentialsFileCreator(); StampFiles stampFiles = new StampFiles(paths); StructureInformation info = new StructureInformation(paths); - MongodbUserSetup mongoSetup = new MongodbUserSetup(new UserCredsValidator(), launcher, finder, creator , paths, stampFiles, info); + ClientPreferences prefs = new ClientPreferences(paths); + KeyringWriter keyringWriter = new KeyringWriter(prefs, keyring); + AuthFileWriter authWriter = new AuthFileWriter(paths, creator); + MongodbUserSetup mongoSetup = new MongodbUserSetup(new UserCredsValidator(), launcher, finder, creator , paths, stampFiles, info, authWriter, keyringWriter); ThermostatUserSetup userSetup = new ThermostatUserSetup(new UserPropertiesFinder(finder), new UserCredsValidator(), creator, stampFiles); - ClientPreferences prefs = new ClientPreferences(paths); - return new ThermostatSetup(userSetup, mongoSetup, info, paths, creator, keyring, prefs); + return new ThermostatSetup(userSetup, mongoSetup, info, authWriter, keyringWriter); } }
--- a/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/ThermostatUserSetup.java Fri Sep 04 14:57:14 2015 -0400 +++ b/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/ThermostatUserSetup.java Mon Sep 07 16:44:52 2015 +0200 @@ -72,7 +72,6 @@ @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);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup/command/src/test/java/com/redhat/thermostat/setup/command/internal/model/AuthFileWriterTest.java Mon Sep 07 16:44:52 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.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertArrayEquals; +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.charset.Charset; +import java.nio.file.Files; +import java.util.List; + +import org.junit.Test; + +import com.redhat.thermostat.shared.config.CommonPaths; + +public class AuthFileWriterTest { + + @Test + public void canWriteAuthFile() throws IOException { + CommonPaths paths = mock(CommonPaths.class); + File mockAgentAuthFile = File.createTempFile("thermostat-test-", getClass().getName()); + try { + when(paths.getUserAgentAuthConfigFile()).thenReturn(mockAgentAuthFile); + AuthFileWriter authWriter = new AuthFileWriter(paths, mock(CredentialsFileCreator.class)); + List<String> contents = Files.readAllLines(mockAgentAuthFile.toPath(), Charset.forName("UTF-8")); + assertEquals(0, contents.size()); + char[] password = new char[] { 't', 'e', 's', 't' }; + authWriter.setCredentials("damian", password); + authWriter.write(); + 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")); + assertArrayEquals("Expected password to have been cleared.", new char[] { '\0', '\0', '\0', '\0' }, password); + } catch (IOException e) { + e.printStackTrace(); + fail("Did not expect failure on commit()"); + } + finally { + Files.delete(mockAgentAuthFile.toPath()); + } + } + + @Test(expected=NullPointerException.class) + public void notSettingCredsBeforeWriteThrowsNPE() throws IOException { + AuthFileWriter authWriter = new AuthFileWriter(mock(CommonPaths.class), mock(CredentialsFileCreator.class)); + authWriter.write(); // expected creds to be set first + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup/command/src/test/java/com/redhat/thermostat/setup/command/internal/model/KeyringWriterTest.java Mon Sep 07 16:44:52 2015 +0200 @@ -0,0 +1,114 @@ +/* + * 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 static org.junit.Assert.assertArrayEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import java.io.IOException; + +import org.junit.Test; + +import com.redhat.thermostat.common.config.ClientPreferences; +import com.redhat.thermostat.utils.keyring.Keyring; +import com.redhat.thermostat.utils.keyring.KeyringException; + +public class KeyringWriterTest { + + @Test + public void writeStoresToKeyringSuccess() throws IOException { + Keyring keyring = mock(Keyring.class); // well behaved keyring + doKeyringTest(keyring); + } + + private void doKeyringTest(Keyring keyring) throws IOException { + ClientPreferences prefs = mock(ClientPreferences.class); + KeyringWriter keyringWriter = new KeyringWriter(prefs, keyring); + try { + String clientUser = "client-admin"; + char[] clientPass = new char[] { 't' }; + String storageUrl = "http://somestorage.example.com"; + keyringWriter.setCredentials(clientUser, clientPass); + keyringWriter.setStorageUrl(storageUrl); + keyringWriter.write(); + verify(keyring).savePassword(storageUrl, clientUser, clientPass); + verify(prefs).flush(); + verify(prefs).setSaveEntitlements(true); + verify(prefs).setUserName(clientUser); + verify(prefs).setConnectionUrl(storageUrl); + assertArrayEquals("Expected password array to have been cleared", new char[] { '\0' }, clientPass); + } catch (IOException e) { + e.printStackTrace(); + fail("Did not expect failure on commit()"); + } + } + + @Test + public void keyringStoreFailureIsNonFatal() throws IOException { + Keyring keyring = mock(Keyring.class); + doThrow(new MockKeyringException("This is a test")).when(keyring).savePassword(any(String.class), any(String.class), any(char[].class)); + doKeyringTest(keyring); + } + + @Test(expected = NullPointerException.class) + public void notSettingCredsBeforeWriteThrowsNPE() throws IOException { + KeyringWriter writer = new KeyringWriter(mock(ClientPreferences.class), mock(Keyring.class)); + writer.setStorageUrl("http://somestorage.example.com"); + writer.write(); // expected creds to be set first. + } + + @Test(expected = NullPointerException.class) + public void notSettingStorageUrlBeforeWriteThrowsNPE() throws IOException { + KeyringWriter writer = new KeyringWriter(mock(ClientPreferences.class), mock(Keyring.class)); + writer.setCredentials("something", new char[] { 'x' }); // doesn't matter, just non-null + writer.write(); // expected storage URL to be set first + } + + @SuppressWarnings("serial") + private static class MockKeyringException extends KeyringException { + + public MockKeyringException(String string) { + super(string); + } + + } + +}
--- a/setup/command/src/test/java/com/redhat/thermostat/setup/command/internal/model/MongodbUserSetupTest.java Fri Sep 04 14:57:14 2015 -0400 +++ b/setup/command/src/test/java/com/redhat/thermostat/setup/command/internal/model/MongodbUserSetupTest.java Mon Sep 07 16:44:52 2015 +0200 @@ -37,16 +37,19 @@ package com.redhat.thermostat.setup.command.internal.model; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertArrayEquals; 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.Matchers.argThat; 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.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import java.io.File; @@ -70,6 +73,7 @@ import com.redhat.thermostat.common.cli.AbstractStateNotifyingCommand; import com.redhat.thermostat.common.tools.ApplicationState; import com.redhat.thermostat.launcher.Launcher; +import com.redhat.thermostat.setup.command.internal.cli.CharArrayMatcher; import com.redhat.thermostat.shared.config.CommonPaths; public class MongodbUserSetupTest { @@ -81,6 +85,8 @@ private CredentialsFileCreator fileCreator; private CommonPaths paths; private StructureInformation info; + private AuthFileWriter authFileWriter; + private KeyringWriter keyringWriter; @Before public void setup() { @@ -90,7 +96,9 @@ stampFiles = mock(StampFiles.class); info = mock(StructureInformation.class); mockLauncher = mock(Launcher.class); - mongoSetup = new MongodbUserSetup(new UserCredsValidator(), mockLauncher, finder, fileCreator, paths, stampFiles, info) { + authFileWriter = mock(AuthFileWriter.class); + keyringWriter = mock(KeyringWriter.class); + mongoSetup = new MongodbUserSetup(new UserCredsValidator(), mockLauncher, finder, fileCreator, paths, stampFiles, info, authFileWriter, keyringWriter) { @Override int runMongo() { //instead of running mongo through ProcessBuilder @@ -220,7 +228,7 @@ Path testRoot = TestRootHelper.createTestRootDirectory(getClass().getName()); when(paths.getSystemThermostatHome()).thenReturn(testRoot.toFile()); try { - mongoSetup = new MongodbUserSetup(new UserCredsValidator(), mockLauncher, finder, fileCreator, paths, stampFiles, info) { + mongoSetup = new MongodbUserSetup(new UserCredsValidator(), mockLauncher, finder, fileCreator, paths, stampFiles, info, authFileWriter, keyringWriter) { @Override int runMongo() { //return non-zero val to test failure @@ -276,7 +284,7 @@ @SuppressWarnings("unchecked") @Test - public void testSetupMongodbUser() throws IOException { + public void testSetupMongodbUserWebappInstalled() throws IOException { Path testRoot = TestRootHelper.createTestRootDirectory(getClass().getName()); when(paths.getSystemThermostatHome()).thenReturn(testRoot.toFile()); try { @@ -332,7 +340,7 @@ } }).when(mockLauncher).run(eq(MongodbUserSetup.STORAGE_STOP_ARGS), isA(Collection.class), anyBoolean()); - mongoSetup = new MongodbUserSetup(new UserCredsValidator(), mockLauncher, finder, fileCreator, paths, stampFiles, info) { + mongoSetup = new MongodbUserSetup(new UserCredsValidator(), mockLauncher, finder, fileCreator, paths, stampFiles, info, authFileWriter, keyringWriter) { @Override int runMongo() { //instead of running mongo through ProcessBuilder @@ -358,6 +366,9 @@ verify(stampFiles).createMongodbUserStamp(); // temp unlocking calls this verify(stampFiles, times(1)).createSetupCompleteStamp(any(String.class)); + // we don't expect any interactions for the non-webapp case + verifyNoMoreInteractions(authFileWriter); + verifyNoMoreInteractions(keyringWriter); assertTrue(mockWebAuthFile.exists()); Properties webauthProps = new Properties(); @@ -373,10 +384,103 @@ } } - 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]); + @SuppressWarnings("unchecked") + @Test + public void testSetupMongodbUserNoWebappInstalled() throws IOException { + Path testRoot = TestRootHelper.createTestRootDirectory(getClass().getName()); + Path libDir = Paths.get(testRoot.toString(), "libs"); + Files.createDirectory(libDir); + File createUserJsFile = new File(libDir.toFile(), "create-user.js"); + createUserJsFile.createNewFile(); + when(paths.getSystemThermostatHome()).thenReturn(testRoot.toFile()); + // Fake webapp is *not* installed. + when(info.isWebAppInstalled()).thenReturn(false); + 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); + + 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()); + + // We started storage successfully, thus after we are done we + // stop it again. Mock the storage --stop. + 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.STOP); + + for (ActionListener<ApplicationState> listener : listeners[0]) { + listener.actionPerformed(mockActionEvent); + } + return null; + } + }).when(mockLauncher).run(eq(MongodbUserSetup.STORAGE_STOP_ARGS), isA(Collection.class), anyBoolean()); + + mongoSetup = new MongodbUserSetup(new UserCredsValidator(), mockLauncher, finder, fileCreator, paths, stampFiles, info, authFileWriter, keyringWriter) { + @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(2)).getActionId(); + verifyNoMoreInteractions(fileCreator); // We don't want web.auth created + verify(stampFiles).createMongodbUserStamp(); + + // twice = 1 x temp unlock, 2 x final creation since webapp is not installed + verify(stampFiles, times(2)).createSetupCompleteStamp(any(String.class)); + verify(authFileWriter).setCredentials(eq(username), argThat(matchesPassword(new char[] { 't', 'e', 's', 't' }))); + verify(authFileWriter).write(); + verify(keyringWriter).setCredentials(eq(username), argThat(matchesPassword(new char[] { 't', 'e', 's', 't' }))); + verify(keyringWriter).setStorageUrl(ThermostatSetup.MONGODB_STORAGE_URL); + verify(keyringWriter).write(); + + // Passed in password array is expected to be cleared. + assertArrayEquals(new char[] { '\0', '\0', '\0', '\0'}, password); + } finally { + TestRootHelper.recursivelyRemoveTestRootDirectory(testRoot); } } + + private CharArrayMatcher matchesPassword(char[] expected) { + return new CharArrayMatcher(expected); + } }
--- a/setup/command/src/test/java/com/redhat/thermostat/setup/command/internal/model/ThermostatSetupTest.java Fri Sep 04 14:57:14 2015 -0400 +++ b/setup/command/src/test/java/com/redhat/thermostat/setup/command/internal/model/ThermostatSetupTest.java Mon Sep 07 16:44:52 2015 +0200 @@ -36,21 +36,17 @@ package com.redhat.thermostat.setup.command.internal.model; -import static org.junit.Assert.*; +import static org.junit.Assert.assertTrue; 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.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -import static org.mockito.Mockito.doThrow; -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; @@ -58,10 +54,7 @@ import org.junit.Before; import org.junit.Test; -import com.redhat.thermostat.common.config.ClientPreferences; -import com.redhat.thermostat.shared.config.CommonPaths; -import com.redhat.thermostat.utils.keyring.Keyring; -import com.redhat.thermostat.utils.keyring.KeyringException; +import com.redhat.thermostat.setup.command.internal.cli.CharArrayMatcher; public class ThermostatSetupTest { @@ -77,7 +70,7 @@ @Test public void testIsWebAppInstalledDelegates() { StructureInformation structureInfo = mock(StructureInformation.class); - ThermostatSetup setup = new ThermostatSetup(userSetup, mongoUserSetup, structureInfo, mock(CommonPaths.class), mock(CredentialsFileCreator.class), mock(Keyring.class), mock(ClientPreferences.class)); + ThermostatSetup setup = new ThermostatSetup(userSetup, mongoUserSetup, structureInfo, mock(AuthFileWriter.class), mock(KeyringWriter.class)); when(structureInfo.isWebAppInstalled()).thenReturn(true); assertTrue(setup.isWebAppInstalled()); verify(structureInfo).isWebAppInstalled(); @@ -85,76 +78,57 @@ @Test public void testCreateAgentUser() { - ThermostatSetup setup = new ThermostatSetup(userSetup, mongoUserSetup, mock(StructureInformation.class), mock(CommonPaths.class), mock(CredentialsFileCreator.class), mock(Keyring.class), mock(ClientPreferences.class)); + AuthFileWriter writer = mock(AuthFileWriter.class); + ThermostatSetup setup = new ThermostatSetup(userSetup, mongoUserSetup, mock(StructureInformation.class), writer, mock(KeyringWriter.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)); + verify(writer).setCredentials(eq("foo-agent"), argThat(matchesPassword(new char[] { 't' }))); + } + + private CharArrayMatcher matchesPassword(char[] password) { + return new CharArrayMatcher(password); } @Test public void testCreateClientAdminUser() { - ThermostatSetup setup = new ThermostatSetup(userSetup, mongoUserSetup, mock(StructureInformation.class), mock(CommonPaths.class), mock(CredentialsFileCreator.class), mock(Keyring.class), mock(ClientPreferences.class)); + KeyringWriter writer = mock(KeyringWriter.class); + ThermostatSetup setup = new ThermostatSetup(userSetup, mongoUserSetup, mock(StructureInformation.class), mock(AuthFileWriter.class), writer); 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)); + verify(writer).setCredentials(eq("foo-client"), argThat(matchesPassword(new char[] { 't' }))); } @Test - public void commitCreatesAgentAuthFileStoresToKeyring() throws IOException { - Keyring keyring = mock(Keyring.class); // well behaved keyring - doCommitTest(keyring); - } - - private void doCommitTest(Keyring keyring) throws IOException { - ClientPreferences prefs = mock(ClientPreferences.class); - 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), keyring, prefs); - List<String> contents = Files.readAllLines(mockAgentAuthFile.toPath(), Charset.forName("UTF-8")); - assertEquals(0, contents.size()); - setup.createAgentUser("damian", new char[] { 't', 'e', 's', 't' }); - String clientUser = "client-admin"; - char[] clientPass = new char[] { 't' }; - setup.createClientAdminUser(clientUser, clientPass); - setup.commit(); - verify(userSetup).commit(); - verify(mongoUserSetup).commit(); - verify(keyring).savePassword(prefs.getConnectionUrl(), clientUser, clientPass); - verify(prefs).flush(); - verify(prefs).setSaveEntitlements(true); - verify(prefs).setUserName(clientUser); - 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")); - } catch (IOException e) { - e.printStackTrace(); - fail("Did not expect failure on commit()"); - } - finally { - Files.delete(mockAgentAuthFile.toPath()); - } + public void commitCreatesAgentAuthFileStoresToKeyringWhenWebappInstalled() throws IOException { + StructureInformation info = mock(StructureInformation.class); + when(info.isWebAppInstalled()).thenReturn(true); + AuthFileWriter authWriter = mock(AuthFileWriter.class); + KeyringWriter keyringWriter = mock(KeyringWriter.class); + ThermostatSetup setup = new ThermostatSetup(userSetup, mongoUserSetup, info, authWriter, keyringWriter); + setup.commit(); + verify(authWriter).write(); + verify(keyringWriter).write(); + verify(userSetup).commit(); + verify(mongoUserSetup).commit(); } @Test - public void keyringStoreFailureIsNonFatal() throws IOException { - Keyring keyring = mock(Keyring.class); - doThrow(new MockKeyringException("This is a test")).when(keyring).savePassword(any(String.class), any(String.class), any(char[].class)); - doCommitTest(keyring); - } - - @SuppressWarnings("serial") - private static class MockKeyringException extends KeyringException { - - public MockKeyringException(String string) { - super(string); - } - + public void commitOnlyCommitsMongodbCredsWhenWebappIsNotInstalled() throws IOException { + StructureInformation info = mock(StructureInformation.class); + when(info.isWebAppInstalled()).thenReturn(false); + AuthFileWriter authWriter = mock(AuthFileWriter.class); + KeyringWriter keyringWriter = mock(KeyringWriter.class); + ThermostatSetup setup = new ThermostatSetup(userSetup, mongoUserSetup, info, authWriter, keyringWriter); + setup.commit(); + verify(mongoUserSetup).commit(); + verifyNoMoreInteractions(authWriter); + verifyNoMoreInteractions(keyringWriter); + verifyNoMoreInteractions(userSetup); } private static class RoleMatcher extends BaseMatcher<String[]> {