aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Brandhof <simon.brandhof@sonarsource.com>2016-08-06 10:17:05 +0200
committerGitHub <noreply@github.com>2016-08-06 10:17:05 +0200
commite55369090f3be4f1e4baa791b05c3f5cf1cef6e1 (patch)
tree72d61cf2877982bf0af0e4f3c1d168033f12269a
parente8732e24d023811bb542c0726dfeb1715849ba01 (diff)
downloadsonarqube-e55369090f3be4f1e4baa791b05c3f5cf1cef6e1.tar.gz
sonarqube-e55369090f3be4f1e4baa791b05c3f5cf1cef6e1.zip
SONAR-7581 ability to have user login in access.log
-rw-r--r--it/it-tests/src/test/java/it/Category4Suite.java7
-rw-r--r--it/it-tests/src/test/java/it/serverSystem/LogsTest.java75
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java41
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/user/UserSessionFilterTest.java27
-rw-r--r--sonar-application/src/main/assembly/conf/sonar.properties2
5 files changed, 122 insertions, 30 deletions
diff --git a/it/it-tests/src/test/java/it/Category4Suite.java b/it/it-tests/src/test/java/it/Category4Suite.java
index 15474220cbd..915a1ed8395 100644
--- a/it/it-tests/src/test/java/it/Category4Suite.java
+++ b/it/it-tests/src/test/java/it/Category4Suite.java
@@ -34,6 +34,7 @@ import it.http.HttpHeadersTest;
import it.projectComparison.ProjectComparisonTest;
import it.projectEvent.EventTest;
import it.qualityProfile.QualityProfilesPageTest;
+import it.serverSystem.LogsTest;
import it.serverSystem.ServerSystemTest;
import it.ui.UiTest;
import it.uiExtension.UiExtensionsTest;
@@ -95,7 +96,8 @@ import static util.ItUtils.xooPlugin;
WsLocalCallTest.class,
WsTest.class,
// quality profiles
- QualityProfilesPageTest.class
+ QualityProfilesPageTest.class,
+ LogsTest.class
})
public class Category4Suite {
@@ -117,5 +119,8 @@ public class Category4Suite {
// Used by WsLocalCallTest
.addPlugin(pluginArtifact("ws-plugin"))
+
+ // Used by LogsTest
+ .setServerProperty("sonar.web.accessLogs.pattern", LogsTest.ACCESS_LOGS_PATTERN)
.build();
}
diff --git a/it/it-tests/src/test/java/it/serverSystem/LogsTest.java b/it/it-tests/src/test/java/it/serverSystem/LogsTest.java
new file mode 100644
index 00000000000..bd77db688bc
--- /dev/null
+++ b/it/it-tests/src/test/java/it/serverSystem/LogsTest.java
@@ -0,0 +1,75 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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 it.serverSystem;
+
+import com.sonar.orchestrator.Orchestrator;
+import it.Category4Suite;
+import java.io.File;
+import java.io.IOException;
+import org.apache.commons.io.input.ReversedLinesFileReader;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.sonarqube.ws.client.GetRequest;
+import org.sonarqube.ws.client.WsClient;
+import util.ItUtils;
+
+import static java.lang.String.format;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class LogsTest {
+
+ public static final String ACCESS_LOGS_PATTERN = "\"%reqAttribute{LOGIN}\" \"%r\" %s";
+ private static final String PATH = "/called/from/LogsTest";
+
+ @ClassRule
+ public static final Orchestrator orchestrator = Category4Suite.ORCHESTRATOR;
+
+ /**
+ * SONAR-7581
+ */
+ @Test
+ public void test_access_logs() throws Exception {
+ // log "-" for anonymous
+ sendHttpRequest(ItUtils.newWsClient(orchestrator), PATH);
+ assertThat(accessLogsFile()).isFile().exists();
+ verifyLastAccessLogLine("-", PATH, 404);
+
+ sendHttpRequest(ItUtils.newAdminWsClient(orchestrator), PATH);
+ verifyLastAccessLogLine("admin", PATH, 404);
+ }
+
+ private void verifyLastAccessLogLine(String login, String path, int status) throws IOException {
+ assertThat(readLastAccessLog()).isEqualTo(format("\"%s\" \"GET %s HTTP/1.1\" %d", login, path, status));
+ }
+
+ private String readLastAccessLog() throws IOException {
+ try (ReversedLinesFileReader tailer = new ReversedLinesFileReader(accessLogsFile())) {
+ return tailer.readLine();
+ }
+ }
+
+ private void sendHttpRequest(WsClient client, String path) {
+ client.wsConnector().call(new GetRequest(path));
+ }
+
+ private File accessLogsFile() {
+ return new File(orchestrator.getServer().getHome(), "logs/access.log");
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java
index 3b2053eee66..f29a0807c74 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java
@@ -20,15 +20,6 @@
package org.sonar.server.authentication;
-import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
-import static org.sonar.api.CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY;
-import static org.sonar.api.web.ServletFilter.UrlPattern;
-import static org.sonar.api.web.ServletFilter.UrlPattern.Builder.staticResourcePatterns;
-import static org.sonar.server.authentication.ws.LoginAction.AUTH_LOGIN_URL;
-import static org.sonar.server.authentication.ws.ValidateAction.AUTH_VALIDATE_URL;
-import static org.sonar.server.user.ServerUserSession.createForAnonymous;
-import static org.sonar.server.user.ServerUserSession.createForUser;
-
import com.google.common.collect.ImmutableSet;
import java.util.Optional;
import java.util.Set;
@@ -39,11 +30,28 @@ import org.sonar.api.server.ServerSide;
import org.sonar.db.DbClient;
import org.sonar.db.user.UserDto;
import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.user.ServerUserSession;
import org.sonar.server.user.ThreadLocalUserSession;
+import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
+import static org.sonar.api.CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY;
+import static org.sonar.api.web.ServletFilter.UrlPattern;
+import static org.sonar.api.web.ServletFilter.UrlPattern.Builder.staticResourcePatterns;
+import static org.sonar.server.authentication.ws.LoginAction.AUTH_LOGIN_URL;
+import static org.sonar.server.authentication.ws.ValidateAction.AUTH_VALIDATE_URL;
+import static org.sonar.server.user.ServerUserSession.createForAnonymous;
+import static org.sonar.server.user.ServerUserSession.createForUser;
+
@ServerSide
public class UserSessionInitializer {
+ /**
+ * Key of attribute to be used for displaying user login
+ * in logs/access.log. The pattern to be configured
+ * in property sonar.web.accessLogs.pattern is "%reqAttribute{LOGIN}"
+ */
+ public static final String ACCESS_LOG_LOGIN = "LOGIN";
+
// SONAR-6546 these urls should be get from WebService
private static final Set<String> SKIPPED_URLS = ImmutableSet.of(
"/batch/index", "/batch/file",
@@ -64,15 +72,15 @@ public class UserSessionInitializer {
private final Settings settings;
private final JwtHttpHandler jwtHttpHandler;
private final BasicAuthenticator basicAuthenticator;
- private final ThreadLocalUserSession userSession;
+ private final ThreadLocalUserSession threadLocalSession;
public UserSessionInitializer(DbClient dbClient, Settings settings, JwtHttpHandler jwtHttpHandler, BasicAuthenticator basicAuthenticator,
- ThreadLocalUserSession userSession) {
+ ThreadLocalUserSession threadLocalSession) {
this.dbClient = dbClient;
this.settings = settings;
this.jwtHttpHandler = jwtHttpHandler;
this.basicAuthenticator = basicAuthenticator;
- this.userSession = userSession;
+ this.threadLocalSession = threadLocalSession;
}
public boolean initUserSession(HttpServletRequest request, HttpServletResponse response) {
@@ -97,17 +105,20 @@ public class UserSessionInitializer {
private void setUserSession(HttpServletRequest request, HttpServletResponse response) {
Optional<UserDto> user = authenticate(request, response);
if (user.isPresent()) {
- userSession.set(createForUser(dbClient, user.get()));
+ ServerUserSession session = createForUser(dbClient, user.get());
+ threadLocalSession.set(session);
+ request.setAttribute(ACCESS_LOG_LOGIN, session.getLogin());
} else {
if (settings.getBoolean(CORE_FORCE_AUTHENTICATION_PROPERTY)) {
throw new UnauthorizedException("User must be authenticated");
}
- userSession.set(createForAnonymous(dbClient));
+ threadLocalSession.set(createForAnonymous(dbClient));
+ request.setAttribute(ACCESS_LOG_LOGIN, "-");
}
}
public void removeUserSession() {
- userSession.remove();
+ threadLocalSession.remove();
}
// Try first to authenticate from JWT token, then try from basic http header
diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/UserSessionFilterTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/UserSessionFilterTest.java
index 1e75fe2279a..55e0b0cda3d 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/user/UserSessionFilterTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/user/UserSessionFilterTest.java
@@ -19,12 +19,6 @@
*/
package org.sonar.server.user;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
@@ -37,17 +31,22 @@ import org.sonar.core.platform.ComponentContainer;
import org.sonar.server.authentication.UserSessionInitializer;
import org.sonar.server.platform.Platform;
-public class UserSessionFilterTest {
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
- UserSessionInitializer userSessionInitializer = mock(UserSessionInitializer.class);
- Platform platform = mock(Platform.class);
- ComponentContainer componentContainer = mock(ComponentContainer.class);
+public class UserSessionFilterTest {
- HttpServletRequest request = mock(HttpServletRequest.class);
- HttpServletResponse response = mock(HttpServletResponse.class);
- FilterChain chain = mock(FilterChain.class);
+ private UserSessionInitializer userSessionInitializer = mock(UserSessionInitializer.class);
+ private Platform platform = mock(Platform.class);
+ private ComponentContainer componentContainer = mock(ComponentContainer.class);
+ private HttpServletRequest request = mock(HttpServletRequest.class);
+ private HttpServletResponse response = mock(HttpServletResponse.class);
+ private FilterChain chain = mock(FilterChain.class);
- UserSessionFilter underTest = new UserSessionFilter(platform);
+ private UserSessionFilter underTest = new UserSessionFilter(platform);
@Before
public void setUp() {
diff --git a/sonar-application/src/main/assembly/conf/sonar.properties b/sonar-application/src/main/assembly/conf/sonar.properties
index f2912583808..64641e13148 100644
--- a/sonar-application/src/main/assembly/conf/sonar.properties
+++ b/sonar-application/src/main/assembly/conf/sonar.properties
@@ -244,6 +244,8 @@
# - "common" is the Common Log Format, shortcut to: %h %l %u %user %date "%r" %s %b
# - "combined" is another format widely recognized, shortcut to: %h %l %u [%t] "%r" %s %b "%i{Referer}" "%i{User-Agent}"
# - else a custom pattern. See http://logback.qos.ch/manual/layouts.html#AccessPatternLayout.
+# The login of authenticated user is not implemented with "%u" but with "%reqAttribute{LOGIN}" (since version 6.1).
+# The value displayed for anonymous users is "-".
# If SonarQube is behind a reverse proxy, then the following value allows to display the correct remote IP address:
#sonar.web.accessLogs.pattern=%i{X-Forwarded-For} %l %u [%t] "%r" %s %b "%i{Referer}" "%i{User-Agent}"
# Default value is: