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