Browse Source

SONAR-14159 enforce user authentication by default

tags/8.6.0.39681
Jacek 3 years ago
parent
commit
6e18f97ab5
18 changed files with 385 additions and 45 deletions
  1. 1
    0
      server/sonar-db-dao/src/testFixtures/java/org/sonar/db/SQDatabase.java
  2. 17
    0
      server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v00/PopulateInitialSchema.java
  3. 1
    0
      server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/DbVersion86.java
  4. 65
    0
      server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/SetForceAuthenticationSettings.java
  5. 57
    31
      server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v00/PopulateInitialSchemaTest.java
  6. 120
    0
      server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/SetForceAuthenticationSettingsTest.java
  7. 12
    0
      server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/SetForceAuthenticationSettingsTest/schema.sql
  8. 2
    1
      server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java
  9. 19
    7
      server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/UserSessionInitializerTest.java
  10. 2
    1
      server/sonar-webserver-core/src/main/java/org/sonar/server/platform/monitoring/StandaloneSystemSection.java
  11. 2
    1
      server/sonar-webserver-core/src/main/java/org/sonar/server/platform/monitoring/cluster/GlobalSystemSection.java
  12. 15
    1
      server/sonar-webserver-core/src/test/java/org/sonar/server/platform/monitoring/StandaloneSystemSectionTest.java
  13. 14
    0
      server/sonar-webserver-core/src/test/java/org/sonar/server/platform/monitoring/cluster/GlobalSystemSectionTest.java
  14. 2
    1
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/authentication/ws/ValidateAction.java
  15. 11
    0
      server/sonar-webserver-webapi/src/test/java/org/sonar/server/authentication/ws/ValidateActionTest.java
  16. 2
    1
      sonar-core/src/main/java/org/sonar/core/config/SecurityProperties.java
  17. 42
    0
      sonar-core/src/test/java/org/sonar/core/config/SecurityPropertiesTest.java
  18. 1
    1
      sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java

+ 1
- 0
server/sonar-db-dao/src/testFixtures/java/org/sonar/db/SQDatabase.java View File

