create update step for migration of anonymous mode

This commit is contained in:
Eduard Heimbuch
2020-08-05 14:11:41 +02:00
parent 9dc5d2da2b
commit 4929784a2b
4 changed files with 256 additions and 10 deletions

View File

@@ -0,0 +1,100 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.update.repository;
import lombok.Getter;
import lombok.NoArgsConstructor;
import sonia.scm.SCMContextProvider;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.migration.UpdateStep;
import sonia.scm.security.AnonymousMode;
import sonia.scm.store.ConfigurationStore;
import sonia.scm.store.ConfigurationStoreFactory;
import sonia.scm.store.StoreConstants;
import sonia.scm.version.Version;
import javax.inject.Inject;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.File;
import java.nio.file.Path;
import static sonia.scm.version.Version.parse;
public class AnonymousModeUpdateStep implements UpdateStep {
private final SCMContextProvider contextProvider;
private final ConfigurationStore<ScmConfiguration> configStore;
@Inject
public AnonymousModeUpdateStep(SCMContextProvider contextProvider, ConfigurationStoreFactory configurationStoreFactory) {
this.contextProvider = contextProvider;
this.configStore = configurationStoreFactory.withType(ScmConfiguration.class).withName("config").build();
}
@Override
public void doUpdate() throws JAXBException {
Path configFile = determineConfigDirectory().resolve("config" + StoreConstants.FILE_EXTENSION);
if (configFile.toFile().exists()) {
ScmConfiguration config = configStore.get();
if (getPreUpdateScmConfigurationFromOldConfig(configFile).isAnonymousAccessEnabled()) {
config.setAnonymousMode(AnonymousMode.PROTOCOL_ONLY);
} else {
config.setAnonymousMode(AnonymousMode.OFF);
}
}
}
@Override
public Version getTargetVersion() {
return parse("2.4.0");
}
@Override
public String getAffectedDataType() {
return "config.xml";
}
private PreUpdateScmConfiguration getPreUpdateScmConfigurationFromOldConfig(Path configFile) throws JAXBException {
JAXBContext jaxbContext = JAXBContext.newInstance(AnonymousModeUpdateStep.PreUpdateScmConfiguration.class);
return (AnonymousModeUpdateStep.PreUpdateScmConfiguration) jaxbContext.createUnmarshaller().unmarshal(configFile.toFile());
}
private Path determineConfigDirectory() {
return new File(contextProvider.getBaseDirectory(), StoreConstants.CONFIG_DIRECTORY_NAME).toPath();
}
@XmlRootElement(name = "scm-config")
@XmlAccessorType(XmlAccessType.FIELD)
@NoArgsConstructor
@Getter
static class PreUpdateScmConfiguration {
private boolean anonymousAccessEnabled;
}
}

View File

