]> source.dussan.org Git - sonarqube.git/blob
cf2eb5f19fe595e932ec0385fd820b3904992f11
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2019 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 com.google.common.collect.ImmutableSet;
23 import java.util.Optional;
24 import java.util.Set;
25 import javax.servlet.http.HttpServletRequest;
26 import javax.servlet.http.HttpServletResponse;
27 import javax.servlet.http.HttpSession;
28 import org.junit.Before;
29 import org.junit.Rule;
30 import org.junit.Test;
31 import org.junit.rules.ExpectedException;
32 import org.mockito.ArgumentCaptor;
33 import org.sonar.api.platform.Server;
34 import org.sonar.api.server.authentication.OAuth2IdentityProvider;
35 import org.sonar.api.server.authentication.UserIdentity;
36 import org.sonar.db.user.UserDto;
37 import org.sonar.server.authentication.OAuth2ContextFactory.OAuthContextImpl;
38 import org.sonar.server.authentication.UserRegistration.ExistingEmailStrategy;
39 import org.sonar.server.user.TestUserSessionFactory;
40 import org.sonar.server.user.ThreadLocalUserSession;
41 import org.sonar.server.user.UserSession;
42
43 import static org.assertj.core.api.Assertions.assertThat;
44 import static org.mockito.ArgumentMatchers.any;
45 import static org.mockito.ArgumentMatchers.eq;
46 import static org.mockito.Mockito.mock;
47 import static org.mockito.Mockito.verify;
48 import static org.mockito.Mockito.when;
49
50 public class OAuth2ContextFactoryTest {
51
52   private static final String PROVIDER_KEY = "github";
53   private static final String SECURED_PUBLIC_ROOT_URL = "https://mydomain.com";
54   private static final String PROVIDER_NAME = "provider name";
55   private static final UserIdentity USER_IDENTITY = UserIdentity.builder()
56     .setProviderId("ABCD")
57     .setProviderLogin("johndoo")
58     .setLogin("id:johndoo")
59     .setName("John")
60     .setEmail("john@email.com")
61     .build();
62
63   @Rule
64   public ExpectedException thrown = ExpectedException.none();
65
66   private ThreadLocalUserSession threadLocalUserSession = mock(ThreadLocalUserSession.class);
67   private TestUserRegistrar userIdentityAuthenticator = new TestUserRegistrar();
68   private Server server = mock(Server.class);
69   private OAuthCsrfVerifier csrfVerifier = mock(OAuthCsrfVerifier.class);
70   private JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class);
71   private TestUserSessionFactory userSessionFactory = TestUserSessionFactory.standalone();
72   private OAuth2AuthenticationParameters oAuthParameters = mock(OAuth2AuthenticationParameters.class);
73   private HttpServletRequest request = mock(HttpServletRequest.class);
74   private HttpServletResponse response = mock(HttpServletResponse.class);
75   private HttpSession session = mock(HttpSession.class);
76   private OAuth2IdentityProvider identityProvider = mock(OAuth2IdentityProvider.class);
77
78   private OAuth2ContextFactory underTest = new OAuth2ContextFactory(threadLocalUserSession, userIdentityAuthenticator, server, csrfVerifier, jwtHttpHandler, userSessionFactory,
79     oAuthParameters);
80
81   @Before
82   public void setUp() throws Exception {
83     when(request.getSession()).thenReturn(session);
84     when(identityProvider.getKey()).thenReturn(PROVIDER_KEY);
85     when(identityProvider.getName()).thenReturn(PROVIDER_NAME);
86   }
87
88   @Test
89   public void create_context() {
90     when(server.getPublicRootUrl()).thenReturn(SECURED_PUBLIC_ROOT_URL);
91
92     OAuth2IdentityProvider.InitContext context = newInitContext();
93
94     assertThat(context.getRequest()).isEqualTo(request);
95     assertThat(context.getResponse()).isEqualTo(response);
96     assertThat(context.getCallbackUrl()).isEqualTo("https://mydomain.com/oauth2/callback/github");
97   }
98
99   @Test
100   public void generate_csrf_state() {
101     OAuth2IdentityProvider.InitContext context = newInitContext();
102
103     context.generateCsrfState();
104
105     verify(csrfVerifier).generateState(request, response);
106   }
107
108   @Test
109   public void redirect_to() throws Exception {
110     OAuth2IdentityProvider.InitContext context = newInitContext();
111
112     context.redirectTo("/test");
113
114     verify(response).sendRedirect("/test");
115   }
116
117   @Test
118   public void create_callback() {
119     when(server.getPublicRootUrl()).thenReturn(SECURED_PUBLIC_ROOT_URL);
120
121     OAuth2IdentityProvider.CallbackContext callback = newCallbackContext();
122
123     assertThat(callback.getRequest()).isEqualTo(request);
124     assertThat(callback.getResponse()).isEqualTo(response);
125     assertThat(callback.getCallbackUrl()).isEqualTo("https://mydomain.com/oauth2/callback/github");
126   }
127
128   @Test
129   public void authenticate() {
130     OAuth2IdentityProvider.CallbackContext callback = newCallbackContext();
131
132     callback.authenticate(USER_IDENTITY);
133
134     assertThat(userIdentityAuthenticator.isAuthenticated()).isTrue();
135     verify(threadLocalUserSession).set(any(UserSession.class));
136     ArgumentCaptor<UserDto> userArgumentCaptor = ArgumentCaptor.forClass(UserDto.class);
137     verify(jwtHttpHandler).generateToken(userArgumentCaptor.capture(), eq(request), eq(response));
138     assertThat(userArgumentCaptor.getValue().getLogin()).isEqualTo(USER_IDENTITY.getLogin());
139     assertThat(userArgumentCaptor.getValue().getExternalId()).isEqualTo(USER_IDENTITY.getProviderId());
140     assertThat(userArgumentCaptor.getValue().getExternalLogin()).isEqualTo(USER_IDENTITY.getProviderLogin());
141     assertThat(userArgumentCaptor.getValue().getExternalIdentityProvider()).isEqualTo(PROVIDER_KEY);
142   }
143
144   @Test
145   public void authenticate_with_allow_email_shift() {
146     when(oAuthParameters.getAllowEmailShift(request)).thenReturn(Optional.of(true));
147     OAuth2IdentityProvider.CallbackContext callback = newCallbackContext();
148
149     callback.authenticate(USER_IDENTITY);
150
151     assertThat(userIdentityAuthenticator.isAuthenticated()).isTrue();
152     assertThat(userIdentityAuthenticator.getAuthenticatorParameters().getExistingEmailStrategy()).isEqualTo(ExistingEmailStrategy.ALLOW);
153   }
154
155   @Test
156   public void authenticate_without_email_shift() {
157     when(oAuthParameters.getAllowEmailShift(request)).thenReturn(Optional.of(false));
158     OAuth2IdentityProvider.CallbackContext callback = newCallbackContext();
159
160     callback.authenticate(USER_IDENTITY);
161
162     assertThat(userIdentityAuthenticator.isAuthenticated()).isTrue();
163     assertThat(userIdentityAuthenticator.getAuthenticatorParameters().getExistingEmailStrategy()).isEqualTo(ExistingEmailStrategy.WARN);
164   }
165
166   @Test
167   public void authenticate_with_organization_alm_ids() {
168     OAuthContextImpl callback = (OAuthContextImpl) newCallbackContext();
169     Set<String> organizationAlmIds = ImmutableSet.of("ABCD", "EFGH");
170
171     callback.authenticate(USER_IDENTITY, organizationAlmIds);
172
173     assertThat(userIdentityAuthenticator.getAuthenticatorParameters().getOrganizationAlmIds()).containsAll(organizationAlmIds);
174   }
175
176   @Test
177   public void redirect_to_home() throws Exception {
178     when(server.getContextPath()).thenReturn("");
179     when(oAuthParameters.getReturnTo(request)).thenReturn(Optional.empty());
180     OAuth2IdentityProvider.CallbackContext callback = newCallbackContext();
181
182     callback.redirectToRequestedPage();
183
184     verify(response).sendRedirect("/");
185   }
186
187   @Test
188   public void redirect_to_home_with_context() throws Exception {
189     when(server.getContextPath()).thenReturn("/sonarqube");
190     when(oAuthParameters.getReturnTo(request)).thenReturn(Optional.empty());
191     OAuth2IdentityProvider.CallbackContext callback = newCallbackContext();
192
193     callback.redirectToRequestedPage();
194
195     verify(response).sendRedirect("/sonarqube/");
196   }
197
198   @Test
199   public void redirect_to_requested_page() throws Exception {
200     when(oAuthParameters.getReturnTo(request)).thenReturn(Optional.of("/settings"));
201     when(server.getContextPath()).thenReturn("");
202     OAuth2IdentityProvider.CallbackContext callback = newCallbackContext();
203
204     callback.redirectToRequestedPage();
205
206     verify(response).sendRedirect("/settings");
207   }
208
209   @Test
210   public void redirect_to_requested_page_does_not_need_context() throws Exception {
211     when(oAuthParameters.getReturnTo(request)).thenReturn(Optional.of("/sonarqube/settings"));
212     when(server.getContextPath()).thenReturn("/other");
213     OAuth2IdentityProvider.CallbackContext callback = newCallbackContext();
214
215     callback.redirectToRequestedPage();
216
217     verify(response).sendRedirect("/sonarqube/settings");
218   }
219
220   @Test
221   public void verify_csrf_state() {
222     OAuth2IdentityProvider.CallbackContext callback = newCallbackContext();
223
224     callback.verifyCsrfState();
225
226     verify(csrfVerifier).verifyState(request, response, identityProvider);
227   }
228
229   @Test
230   public void delete_oauth2_parameters_during_redirection() {
231     when(oAuthParameters.getReturnTo(request)).thenReturn(Optional.of("/settings"));
232     when(server.getContextPath()).thenReturn("");
233     OAuth2IdentityProvider.CallbackContext callback = newCallbackContext();
234
235     callback.redirectToRequestedPage();
236
237     verify(oAuthParameters).delete(eq(request), eq(response));
238   }
239
240   private OAuth2IdentityProvider.InitContext newInitContext() {
241     return underTest.newContext(request, response, identityProvider);
242   }
243
244   private OAuth2IdentityProvider.CallbackContext newCallbackContext() {
245     return underTest.newCallback(request, response, identityProvider);
246   }
247
248 }