]> source.dussan.org Git - sonarqube.git/blob
e0aed1011888aa54b52381923720fd64cb11a0d2
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2018 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.HttpServletRequest;
23 import org.junit.Before;
24 import org.junit.Rule;
25 import org.junit.Test;
26 import org.junit.rules.ExpectedException;
27 import org.sonar.api.config.internal.MapSettings;
28 import org.sonar.api.security.Authenticator;
29 import org.sonar.api.security.ExternalGroupsProvider;
30 import org.sonar.api.security.ExternalUsersProvider;
31 import org.sonar.api.security.SecurityRealm;
32 import org.sonar.api.security.UserDetails;
33 import org.sonar.server.authentication.event.AuthenticationEvent;
34 import org.sonar.server.authentication.event.AuthenticationEvent.Source;
35 import org.sonar.server.user.SecurityRealmFactory;
36
37 import static java.util.Arrays.asList;
38 import static org.assertj.core.api.Assertions.assertThat;
39 import static org.junit.rules.ExpectedException.none;
40 import static org.mockito.ArgumentMatchers.any;
41 import static org.mockito.Mockito.doThrow;
42 import static org.mockito.Mockito.mock;
43 import static org.mockito.Mockito.verify;
44 import static org.mockito.Mockito.verifyNoMoreInteractions;
45 import static org.mockito.Mockito.verifyZeroInteractions;
46 import static org.mockito.Mockito.when;
47 import static org.sonar.server.authentication.UserIdentityAuthenticatorParameters.ExistingEmailStrategy.FORBID;
48 import static org.sonar.server.authentication.event.AuthenticationEvent.Method.BASIC;
49 import static org.sonar.server.authentication.event.AuthenticationEvent.Method.BASIC_TOKEN;
50 import static org.sonar.server.authentication.event.AuthenticationExceptionMatcher.authenticationException;
51
52 public class CredentialsExternalAuthenticationTest {
53
54   private static final String LOGIN = "LOGIN";
55   private static final String PASSWORD = "PASSWORD";
56
57   private static final String REALM_NAME = "realm name";
58
59   @Rule
60   public ExpectedException expectedException = none();
61
62   private MapSettings settings = new MapSettings();
63
64   private SecurityRealmFactory securityRealmFactory = mock(SecurityRealmFactory.class);
65   private SecurityRealm realm = mock(SecurityRealm.class);
66   private Authenticator authenticator = mock(Authenticator.class);
67   private ExternalUsersProvider externalUsersProvider = mock(ExternalUsersProvider.class);
68   private ExternalGroupsProvider externalGroupsProvider = mock(ExternalGroupsProvider.class);
69
70   private TestUserIdentityAuthenticator userIdentityAuthenticator = new TestUserIdentityAuthenticator();
71   private AuthenticationEvent authenticationEvent = mock(AuthenticationEvent.class);
72
73   private HttpServletRequest request = mock(HttpServletRequest.class);
74
75   private CredentialsExternalAuthentication underTest = new CredentialsExternalAuthentication(settings.asConfig(), securityRealmFactory, userIdentityAuthenticator, authenticationEvent);
76
77   @Before
78   public void setUp() throws Exception {
79     when(realm.getName()).thenReturn(REALM_NAME);
80   }
81
82   @Test
83   public void authenticate() {
84     executeStartWithoutGroupSync();
85     when(authenticator.doAuthenticate(any(Authenticator.Context.class))).thenReturn(true);
86     UserDetails userDetails = new UserDetails();
87     userDetails.setName("name");
88     userDetails.setEmail("email");
89     when(externalUsersProvider.doGetUserDetails(any(ExternalUsersProvider.Context.class))).thenReturn(userDetails);
90
91     underTest.authenticate(new Credentials(LOGIN, PASSWORD), request, BASIC);
92
93     assertThat(userIdentityAuthenticator.isAuthenticated()).isTrue();
94     assertThat(userIdentityAuthenticator.getAuthenticatorParameters().getExistingEmailStrategy()).isEqualTo(FORBID);
95     assertThat(userIdentityAuthenticator.getAuthenticatorParameters().getUserIdentity().getLogin()).isEqualTo(LOGIN);
96     assertThat(userIdentityAuthenticator.getAuthenticatorParameters().getUserIdentity().getProviderLogin()).isEqualTo(LOGIN);
97     assertThat(userIdentityAuthenticator.getAuthenticatorParameters().getUserIdentity().getProviderId()).isNull();
98     assertThat(userIdentityAuthenticator.getAuthenticatorParameters().getUserIdentity().getName()).isEqualTo("name");
99     assertThat(userIdentityAuthenticator.getAuthenticatorParameters().getUserIdentity().getEmail()).isEqualTo("email");
100     assertThat(userIdentityAuthenticator.getAuthenticatorParameters().getUserIdentity().shouldSyncGroups()).isFalse();
101     verify(authenticationEvent).loginSuccess(request, LOGIN, Source.realm(BASIC, REALM_NAME));
102   }
103
104   @Test
105   public void authenticate_with_sonarqube_identity_provider() {
106     executeStartWithoutGroupSync();
107     when(authenticator.doAuthenticate(any(Authenticator.Context.class))).thenReturn(true);
108     UserDetails userDetails = new UserDetails();
109     userDetails.setName("name");
110     userDetails.setEmail("email");
111     when(externalUsersProvider.doGetUserDetails(any(ExternalUsersProvider.Context.class))).thenReturn(userDetails);
112
113     underTest.authenticate(new Credentials(LOGIN, PASSWORD), request, BASIC);
114
115     assertThat(userIdentityAuthenticator.isAuthenticated()).isTrue();
116     assertThat(userIdentityAuthenticator.getAuthenticatorParameters().getProvider().getKey()).isEqualTo("sonarqube");
117     assertThat(userIdentityAuthenticator.getAuthenticatorParameters().getProvider().getName()).isEqualTo("sonarqube");
118     assertThat(userIdentityAuthenticator.getAuthenticatorParameters().getProvider().getDisplay()).isNull();
119     assertThat(userIdentityAuthenticator.getAuthenticatorParameters().getProvider().isEnabled()).isTrue();
120     verify(authenticationEvent).loginSuccess(request, LOGIN, Source.realm(BASIC, REALM_NAME));
121   }
122
123   @Test
124   public void login_is_used_when_no_name_provided() {
125     executeStartWithoutGroupSync();
126     when(authenticator.doAuthenticate(any(Authenticator.Context.class))).thenReturn(true);
127     UserDetails userDetails = new UserDetails();
128     userDetails.setEmail("email");
129     when(externalUsersProvider.doGetUserDetails(any(ExternalUsersProvider.Context.class))).thenReturn(userDetails);
130
131     underTest.authenticate(new Credentials(LOGIN, PASSWORD), request, BASIC);
132
133     assertThat(userIdentityAuthenticator.getAuthenticatorParameters().getProvider().getName()).isEqualTo("sonarqube");
134     verify(authenticationEvent).loginSuccess(request, LOGIN, Source.realm(BASIC, REALM_NAME));
135   }
136
137   @Test
138   public void authenticate_with_group_sync() {
139     when(externalGroupsProvider.doGetGroups(any(ExternalGroupsProvider.Context.class))).thenReturn(asList("group1", "group2"));
140     executeStartWithGroupSync();
141
142     executeAuthenticate();
143
144     assertThat(userIdentityAuthenticator.isAuthenticated()).isTrue();
145     assertThat(userIdentityAuthenticator.getAuthenticatorParameters().getUserIdentity().shouldSyncGroups()).isTrue();
146     verify(authenticationEvent).loginSuccess(request, LOGIN, Source.realm(BASIC, REALM_NAME));
147   }
148
149   @Test
150   public void use_login_if_user_details_contains_no_name() {
151     executeStartWithoutGroupSync();
152     when(authenticator.doAuthenticate(any(Authenticator.Context.class))).thenReturn(true);
153     UserDetails userDetails = new UserDetails();
154     userDetails.setName(null);
155     when(externalUsersProvider.doGetUserDetails(any(ExternalUsersProvider.Context.class))).thenReturn(userDetails);
156
157     underTest.authenticate(new Credentials(LOGIN, PASSWORD), request, BASIC);
158
159     assertThat(userIdentityAuthenticator.isAuthenticated()).isTrue();
160     assertThat(userIdentityAuthenticator.getAuthenticatorParameters().getUserIdentity().getName()).isEqualTo(LOGIN);
161     verify(authenticationEvent).loginSuccess(request, LOGIN, Source.realm(BASIC, REALM_NAME));
162   }
163
164   @Test
165   public void use_downcase_login() {
166     settings.setProperty("sonar.authenticator.downcase", true);
167     executeStartWithoutGroupSync();
168
169     executeAuthenticate("LOGIN");
170
171     assertThat(userIdentityAuthenticator.isAuthenticated()).isTrue();
172     assertThat(userIdentityAuthenticator.getAuthenticatorParameters().getUserIdentity().getLogin()).isEqualTo("login");
173     assertThat(userIdentityAuthenticator.getAuthenticatorParameters().getUserIdentity().getProviderLogin()).isEqualTo("login");
174     verify(authenticationEvent).loginSuccess(request, "login", Source.realm(BASIC, REALM_NAME));
175   }
176
177   @Test
178   public void does_not_user_downcase_login() {
179     settings.setProperty("sonar.authenticator.downcase", false);
180     executeStartWithoutGroupSync();
181
182     executeAuthenticate("LoGiN");
183
184     assertThat(userIdentityAuthenticator.isAuthenticated()).isTrue();
185     assertThat(userIdentityAuthenticator.getAuthenticatorParameters().getUserIdentity().getLogin()).isEqualTo("LoGiN");
186     assertThat(userIdentityAuthenticator.getAuthenticatorParameters().getUserIdentity().getProviderLogin()).isEqualTo("LoGiN");
187     verify(authenticationEvent).loginSuccess(request, "LoGiN", Source.realm(BASIC, REALM_NAME));
188   }
189
190   @Test
191   public void fail_to_authenticate_when_user_details_are_null() {
192     executeStartWithoutGroupSync();
193     when(authenticator.doAuthenticate(any(Authenticator.Context.class))).thenReturn(true);
194
195     when(externalUsersProvider.doGetUserDetails(any(ExternalUsersProvider.Context.class))).thenReturn(null);
196
197     expectedException.expect(authenticationException().from(Source.realm(BASIC, REALM_NAME)).withLogin(LOGIN).andNoPublicMessage());
198     expectedException.expectMessage("No user details");
199     try {
200       underTest.authenticate(new Credentials(LOGIN, PASSWORD), request, BASIC);
201     } finally {
202       verifyZeroInteractions(authenticationEvent);
203     }
204   }
205
206   @Test
207   public void fail_to_authenticate_when_external_authentication_fails() {
208     executeStartWithoutGroupSync();
209     when(externalUsersProvider.doGetUserDetails(any(ExternalUsersProvider.Context.class))).thenReturn(new UserDetails());
210
211     when(authenticator.doAuthenticate(any(Authenticator.Context.class))).thenReturn(false);
212
213     expectedException.expect(authenticationException().from(Source.realm(BASIC, REALM_NAME)).withLogin(LOGIN).andNoPublicMessage());
214     expectedException.expectMessage("Realm returned authenticate=false");
215     try {
216       underTest.authenticate(new Credentials(LOGIN, PASSWORD), request, BASIC);
217     } finally {
218       verifyZeroInteractions(authenticationEvent);
219     }
220   }
221
222   @Test
223   public void fail_to_authenticate_when_any_exception_is_thrown() {
224     executeStartWithoutGroupSync();
225     String expectedMessage = "emulating exception in doAuthenticate";
226     doThrow(new IllegalArgumentException(expectedMessage)).when(authenticator).doAuthenticate(any(Authenticator.Context.class));
227
228     when(externalUsersProvider.doGetUserDetails(any(ExternalUsersProvider.Context.class))).thenReturn(new UserDetails());
229
230     expectedException.expect(authenticationException().from(Source.realm(BASIC_TOKEN, REALM_NAME)).withLogin(LOGIN).andNoPublicMessage());
231     expectedException.expectMessage(expectedMessage);
232     try {
233       underTest.authenticate(new Credentials(LOGIN, PASSWORD), request, BASIC_TOKEN);
234     } finally {
235       verifyZeroInteractions(authenticationEvent);
236     }
237   }
238
239   @Test
240   public void return_empty_user_when_no_realm() {
241     assertThat(underTest.authenticate(new Credentials(LOGIN, PASSWORD), request, BASIC)).isEmpty();
242     verifyNoMoreInteractions(authenticationEvent);
243   }
244
245   @Test
246   public void fail_to_start_when_no_authenticator() {
247     when(realm.doGetAuthenticator()).thenReturn(null);
248     when(securityRealmFactory.getRealm()).thenReturn(realm);
249
250     expectedException.expect(NullPointerException.class);
251     expectedException.expectMessage("No authenticator available");
252     underTest.start();
253   }
254
255   @Test
256   public void fail_to_start_when_no_user_provider() {
257     when(realm.doGetAuthenticator()).thenReturn(authenticator);
258     when(realm.getUsersProvider()).thenReturn(null);
259     when(securityRealmFactory.getRealm()).thenReturn(realm);
260
261     expectedException.expect(NullPointerException.class);
262     expectedException.expectMessage("No users provider available");
263     underTest.start();
264   }
265
266   private void executeStartWithoutGroupSync() {
267     when(realm.doGetAuthenticator()).thenReturn(authenticator);
268     when(realm.getUsersProvider()).thenReturn(externalUsersProvider);
269     when(securityRealmFactory.getRealm()).thenReturn(realm);
270     underTest.start();
271   }
272
273   private void executeStartWithGroupSync() {
274     when(realm.doGetAuthenticator()).thenReturn(authenticator);
275     when(realm.getUsersProvider()).thenReturn(externalUsersProvider);
276     when(realm.getGroupsProvider()).thenReturn(externalGroupsProvider);
277     when(securityRealmFactory.getRealm()).thenReturn(realm);
278     underTest.start();
279   }
280
281   private void executeAuthenticate() {
282     executeAuthenticate(LOGIN);
283   }
284
285   private void executeAuthenticate(String login) {
286     when(authenticator.doAuthenticate(any(Authenticator.Context.class))).thenReturn(true);
287     UserDetails userDetails = new UserDetails();
288     userDetails.setName("name");
289     when(externalUsersProvider.doGetUserDetails(any(ExternalUsersProvider.Context.class))).thenReturn(userDetails);
290     underTest.authenticate(new Credentials(login, PASSWORD), request, BASIC);
291   }
292
293 }