@@ -158,6 +158,7 @@ public class SQDatabase extends DefaultDatabase {
parentContainer.add(SonarRuntimeImpl.forSonarQube(Version.create(8, 0), SonarQubeSide.SERVER, SonarEdition.COMMUNITY));
parentContainer.add(UuidFactoryFast.getInstance());
parentContainer.add(System2.INSTANCE);
parentContainer.add(MapSettings.class);

parentContainer.startComponents();


+ 17
- 0
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v00/PopulateInitialSchema.java View File

@@ -64,6 +64,7 @@ public class PopulateInitialSchema extends DataChange {
insertOrganization(context, organizationUuid, groups, defaultQGUuid);
insertOrgQualityGate(context, organizationUuid, defaultQGUuid);
insertInternalProperty(context, organizationUuid);
insertPropertyToEnableForceAuthentication(context);
insertGroupRoles(context, organizationUuid, groups);
insertGroupUsers(context, adminUserId, groups);
insertOrganizationMember(context, adminUserId, organizationUuid);
@@ -153,6 +154,22 @@ public class PopulateInitialSchema extends DataChange {
.commit();
}

private void insertPropertyToEnableForceAuthentication(Context context) throws SQLException {
String tableName = "properties";
truncateTable(context, tableName);

long now = system2.now();
Upsert upsert = context.prepareUpsert(createInsertStatement(tableName, "prop_key", "is_empty", "text_value", "created_at"));
upsert
.setString(1, "sonar.forceAuthentication")
.setBoolean(2, false)
.setString(3, "true")
.setLong(4, now);
upsert
.execute()
.commit();
}

private Groups insertGroups(Context context, String organizationUuid) throws SQLException {
truncateTable(context, "groups");


+ 1
- 0
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/DbVersion86.java View File

@@ -42,6 +42,7 @@ public class DbVersion86 implements DbVersion {
.add(4112, "Make 'name' column in 'groups' table not nullable", MakeNameColumnInGroupsTableNotNullable.class)
.add(4113, "Make 'name' column in 'groups' table unique", AddUniqueIndexOnNameColumnOfGroupsTable.class)
.add(4114, "Move default permission templates to internal properties", MoveDefaultTemplatesToInternalProperties.class)
.add(4115, "Set 'sonar.forceAuthentication' to false for upgraded instances", SetForceAuthenticationSettings.class)
;
}
}

+ 65
- 0
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/SetForceAuthenticationSettings.java View File

@@ -0,0 +1,65 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.platform.db.migration.version.v86;

import java.sql.SQLException;
import java.util.Optional;
import org.sonar.api.config.internal.Settings;
import org.sonar.api.utils.System2;
import org.sonar.core.util.UuidFactory;
import org.sonar.db.Database;
import org.sonar.server.platform.db.migration.step.DataChange;
import org.sonar.server.platform.db.migration.step.Select;

public class SetForceAuthenticationSettings extends DataChange {
private static final String PROPERTY_NAME_FORCE_AUTHENTICATION = "sonar.forceAuthentication";
private System2 system2;
private UuidFactory uuidFactory;
private Settings settings;

public SetForceAuthenticationSettings(Database db, System2 system2, UuidFactory uuidFactory, Settings settings) {
super(db);
this.system2 = system2;
this.uuidFactory = uuidFactory;
this.settings = settings;
}

@Override
protected void execute(Context context) throws SQLException {
Select select = context.prepareSelect("select p.text_value from properties p where p.prop_key = ?")
.setString(1, PROPERTY_NAME_FORCE_AUTHENTICATION);
String value = select.get(row -> row.getString(1));

if (value == null) {
Optional<String> forceAuthenticationSettings = settings.getRawString(PROPERTY_NAME_FORCE_AUTHENTICATION);

context.prepareUpsert("INSERT INTO properties"
+ "(prop_key, is_empty, text_value, created_at, uuid) "
+ "VALUES(?, ?, ?, ?, ?)")
.setString(1, PROPERTY_NAME_FORCE_AUTHENTICATION)
.setBoolean(2, false)
.setString(3, forceAuthenticationSettings.orElse("false"))
.setLong(4, system2.now())
.setString(5, uuidFactory.create())
.execute()
.commit();
}
}
}

+ 57
- 31
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v00/PopulateInitialSchemaTest.java View File

@@ -28,7 +28,6 @@ import java.util.stream.Stream;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.SonarRuntime;
import org.sonar.api.utils.System2;
import org.sonar.api.utils.Version;
@@ -45,9 +44,6 @@ public class PopulateInitialSchemaTest {

private static final long NOW = 1_500L;

@Rule
public ExpectedException expectedException = ExpectedException.none();

private final Random random = new Random();
private final Version version = Version.create(1 + random.nextInt(10), 1 + random.nextInt(10), random.nextInt(10));
private UuidFactory uuidFactory = UuidFactoryFast.getInstance();
@@ -77,6 +73,7 @@ public class PopulateInitialSchemaTest {
String orgUuid = verifyDefaultOrganization(userGroupId, qgUuid);
verifyOrgQualityGate(orgUuid, qgUuid);
verifyInternalProperties(orgUuid);
verifyProperties();
verifyRolesOfAdminsGroup();
verifyRolesOfUsersGroup();
verifyRolesOfAnyone();
@@ -100,20 +97,23 @@ public class PopulateInitialSchemaTest {
"created_at as \"CREATED_AT\", " +
"updated_at as \"UPDATED_AT\" " +
"from users where login='admin'");
assertThat(cols.get("LOGIN")).isEqualTo("admin");
assertThat(cols.get("NAME")).isEqualTo("Administrator");

assertThat(cols)
.containsEntry("LOGIN", "admin")
.containsEntry("NAME", "Administrator")
.containsEntry("EXTERNAL_ID", "admin")
.containsEntry("EXTERNAL_LOGIN", "admin")
.containsEntry("EXT_IDENT_PROVIDER", "sonarqube")
.containsEntry("USER_LOCAL", true)
.containsEntry("CRYPTED_PASSWORD", "$2a$12$uCkkXmhW5ThVK8mpBvnXOOJRLd64LJeHTeCkSuB3lfaR2N0AYBaSi")
.containsEntry("HASH_METHOD", "BCRYPT")
.containsEntry("IS_ROOT", false)
.containsEntry("ONBOARDED", true)
.containsEntry("CREATED_AT", NOW)
.containsEntry("UPDATED_AT", NOW);

assertThat(cols.get("EMAIL")).isNull();
assertThat(cols.get("EXTERNAL_ID")).isEqualTo("admin");
assertThat(cols.get("EXTERNAL_LOGIN")).isEqualTo("admin");
assertThat(cols.get("EXT_IDENT_PROVIDER")).isEqualTo("sonarqube");
assertThat(cols.get("USER_LOCAL")).isEqualTo(true);
assertThat(cols.get("CRYPTED_PASSWORD")).isEqualTo("$2a$12$uCkkXmhW5ThVK8mpBvnXOOJRLd64LJeHTeCkSuB3lfaR2N0AYBaSi");
assertThat(cols.get("SALT")).isNull();
assertThat(cols.get("HASH_METHOD")).isEqualTo("BCRYPT");
assertThat(cols.get("IS_ROOT")).isEqualTo(false);
assertThat(cols.get("ONBOARDED")).isEqualTo(true);
assertThat(cols.get("CREATED_AT")).isEqualTo(NOW);
assertThat(cols.get("UPDATED_AT")).isEqualTo(NOW);
}

private long verifyGroup(String expectedName, String expectedDescription) {
@@ -169,15 +169,17 @@ public class PopulateInitialSchemaTest {
assertThat(rows).hasSize(1);

Map<String, Object> row = rows.get(0);
assertThat(row.get("KEE")).isEqualTo("default-organization");
assertThat(row.get("NAME")).isEqualTo("Default Organization");
assertThat(row.get("GUARDED")).isEqualTo(true);
assertThat(row.get("PRIVATE")).isEqualTo(false);
assertThat(row.get("GROUP_ID")).isEqualTo(userGroupId);
assertThat(row.get("QG_UUID")).isEqualTo(defaultQQUuid);
assertThat(row.get("SUBSCRIPTION")).isEqualTo("SONARQUBE");
assertThat(row.get("CREATED_AT")).isEqualTo(NOW);
assertThat(row.get("UPDATED_AT")).isEqualTo(NOW);

assertThat(row)
.containsEntry("KEE", "default-organization")
.containsEntry("NAME", "Default Organization")
.containsEntry("GUARDED", true)
.containsEntry("PRIVATE", false)
.containsEntry("GROUP_ID", userGroupId)
.containsEntry("QG_UUID", defaultQQUuid)
.containsEntry("SUBSCRIPTION", "SONARQUBE")
.containsEntry("CREATED_AT", NOW)
.containsEntry("UPDATED_AT", NOW);
return (String) row.get("UUID");
}

@@ -191,8 +193,9 @@ public class PopulateInitialSchemaTest {

Map<String, Object> row = rows.get(0);
assertThat(row.get("UUID")).isNotNull();
assertThat(row.get("ORG")).isEqualTo(orgUuid);
assertThat(row.get("QG")).isEqualTo(qgUuid);
assertThat(row)
.containsEntry("ORG", orgUuid)
.containsEntry("QG", qgUuid);
}

private void verifyInternalProperties(String orgUuid) {
@@ -212,10 +215,33 @@ public class PopulateInitialSchemaTest {

private static void verifyInternalProperty(Map<String, Map<String, Object>> rowsByKey, String key, String val) {
Map<String, Object> row = rowsByKey.get(key);
assertThat(row.get("KEE")).isEqualTo(key);
assertThat(row.get("EMPTY")).isEqualTo(false);
assertThat(row.get("VAL")).isEqualTo(val);
assertThat(row.get("CREATED_AT")).isEqualTo(NOW);
assertThat(row)
.containsEntry("KEE", key)
.containsEntry("EMPTY", false)
.containsEntry("VAL", val)
.containsEntry("CREATED_AT", NOW);
}

private void verifyProperties() {
List<Map<String, Object>> rows = db.select("select " +
"prop_key as \"PROP_KEY\", " +
"is_empty as \"EMPTY\", " +
"text_value as \"VAL\"," +
"created_at as \"CREATED_AT\" " +
" from properties");
assertThat(rows).hasSize(1);

Map<String, Map<String, Object>> rowsByKey = rows.stream().collect(MoreCollectors.uniqueIndex(t -> (String) t.get("PROP_KEY")));
verifyProperty(rowsByKey, "sonar.forceAuthentication", "true");
}

private static void verifyProperty(Map<String, Map<String, Object>> rowsByKey, String key, String val) {
Map<String, Object> row = rowsByKey.get(key);
assertThat(row)
.containsEntry("PROP_KEY", key)
.containsEntry("EMPTY", false)
.containsEntry("VAL", val)
.containsEntry("CREATED_AT", NOW);
}

private void verifyRolesOfAdminsGroup() {

+ 120
- 0
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/SetForceAuthenticationSettingsTest.java View File

@@ -0,0 +1,120 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.platform.db.migration.version.v86;

import java.sql.SQLException;
import java.util.Optional;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.config.internal.Settings;
import org.sonar.api.impl.utils.TestSystem2;
import org.sonar.api.utils.System2;
import org.sonar.core.util.UuidFactoryFast;
import org.sonar.db.CoreDbTester;
import org.sonar.server.platform.db.migration.step.DataChange;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class SetForceAuthenticationSettingsTest {

private static final long NOW = 100_000_000_000L;

@Rule
public CoreDbTester db = CoreDbTester.createForSchema(SetForceAuthenticationSettingsTest.class, "schema.sql");

private final System2 system2 = new TestSystem2().setNow(NOW);

private final Settings settingsMock = mock(Settings.class);

private final DataChange underTest = new SetForceAuthenticationSettings(db.database(), system2, UuidFactoryFast.getInstance(), settingsMock);

@Test
public void insert_force_auth_property_based_on_settings_when_false() throws SQLException {
when(settingsMock.getRawString(any())).thenReturn(Optional.of("false"));
underTest.execute();

assertThatForceAuthenticationEquals("false");
}

@Test
public void insert_force_auth_property_based_on_settings_when_true() throws SQLException {
when(settingsMock.getRawString(any())).thenReturn(Optional.of("true"));
underTest.execute();

assertThatForceAuthenticationEquals("true");
}

@Test
public void insert_force_auth_property_based_on_settings_when_empty() throws SQLException {
when(settingsMock.getRawString(any())).thenReturn(Optional.empty());
underTest.execute();

assertThatForceAuthenticationEquals("false");
}

@Test
public void insert_force_auth_property_if_row_not_exists() throws SQLException {
underTest.execute();

assertThatForceAuthenticationEquals("false");
}

@Test
public void migration_is_reentrant() throws SQLException {
underTest.execute();
// re-entrant
underTest.execute();

assertThatForceAuthenticationEquals("false");
}

@Test
public void do_nothing_if_force_auth_property_exists_with_value_false() throws SQLException {
insertProperty("uuid-1", "sonar.forceAuthentication", "false");
underTest.execute();

assertThatForceAuthenticationEquals("false");
}

@Test
public void do_nothing_if_force_auth_property_exists_with_value_true() throws SQLException {
insertProperty("uuid-1", "sonar.forceAuthentication", "true");
underTest.execute();

assertThatForceAuthenticationEquals("true");
}

private void assertThatForceAuthenticationEquals(String s) {
assertThat(db.selectFirst("select p.text_value from properties p where p.prop_key = 'sonar.forceAuthentication'"))
.containsEntry("TEXT_VALUE", s);
}

private void insertProperty(String uuid, String key, String textValue) {
db.executeInsert("properties",
"uuid", uuid,
"prop_key", key,
"is_empty", false,
"text_value", textValue,
"created_at", NOW);
}
}

+ 12
- 0
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/SetForceAuthenticationSettingsTest/schema.sql View File

@@ -0,0 +1,12 @@
CREATE TABLE "PROPERTIES"(
"PROP_KEY" VARCHAR(512) NOT NULL,
"IS_EMPTY" BOOLEAN NOT NULL,
"TEXT_VALUE" VARCHAR(4000),
"CLOB_VALUE" CLOB,
"CREATED_AT" BIGINT NOT NULL,
"COMPONENT_UUID" VARCHAR(40),
"UUID" VARCHAR(40) NOT NULL,
"USER_UUID" VARCHAR(255)
);
ALTER TABLE "PROPERTIES" ADD CONSTRAINT "PK_PROPERTIES" PRIMARY KEY("UUID");
CREATE INDEX "PROPERTIES_KEY" ON "PROPERTIES"("PROP_KEY");

+ 2
- 1
server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java View File

@@ -34,6 +34,7 @@ import org.sonar.server.user.UserSession;

import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
import static org.apache.commons.lang.StringUtils.defaultString;
import static org.sonar.api.CoreProperties.CORE_FORCE_AUTHENTICATION_DEFAULT_VALUE;
import static org.sonar.api.CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY;
import static org.sonar.api.web.ServletFilter.UrlPattern.Builder.staticResourcePatterns;
import static org.sonar.server.authentication.AuthenticationError.handleAuthenticationError;
@@ -115,7 +116,7 @@ public class UserSessionInitializer {

private void loadUserSession(HttpServletRequest request, HttpServletResponse response, boolean urlSupportsSystemPasscode) {
UserSession session = requestAuthenticator.authenticate(request, response);
if (!session.isLoggedIn() && !urlSupportsSystemPasscode && config.getBoolean(CORE_FORCE_AUTHENTICATION_PROPERTY).orElse(false)) {
if (!session.isLoggedIn() && !urlSupportsSystemPasscode && config.getBoolean(CORE_FORCE_AUTHENTICATION_PROPERTY).orElse(CORE_FORCE_AUTHENTICATION_DEFAULT_VALUE)) {
// authentication is required
throw AuthenticationException.newBuilder()
.setSource(Source.local(AuthenticationEvent.Method.BASIC))

+ 19
- 7
server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/UserSessionInitializerTest.java View File

@@ -32,6 +32,7 @@ import org.sonar.api.utils.System2;
import org.sonar.db.DbTester;
import org.sonar.server.authentication.event.AuthenticationEvent;
import org.sonar.server.authentication.event.AuthenticationEvent.Method;
import org.sonar.server.authentication.event.AuthenticationEvent.Provider;
import org.sonar.server.authentication.event.AuthenticationEvent.Source;
import org.sonar.server.authentication.event.AuthenticationException;
import org.sonar.server.tester.AnonymousMockUserSession;
@@ -41,11 +42,12 @@ import org.sonar.server.user.UserSession;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;

public class UserSessionInitializerTest {
@@ -111,13 +113,12 @@ public class UserSessionInitializerTest {
ArgumentCaptor<AuthenticationException> exceptionArgumentCaptor = ArgumentCaptor.forClass(AuthenticationException.class);
when(threadLocalSession.isLoggedIn()).thenReturn(false);
when(authenticator.authenticate(request, response)).thenReturn(new AnonymousMockUserSession());
settings.setProperty("sonar.forceAuthentication", true);

assertThat(underTest.initUserSession(request, response)).isTrue();

verifyZeroInteractions(response);
verifyNoMoreInteractions(response);
verify(authenticationEvent).loginFailure(eq(request), exceptionArgumentCaptor.capture());
verifyZeroInteractions(threadLocalSession);
verifyNoMoreInteractions(threadLocalSession);
AuthenticationException authenticationException = exceptionArgumentCaptor.getValue();
assertThat(authenticationException.getSource()).isEqualTo(Source.local(Method.BASIC));
assertThat(authenticationException.getLogin()).isNull();
@@ -125,6 +126,17 @@ public class UserSessionInitializerTest {
assertThat(authenticationException.getPublicMessage()).isNull();
}

@Test
public void does_not_return_code_401_when_not_authenticated_and_with_force_authentication_off() {
when(threadLocalSession.isLoggedIn()).thenReturn(false);
when(authenticator.authenticate(request, response)).thenReturn(new AnonymousMockUserSession());
settings.setProperty("sonar.forceAuthentication", false);

assertThat(underTest.initUserSession(request, response)).isTrue();

verifyNoMoreInteractions(response);
}

@Test
public void return_401_and_stop_on_ws() {
when(request.getRequestURI()).thenReturn("/api/issues");
@@ -135,7 +147,7 @@ public class UserSessionInitializerTest {

verify(response).setStatus(401);
verify(authenticationEvent).loginFailure(request, authenticationException);
verifyZeroInteractions(threadLocalSession);
verifyNoMoreInteractions(threadLocalSession);
}

@Test
@@ -147,7 +159,7 @@ public class UserSessionInitializerTest {
assertThat(underTest.initUserSession(request, response)).isFalse();

verify(response).setStatus(401);
verifyZeroInteractions(threadLocalSession);
verifyNoMoreInteractions(threadLocalSession);
}

@Test
@@ -184,7 +196,7 @@ public class UserSessionInitializerTest {

assertThat(underTest.initUserSession(request, response)).isTrue();

verifyZeroInteractions(threadLocalSession, authenticator);
verifyNoMoreInteractions(threadLocalSession, authenticator);
reset(threadLocalSession, authenticator);
}


+ 2
- 1
server/sonar-webserver-core/src/main/java/org/sonar/server/platform/monitoring/StandaloneSystemSection.java View File

@@ -36,6 +36,7 @@ import org.sonar.server.platform.DockerSupport;
import org.sonar.server.platform.OfficialDistribution;
import org.sonar.server.user.SecurityRealmFactory;

import static org.sonar.api.CoreProperties.CORE_FORCE_AUTHENTICATION_DEFAULT_VALUE;
import static org.sonar.process.ProcessProperties.Property.PATH_DATA;
import static org.sonar.process.ProcessProperties.Property.PATH_HOME;
import static org.sonar.process.ProcessProperties.Property.PATH_TEMP;
@@ -104,7 +105,7 @@ public class StandaloneSystemSection extends BaseSectionMBean implements SystemS
}

private boolean getForceAuthentication() {
return config.getBoolean(CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY).orElse(false);
return config.getBoolean(CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY).orElse(CORE_FORCE_AUTHENTICATION_DEFAULT_VALUE);
}

@Override

+ 2
- 1
server/sonar-webserver-core/src/main/java/org/sonar/server/platform/monitoring/cluster/GlobalSystemSection.java View File

@@ -37,6 +37,7 @@ import org.sonar.server.authentication.IdentityProviderRepository;
import org.sonar.server.platform.DockerSupport;
import org.sonar.server.user.SecurityRealmFactory;

import static org.sonar.api.CoreProperties.CORE_FORCE_AUTHENTICATION_DEFAULT_VALUE;
import static org.sonar.process.systeminfo.SystemInfoUtils.setAttribute;

@ServerSide
@@ -91,7 +92,7 @@ public class GlobalSystemSection implements SystemInfoSection, Global {
}

private boolean getForceAuthentication() {
return config.getBoolean(CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY).orElse(false);
return config.getBoolean(CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY).orElse(CORE_FORCE_AUTHENTICATION_DEFAULT_VALUE);
}

private static void addIfNotEmpty(ProtobufSystemInfo.Section.Builder protobuf, String key, @Nullable List<String> values) {

+ 15
- 1
server/sonar-webserver-core/src/test/java/org/sonar/server/platform/monitoring/StandaloneSystemSectionTest.java View File

@@ -26,6 +26,7 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.sonar.api.CoreProperties;
import org.sonar.api.config.internal.MapSettings;
import org.sonar.api.platform.Server;
import org.sonar.api.security.SecurityRealm;
@@ -154,7 +155,20 @@ public class StandaloneSystemSectionTest {
@Test
public void return_nb_of_processors() {
ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
assertThat(attribute(protobuf, "Processors").getLongValue()).isGreaterThan(0);
assertThat(attribute(protobuf, "Processors").getLongValue()).isPositive();
}

@Test
public void get_force_authentication_defaults_to_true() {
ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
assertThatAttributeIs(protobuf, "Force authentication", true);
}

@Test
public void get_force_authentication() {
settings.setProperty(CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY, false);
ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
assertThatAttributeIs(protobuf, "Force authentication", false);
}

@Test

+ 14
- 0
server/sonar-webserver-core/src/test/java/org/sonar/server/platform/monitoring/cluster/GlobalSystemSectionTest.java View File

@@ -25,6 +25,7 @@ import com.tngtech.java.junit.dataprovider.UseDataProvider;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.sonar.api.CoreProperties;
import org.sonar.api.config.internal.MapSettings;
import org.sonar.api.platform.Server;
import org.sonar.api.security.SecurityRealm;
@@ -118,6 +119,19 @@ public class GlobalSystemSectionTest {
assertThatAttributeIs(protobuf, "External identity providers whose users are allowed to sign themselves up", "GitHub");
}

@Test
public void get_force_authentication_defaults_to_true() {
ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
assertThatAttributeIs(protobuf, "Force authentication", true);
}

@Test
public void get_force_authentication() {
settings.setProperty(CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY, false);
ProtobufSystemInfo.Section protobuf = underTest.toProtobuf();
assertThatAttributeIs(protobuf, "Force authentication", false);
}

@Test
@UseDataProvider("trueOrFalse")
public void get_docker_flag(boolean flag) {

+ 2
- 1
server/sonar-webserver-webapi/src/main/java/org/sonar/server/authentication/ws/ValidateAction.java View File

@@ -39,6 +39,7 @@ import org.sonar.server.authentication.event.AuthenticationException;
import org.sonar.server.ws.ServletFilterHandler;
import org.sonarqube.ws.MediaTypes;

import static org.sonar.api.CoreProperties.CORE_FORCE_AUTHENTICATION_DEFAULT_VALUE;
import static org.sonar.api.CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY;
import static org.sonar.server.authentication.ws.AuthenticationWs.AUTHENTICATION_CONTROLLER;

@@ -96,7 +97,7 @@ public class ValidateAction extends ServletFilter implements AuthenticationWsAct
if (user.isPresent()) {
return true;
}
return !config.getBoolean(CORE_FORCE_AUTHENTICATION_PROPERTY).orElse(false);
return !config.getBoolean(CORE_FORCE_AUTHENTICATION_PROPERTY).orElse(CORE_FORCE_AUTHENTICATION_DEFAULT_VALUE);
} catch (AuthenticationException e) {
return false;
}

+ 11
- 0
server/sonar-webserver-webapi/src/test/java/org/sonar/server/authentication/ws/ValidateActionTest.java View File

@@ -125,6 +125,17 @@ public class ValidateActionTest {
JsonAssert.assertJson(stringWriter.toString()).isSimilarTo("{\"valid\":false}");
}

@Test
public void return_false_when_no_jwt_nor_basic_auth_and_force_authentication_fallback_to_default() throws Exception {
when(jwtHttpHandler.validateToken(request, response)).thenReturn(Optional.empty());
when(basicAuthentication.authenticate(request)).thenReturn(Optional.empty());

underTest.doFilter(request, response, chain);

verify(response).setContentType(MediaTypes.JSON);
JsonAssert.assertJson(stringWriter.toString()).isSimilarTo("{\"valid\":false}");
}

@Test
public void return_false_when_jwt_throws_unauthorized_exception() throws Exception {
doThrow(AuthenticationException.class).when(jwtHttpHandler).validateToken(request, response);

+ 2
- 1
sonar-core/src/main/java/org/sonar/core/config/SecurityProperties.java View File

@@ -39,7 +39,8 @@ class SecurityProperties {
.name("Force user authentication")
.description(
"Forcing user authentication prevents anonymous users from accessing the SonarQube UI, or project data via the Web API. "
+ "Some specific read-only Web APIs, including those required to prompt authentication, are still available anonymously.")
+ "Some specific read-only Web APIs, including those required to prompt authentication, are still available anonymously."
+ "<br><strong>Disabling this setting can expose the instance to security risks.</strong>")
.type(PropertyType.BOOLEAN)
.category(CoreProperties.CATEGORY_SECURITY)
.build());

+ 42
- 0
sonar-core/src/test/java/org/sonar/core/config/SecurityPropertiesTest.java View File

@@ -0,0 +1,42 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.core.config;

import java.util.Optional;
import org.junit.Test;
import org.sonar.api.config.PropertyDefinition;

import static org.assertj.core.api.Assertions.assertThat;

public class SecurityPropertiesTest {

@Test
public void creates_properties() {
assertThat(SecurityProperties.all()).isNotEmpty();
Optional<PropertyDefinition> propertyDefinition = SecurityProperties.all().stream()
.filter(d -> d.key().equals("sonar.forceAuthentication")).findFirst();
assertThat(propertyDefinition)
.isNotEmpty()
.get()
.extracting(PropertyDefinition::defaultValue)
.isEqualTo("true");
}

}

+ 1
- 1
sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java View File

@@ -197,7 +197,7 @@ public interface CoreProperties {
/* Sonar Core */

String CORE_FORCE_AUTHENTICATION_PROPERTY = "sonar.forceAuthentication";
boolean CORE_FORCE_AUTHENTICATION_DEFAULT_VALUE = false;
boolean CORE_FORCE_AUTHENTICATION_DEFAULT_VALUE = true;

/**
* @deprecated since 2.14. See http://jira.sonarsource.com/browse/SONAR-3153. Replaced by {@link #CORE_AUTHENTICATOR_REALM}.

Loading…
Cancel
Save