aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvgeny Mandrikov <mandrikov@gmail.com>2012-01-24 09:21:30 +0400
committerEvgeny Mandrikov <mandrikov@gmail.com>2012-01-24 09:27:15 +0400
commita3e3cd6494586597404360435d11f4f686648e10 (patch)
treeddac9080a22b2777d283f41fe21d59fdab78f1f8
parent3547823a971749e0bec7fdb2a5f1ce99f37b23f5 (diff)
downloadsonarqube-a3e3cd6494586597404360435d11f4f686648e10.tar.gz
sonarqube-a3e3cd6494586597404360435d11f4f686648e10.zip
SONAR-3138 Prevent possible security flaws
* Save external password only if enabled "sonar.security.savePassword". * Bypass restriction on password length (4), when external system enabled. * Improve error handling.
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java12
-rw-r--r--sonar-server/src/main/java/org/sonar/server/ui/AuthenticatorNotFoundException.java31
-rw-r--r--sonar-server/src/main/java/org/sonar/server/ui/SecurityRealmFactory.java13
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/lib/need_authentication.rb18
-rw-r--r--sonar-server/src/test/java/org/sonar/server/ui/SecurityRealmFactoryTest.java30
5 files changed, 54 insertions, 50 deletions
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
index 6a105b10e98..0d33ee0d00c 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
@@ -189,6 +189,18 @@ import java.util.List;
// SERVER-SIDE TECHNICAL PROPERTIES
@Property(
+ key = "sonar.security.realm",
+ name = "Security Realm",
+ project = false,
+ global = false
+ ),
+ @Property(
+ key = "sonar.security.savePassword",
+ name = "Save external password",
+ project = false,
+ global = false
+ ),
+ @Property(
key = "sonar.authenticator.downcase",
name = "Downcase login",
description = "Downcase login during user authentication, typically for Active Directory",
diff --git a/sonar-server/src/main/java/org/sonar/server/ui/AuthenticatorNotFoundException.java b/sonar-server/src/main/java/org/sonar/server/ui/AuthenticatorNotFoundException.java
deleted file mode 100644
index 0ea7621b041..00000000000
--- a/sonar-server/src/main/java/org/sonar/server/ui/AuthenticatorNotFoundException.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2012 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar 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.
- *
- * Sonar 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 Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
- */
-package org.sonar.server.ui;
-
-/**
- * @deprecated in 2.14 and should be removed
- */
-@Deprecated
-public class AuthenticatorNotFoundException extends RuntimeException {
-
- public AuthenticatorNotFoundException(String classname) {
- super(classname);
- }
-}
diff --git a/sonar-server/src/main/java/org/sonar/server/ui/SecurityRealmFactory.java b/sonar-server/src/main/java/org/sonar/server/ui/SecurityRealmFactory.java
index 2761f575399..23c1f653d0f 100644
--- a/sonar-server/src/main/java/org/sonar/server/ui/SecurityRealmFactory.java
+++ b/sonar-server/src/main/java/org/sonar/server/ui/SecurityRealmFactory.java
@@ -27,6 +27,7 @@ import org.sonar.api.ServerComponent;
import org.sonar.api.config.Settings;
import org.sonar.api.security.LoginPasswordAuthenticator;
import org.sonar.api.security.SecurityRealm;
+import org.sonar.api.utils.SonarException;
/**
* @since 2.14
@@ -34,7 +35,6 @@ import org.sonar.api.security.SecurityRealm;
public class SecurityRealmFactory implements ServerComponent {
private static final Logger INFO = LoggerFactory.getLogger("org.sonar.INFO");
- private static final Logger LOG = LoggerFactory.getLogger(SecurityRealmFactory.class);
private final boolean ignoreStartupFailure;
private final SecurityRealm realm;
@@ -49,16 +49,14 @@ public class SecurityRealmFactory implements ServerComponent {
if (!StringUtils.isEmpty(realmName)) {
selectedRealm = selectRealm(realms, realmName);
if (selectedRealm == null) {
- LOG.error("Realm not found. Please check the property '" + REALM_PROPERTY + "' in conf/sonar.properties");
- throw new AuthenticatorNotFoundException(realmName);
+ throw new SonarException("Realm '" + realmName + "' not found. Please check the property '" + REALM_PROPERTY + "' in conf/sonar.properties");
}
}
if (selectedRealm == null && !StringUtils.isEmpty(className)) {
LoginPasswordAuthenticator authenticator = selectAuthenticator(authenticators, className);
if (authenticator == null) {
- LOG.error("Authenticator plugin not found. Please check the property '" + CoreProperties.CORE_AUTHENTICATOR_CLASS
+ throw new SonarException("Authenticator '" + className + "' not found. Please check the property '" + CoreProperties.CORE_AUTHENTICATOR_CLASS
+ "' in conf/sonar.properties");
- throw new AuthenticatorNotFoundException(className);
}
selectedRealm = new CompatibilityRealm(authenticator);
}
@@ -85,10 +83,9 @@ public class SecurityRealmFactory implements ServerComponent {
INFO.info("Security realm started");
} catch (RuntimeException e) {
if (ignoreStartupFailure) {
- LOG.error("IGNORED - Realm fails to start: " + e.getMessage());
+ INFO.error("IGNORED - Security realm fails to start: " + e.getMessage());
} else {
- LOG.error("Realm fails to start: " + e.getMessage());
- throw e;
+ throw new SonarException("Security realm fails to start: " + e.getMessage(), e);
}
}
}
diff --git a/sonar-server/src/main/webapp/WEB-INF/lib/need_authentication.rb b/sonar-server/src/main/webapp/WEB-INF/lib/need_authentication.rb
index afa3160562b..daa8c4addb7 100644
--- a/sonar-server/src/main/webapp/WEB-INF/lib/need_authentication.rb
+++ b/sonar-server/src/main/webapp/WEB-INF/lib/need_authentication.rb
@@ -45,6 +45,8 @@ class PluginRealm
@java_authenticator = java_realm.getLoginPasswordAuthenticator()
@java_users_provider = java_realm.getUsersProvider()
@java_groups_provider = java_realm.getGroupsProvider()
+
+ @save_password = Java::OrgSonarServerUi::JRubyFacade.new.getSettings().getBoolean('sonar.security.savePassword')
end
def authenticate?(username, password)
@@ -53,10 +55,11 @@ class PluginRealm
details = @java_users_provider.doGetUserDetails(username)
rescue Exception => e
Rails.logger.error("Error from external users provider: #{e.message}")
+ return false if !@save_password
return fallback(username, password)
else
# User exist in external system
- auth(username, password, details) if details
+ return auth(username, password, details) if details
# No such user in external system
return fallback(username, password)
end
@@ -109,7 +112,7 @@ class PluginRealm
java_facade = Java::OrgSonarServerUi::JRubyFacade.new
return nil if !java_facade.getSettings().getBoolean('sonar.authenticator.createUsers')
# Automatically create a user in the sonar db if authentication has been successfully done
- user = User.new(:login => username, :name => username, :email => '', :password => password, :password_confirmation => password)
+ user = User.new(:login => username, :name => username, :email => '')
default_group_name = java_facade.getSettings().getString('sonar.defaultGroup')
default_group = Group.find_by_name(default_group_name)
if default_group
@@ -119,11 +122,16 @@ class PluginRealm
end
end
if details
- user.update_attributes(:name => details.getName(), :email => details.getEmail())
+ user.name = details.getName()
+ user.email = details.getEmail()
+ end
+ if @save_password
+ user.password = password
+ user.password_confirmation = password
end
- user.update_attributes(:password => password, :password_confirmation => password)
synchronize_groups(user)
- user.save
+ # Note that validation disabled
+ user.save(false)
return user
end
diff --git a/sonar-server/src/test/java/org/sonar/server/ui/SecurityRealmFactoryTest.java b/sonar-server/src/test/java/org/sonar/server/ui/SecurityRealmFactoryTest.java
index 38ba8cd2662..2e5904a9661 100644
--- a/sonar-server/src/test/java/org/sonar/server/ui/SecurityRealmFactoryTest.java
+++ b/sonar-server/src/test/java/org/sonar/server/ui/SecurityRealmFactoryTest.java
@@ -25,9 +25,11 @@ import org.sonar.api.CoreProperties;
import org.sonar.api.config.Settings;
import org.sonar.api.security.LoginPasswordAuthenticator;
import org.sonar.api.security.SecurityRealm;
+import org.sonar.api.utils.SonarException;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -61,11 +63,16 @@ public class SecurityRealmFactoryTest {
assertThat(factory.getRealm(), nullValue());
}
- @Test(expected = AuthenticatorNotFoundException.class)
+ @Test
public void realmNotFound() {
settings.setProperty(SecurityRealmFactory.REALM_PROPERTY, "Fake");
- new SecurityRealmFactory(settings);
+ try {
+ new SecurityRealmFactory(settings);
+ fail();
+ } catch (SonarException e) {
+ assertThat(e.getMessage(), containsString("Realm 'Fake' not found."));
+ }
}
@Test
@@ -90,11 +97,16 @@ public class SecurityRealmFactoryTest {
assertThat(factory.getRealm(), is(realm));
}
- @Test(expected = AuthenticatorNotFoundException.class)
+ @Test
public void authenticatorNotFound() {
settings.setProperty(CoreProperties.CORE_AUTHENTICATOR_CLASS, "Fake");
- new SecurityRealmFactory(settings);
+ try {
+ new SecurityRealmFactory(settings);
+ fail();
+ } catch (SonarException e) {
+ assertThat(e.getMessage(), containsString("Authenticator 'Fake' not found."));
+ }
}
@Test
@@ -107,12 +119,18 @@ public class SecurityRealmFactoryTest {
verify(realm).init();
}
- @Test(expected = IllegalStateException.class)
+ @Test
public void shouldFail() {
SecurityRealm realm = spy(new AlwaysFailsRealm());
settings.setProperty(SecurityRealmFactory.REALM_PROPERTY, realm.getName());
- new SecurityRealmFactory(settings, new SecurityRealm[] {realm}).start();
+ try {
+ new SecurityRealmFactory(settings, new SecurityRealm[] {realm}).start();
+ fail();
+ } catch (SonarException e) {
+ assertThat(e.getCause(), instanceOf(IllegalStateException.class));
+ assertThat(e.getMessage(), containsString("Security realm fails to start"));
+ }
}
private static class AlwaysFailsRealm extends FakeRealm {