@@ -67,7 +67,7 @@ public class AuthenticationResourceTest {
@Rule
public ShiroRule shiro = new ShiroRule();
private RestDispatcher dispatcher = new RestDispatcher();
private final RestDispatcher dispatcher = new RestDispatcher();
@Mock
private AccessTokenBuilderFactory accessTokenBuilderFactory;
@@ -75,43 +75,43 @@ public class AuthenticationResourceTest {
@Mock
private AccessTokenBuilder accessTokenBuilder;
private AccessTokenCookieIssuer cookieIssuer = new DefaultAccessTokenCookieIssuer(mock(ScmConfiguration.class));
private final AccessTokenCookieIssuer cookieIssuer = new DefaultAccessTokenCookieIssuer(mock(ScmConfiguration.class));
private MockHttpResponse response = new MockHttpResponse();
private final MockHttpResponse response = new MockHttpResponse();
private static final String AUTH_JSON_TRILLIAN = "{\n" +
"\t\"cookie\": true,\n" +
"\t\"grant_type\": \"password\",\n" +
"\t\"grantType\": \"password\",\n" +
"\t\"username\": \"trillian\",\n" +
"\t\"password\": \"secret\"\n" +
"}";
private static final String AUTH_FORMENCODED_TRILLIAN = "cookie=true&grant_type=password&username=trillian&password=secret";
private static final String AUTH_FORMENCODED_TRILLIAN = "cookie=true&grantType=password&username=trillian&password=secret";
private static final String AUTH_JSON_TRILLIAN_WRONG_PW = "{\n" +
"\t\"cookie\": true,\n" +
"\t\"grant_type\": \"password\",\n" +
"\t\"grantType\": \"password\",\n" +
"\t\"username\": \"trillian\",\n" +
"\t\"password\": \"justWrong\"\n" +
"}";
private static final String AUTH_JSON_NOT_EXISTING_USER = "{\n" +
"\t\"cookie\": true,\n" +
"\t\"grant_type\": \"password\",\n" +
"\t\"grantType\": \"password\",\n" +
"\t\"username\": \"iDoNotExist\",\n" +
"\t\"password\": \"doesNotMatter\"\n" +
"}";
private static final String AUTH_JSON_WITHOUT_USERNAME = String.join("\n",
"{",
"\"grant_type\": \"password\",",
"\"grantType\": \"password\",",
"\"password\": \"tricia123\"",
"}"
);
private static final String AUTH_JSON_WITHOUT_PASSWORD = String.join("\n",
"{",
"\"grant_type\": \"password\",",
"\"grantType\": \"password\",",
"\"username\": \"trillian\"",
"}"
);
@@ -125,7 +125,7 @@ public class AuthenticationResourceTest {
private static final String AUTH_JSON_WITH_INVALID_GRANT_TYPE = String.join("\n",
"{",
"\"grant_type\": \"el speciale\",",
"\"grantType\": \"el speciale\",",
"\"username\": \"trillian\",",
"\"password\": \"tricia123\"",
"}"

View File

@@ -0,0 +1,102 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.update.repository;
import com.google.common.io.Resources;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.io.TempDir;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.SCMContextProvider;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.security.AnonymousMode;
import sonia.scm.store.ConfigurationStore;
import sonia.scm.store.InMemoryConfigurationStoreFactory;
import javax.xml.bind.JAXBException;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
import static sonia.scm.store.InMemoryConfigurationStoreFactory.create;
@ExtendWith(MockitoExtension.class)
class AnonymousModeUpdateStepTest {
@Mock
private SCMContextProvider contextProvider;
private AnonymousModeUpdateStep updateStep;
private ConfigurationStore<ScmConfiguration> configurationStore;
private Path configDir;
@BeforeEach
void initUpdateStep(@TempDir Path tempDir) throws IOException {
when(contextProvider.getBaseDirectory()).thenReturn(tempDir.toFile());
configDir = tempDir.resolve("config");
Files.createDirectories(configDir);
InMemoryConfigurationStoreFactory inMemoryConfigurationStoreFactory = create();
configurationStore = inMemoryConfigurationStoreFactory.get("config", null);
updateStep = new AnonymousModeUpdateStep(contextProvider, inMemoryConfigurationStoreFactory);
}
@Test
void shouldNotUpdateIfConfigFileNotAvailable() throws JAXBException {
updateStep.doUpdate();
assertThat(configurationStore.getOptional()).isNotPresent();
}
@Test
void shouldUpdateDisabledAnonymousMode() throws JAXBException, IOException {
copyTestDatabaseFile(configDir, "config.xml", "config.xml");
configurationStore.set(new ScmConfiguration());
updateStep.doUpdate();
assertThat((configurationStore.get()).getAnonymousMode()).isEqualTo(AnonymousMode.OFF);
}
@Test
void shouldUpdateEnabledAnonymousMode() throws JAXBException, IOException {
copyTestDatabaseFile(configDir, "config-withAnon.xml", "config.xml");
configurationStore.set(new ScmConfiguration());
updateStep.doUpdate();
assertThat((configurationStore.get()).getAnonymousMode()).isEqualTo(AnonymousMode.PROTOCOL_ONLY);
}
private void copyTestDatabaseFile(Path configDir, String sourceFileName, String targetFileName) throws IOException {
URL url = Resources.getResource("sonia/scm/update/security/" + sourceFileName);
Files.copy(url.openStream(), configDir.resolve(targetFileName));
}
}

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--
MIT License
Copyright (c) 2020-present Cloudogu GmbH and Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-->
<scm-config>
<admin-groups>admins,vogons</admin-groups>
<admin-users>arthur,dent,ldap-admin</admin-users>
<base-url>http://localhost:8081/scm</base-url>
<enableProxy>false</enableProxy>
<force-base-url>false</force-base-url>
<forwardPort>80</forwardPort>
<plugin-url>http://plugins.scm-manager.org/scm-plugin-backend/api/{version}/plugins?os={os}&amp;arch={arch}&amp;snapshot=false</plugin-url>
<proxyPort>8080</proxyPort>
<proxyServer>proxy.mydomain.com</proxyServer>
<servername>localhost</servername>
<enableSSL>false</enableSSL>
<enablePortForward>false</enablePortForward>
<sslPort>8181</sslPort>
<disableGroupingGrid>false</disableGroupingGrid>
<dateFormat>Y-m-d H:i:s</dateFormat>
<anonymousAccessEnabled>true</anonymousAccessEnabled>
</scm-config>