]> source.dussan.org Git - sonarqube.git/blob
5fe5ac5008f4e8802e8f40140744fbded8dfa44c
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2020 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 package org.sonar.server.authentication;
21
22 import javax.servlet.http.Cookie;
23 import javax.servlet.http.HttpServletRequest;
24 import javax.servlet.http.HttpServletResponse;
25 import org.junit.Before;
26 import org.junit.Rule;
27 import org.junit.Test;
28 import org.mockito.ArgumentCaptor;
29 import org.sonar.api.config.internal.MapSettings;
30 import org.sonar.api.server.authentication.BaseIdentityProvider;
31 import org.sonar.api.utils.System2;
32 import org.sonar.db.DbTester;
33 import org.sonar.server.authentication.event.AuthenticationEvent;
34 import org.sonar.server.authentication.event.AuthenticationEvent.Method;
35 import org.sonar.server.authentication.event.AuthenticationEvent.Source;
36 import org.sonar.server.authentication.event.AuthenticationException;
37 import org.sonar.server.tester.AnonymousMockUserSession;
38 import org.sonar.server.tester.MockUserSession;
39 import org.sonar.server.user.ThreadLocalUserSession;
40 import org.sonar.server.user.UserSession;
41
42 import static org.assertj.core.api.Assertions.assertThat;
43 import static org.mockito.ArgumentMatchers.eq;
44 import static org.mockito.Mockito.doThrow;
45 import static org.mockito.Mockito.mock;
46 import static org.mockito.Mockito.reset;
47 import static org.mockito.Mockito.verify;
48 import static org.mockito.Mockito.verifyZeroInteractions;
49 import static org.mockito.Mockito.when;
50
51 public class UserSessionInitializerTest {
52
53   @Rule
54   public DbTester dbTester = DbTester.create(System2.INSTANCE);
55
56   private ThreadLocalUserSession threadLocalSession = mock(ThreadLocalUserSession.class);
57   private HttpServletRequest request = mock(HttpServletRequest.class);
58   private HttpServletResponse response = mock(HttpServletResponse.class);
59   private RequestAuthenticator authenticator = mock(RequestAuthenticator.class);
60   private AuthenticationEvent authenticationEvent = mock(AuthenticationEvent.class);
61   private MapSettings settings = new MapSettings();
62   private ArgumentCaptor<Cookie> cookieArgumentCaptor = ArgumentCaptor.forClass(Cookie.class);
63
64   private UserSessionInitializer underTest = new UserSessionInitializer(settings.asConfig(), threadLocalSession, authenticationEvent, authenticator);
65
66   @Before
67   public void setUp() {
68     when(request.getContextPath()).thenReturn("");
69     when(request.getRequestURI()).thenReturn("/measures");
70   }
71
72   @Test
73   public void check_urls() {
74     assertPathIsNotIgnored("/");
75     assertPathIsNotIgnored("/foo");
76     assertPathIsNotIgnored("/api/server_id/show");
77
78     assertPathIsIgnored("/api/authentication/login");
79     assertPathIsIgnored("/api/authentication/logout");
80     assertPathIsIgnored("/api/authentication/validate");
81     assertPathIsIgnored("/batch/index");
82     assertPathIsIgnored("/batch/file");
83     assertPathIsIgnored("/maintenance/index");
84     assertPathIsIgnored("/setup/index");
85     assertPathIsIgnored("/sessions/new");
86     assertPathIsIgnored("/sessions/logout");
87     assertPathIsIgnored("/sessions/unauthorized");
88     assertPathIsIgnored("/oauth2/callback/github");
89     assertPathIsIgnored("/oauth2/callback/foo");
90     assertPathIsIgnored("/api/system/db_migration_status");
91     assertPathIsIgnored("/api/system/status");
92     assertPathIsIgnored("/api/system/migrate_db");
93     assertPathIsIgnored("/api/server/version");
94     assertPathIsIgnored("/api/users/identity_providers");
95     assertPathIsIgnored("/api/l10n/index");
96
97     // exlude passcode urls
98     assertPathIsIgnoredWithAnonymousAccess("/api/ce/info");
99     assertPathIsIgnoredWithAnonymousAccess("/api/ce/pause");
100     assertPathIsIgnoredWithAnonymousAccess("/api/ce/resume");
101     assertPathIsIgnoredWithAnonymousAccess("/api/system/health");
102
103     // exclude static resources
104     assertPathIsIgnored("/css/style.css");
105     assertPathIsIgnored("/images/logo.png");
106     assertPathIsIgnored("/js/jquery.js");
107   }
108
109   @Test
110   public void return_code_401_when_not_authenticated_and_with_force_authentication() {
111     ArgumentCaptor<AuthenticationException> exceptionArgumentCaptor = ArgumentCaptor.forClass(AuthenticationException.class);
112     when(threadLocalSession.isLoggedIn()).thenReturn(false);
113     when(authenticator.authenticate(request, response)).thenReturn(new AnonymousMockUserSession());
114     settings.setProperty("sonar.forceAuthentication", true);
115
116     assertThat(underTest.initUserSession(request, response)).isTrue();
117
118     verifyZeroInteractions(response);
119     verify(authenticationEvent).loginFailure(eq(request), exceptionArgumentCaptor.capture());
120     verifyZeroInteractions(threadLocalSession);
121     AuthenticationException authenticationException = exceptionArgumentCaptor.getValue();
122     assertThat(authenticationException.getSource()).isEqualTo(Source.local(Method.BASIC));
123     assertThat(authenticationException.getLogin()).isNull();
124     assertThat(authenticationException.getMessage()).isEqualTo("User must be authenticated");
125     assertThat(authenticationException.getPublicMessage()).isNull();
126   }
127
128   @Test
129   public void return_401_and_stop_on_ws() {
130     when(request.getRequestURI()).thenReturn("/api/issues");
131     AuthenticationException authenticationException = AuthenticationException.newBuilder().setSource(Source.jwt()).setMessage("Token id hasn't been found").build();
132     doThrow(authenticationException).when(authenticator).authenticate(request, response);
133
134     assertThat(underTest.initUserSession(request, response)).isFalse();
135
136     verify(response).setStatus(401);
137     verify(authenticationEvent).loginFailure(request, authenticationException);
138     verifyZeroInteractions(threadLocalSession);
139   }
140
141   @Test
142   public void return_401_and_stop_on_batch_ws() {
143     when(request.getRequestURI()).thenReturn("/batch/global");
144     doThrow(AuthenticationException.newBuilder().setSource(Source.jwt()).setMessage("Token id hasn't been found").build())
145       .when(authenticator).authenticate(request, response);
146
147     assertThat(underTest.initUserSession(request, response)).isFalse();
148
149     verify(response).setStatus(401);
150     verifyZeroInteractions(threadLocalSession);
151   }
152
153   @Test
154   public void return_to_session_unauthorized_when_error_on_from_external_provider() throws Exception {
155     doThrow(AuthenticationException.newBuilder().setSource(Source.external(newBasicIdentityProvider("failing"))).setPublicMessage("Token id hasn't been found").build())
156       .when(authenticator).authenticate(request, response);
157
158     assertThat(underTest.initUserSession(request, response)).isFalse();
159
160     verify(response).sendRedirect("/sessions/unauthorized");
161     verify(response).addCookie(cookieArgumentCaptor.capture());
162     Cookie cookie = cookieArgumentCaptor.getValue();
163     assertThat(cookie.getName()).isEqualTo("AUTHENTICATION-ERROR");
164     assertThat(cookie.getValue()).isEqualTo("Token%20id%20hasn%27t%20been%20found");
165     assertThat(cookie.getPath()).isEqualTo("/");
166     assertThat(cookie.isHttpOnly()).isFalse();
167     assertThat(cookie.getMaxAge()).isEqualTo(300);
168     assertThat(cookie.getSecure()).isFalse();
169   }
170
171   @Test
172   public void return_to_session_unauthorized_when_error_on_from_external_provider_with_context_path() throws Exception {
173     when(request.getContextPath()).thenReturn("/sonarqube");
174     doThrow(AuthenticationException.newBuilder().setSource(Source.external(newBasicIdentityProvider("failing"))).setPublicMessage("Token id hasn't been found").build())
175       .when(authenticator).authenticate(request, response);
176
177     assertThat(underTest.initUserSession(request, response)).isFalse();
178
179     verify(response).sendRedirect("/sonarqube/sessions/unauthorized");
180   }
181
182   private void assertPathIsIgnored(String path) {
183     when(request.getRequestURI()).thenReturn(path);
184
185     assertThat(underTest.initUserSession(request, response)).isTrue();
186
187     verifyZeroInteractions(threadLocalSession, authenticator);
188     reset(threadLocalSession, authenticator);
189   }
190
191   private void assertPathIsIgnoredWithAnonymousAccess(String path) {
192     when(request.getRequestURI()).thenReturn(path);
193     UserSession session = new AnonymousMockUserSession();
194     when(authenticator.authenticate(request, response)).thenReturn(session);
195
196     assertThat(underTest.initUserSession(request, response)).isTrue();
197
198     verify(threadLocalSession).set(session);
199     reset(threadLocalSession, authenticator);
200   }
201
202   private void assertPathIsNotIgnored(String path) {
203     when(request.getRequestURI()).thenReturn(path);
204     UserSession session = new MockUserSession("foo");
205     when(authenticator.authenticate(request, response)).thenReturn(session);
206
207     assertThat(underTest.initUserSession(request, response)).isTrue();
208
209     verify(threadLocalSession).set(session);
210     reset(threadLocalSession, authenticator);
211   }
212
213   private static BaseIdentityProvider newBasicIdentityProvider(String name) {
214     BaseIdentityProvider mock = mock(BaseIdentityProvider.class);
215     when(mock.getName()).thenReturn(name);
216     return mock;
217   }
218 }