3 * Copyright (C) 2009-2018 SonarSource SA
4 * mailto:info AT sonarsource DOT com
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.
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.
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.
20 package org.sonar.server.authentication;
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;
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;
52 public class CredentialsExternalAuthenticationTest {
54 private static final String LOGIN = "LOGIN";
55 private static final String PASSWORD = "PASSWORD";
57 private static final String REALM_NAME = "realm name";
60 public ExpectedException expectedException = none();
62 private MapSettings settings = new MapSettings();
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);
70 private TestUserIdentityAuthenticator userIdentityAuthenticator = new TestUserIdentityAuthenticator();
71 private AuthenticationEvent authenticationEvent = mock(AuthenticationEvent.class);
73 private HttpServletRequest request = mock(HttpServletRequest.class);
75 private CredentialsExternalAuthentication underTest = new CredentialsExternalAuthentication(settings.asConfig(), securityRealmFactory, userIdentityAuthenticator, authenticationEvent);
78 public void setUp() throws Exception {
79 when(realm.getName()).thenReturn(REALM_NAME);
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);
91 underTest.authenticate(new Credentials(LOGIN, PASSWORD), request, BASIC);
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));
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);
113 underTest.authenticate(new Credentials(LOGIN, PASSWORD), request, BASIC);
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));
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);
131 underTest.authenticate(new Credentials(LOGIN, PASSWORD), request, BASIC);
133 assertThat(userIdentityAuthenticator.getAuthenticatorParameters().getProvider().getName()).isEqualTo("sonarqube");
134 verify(authenticationEvent).loginSuccess(request, LOGIN, Source.realm(BASIC, REALM_NAME));
138 public void authenticate_with_group_sync() {
139 when(externalGroupsProvider.doGetGroups(any(ExternalGroupsProvider.Context.class))).thenReturn(asList("group1", "group2"));
140 executeStartWithGroupSync();
142 executeAuthenticate();
144 assertThat(userIdentityAuthenticator.isAuthenticated()).isTrue();
145 assertThat(userIdentityAuthenticator.getAuthenticatorParameters().getUserIdentity().shouldSyncGroups()).isTrue();
146 verify(authenticationEvent).loginSuccess(request, LOGIN, Source.realm(BASIC, REALM_NAME));
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);
157 underTest.authenticate(new Credentials(LOGIN, PASSWORD), request, BASIC);
159 assertThat(userIdentityAuthenticator.isAuthenticated()).isTrue();
160 assertThat(userIdentityAuthenticator.getAuthenticatorParameters().getUserIdentity().getName()).isEqualTo(LOGIN);
161 verify(authenticationEvent).loginSuccess(request, LOGIN, Source.realm(BASIC, REALM_NAME));
165 public void use_downcase_login() {
166 settings.setProperty("sonar.authenticator.downcase", true);
167 executeStartWithoutGroupSync();
169 executeAuthenticate("LOGIN");
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));
178 public void does_not_user_downcase_login() {
179 settings.setProperty("sonar.authenticator.downcase", false);
180 executeStartWithoutGroupSync();
182 executeAuthenticate("LoGiN");
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));
191 public void fail_to_authenticate_when_user_details_are_null() {
192 executeStartWithoutGroupSync();
193 when(authenticator.doAuthenticate(any(Authenticator.Context.class))).thenReturn(true);
195 when(externalUsersProvider.doGetUserDetails(any(ExternalUsersProvider.Context.class))).thenReturn(null);
197 expectedException.expect(authenticationException().from(Source.realm(BASIC, REALM_NAME)).withLogin(LOGIN).andNoPublicMessage());
198 expectedException.expectMessage("No user details");
200 underTest.authenticate(new Credentials(LOGIN, PASSWORD), request, BASIC);
202 verifyZeroInteractions(authenticationEvent);
207 public void fail_to_authenticate_when_external_authentication_fails() {
208 executeStartWithoutGroupSync();
209 when(externalUsersProvider.doGetUserDetails(any(ExternalUsersProvider.Context.class))).thenReturn(new UserDetails());
211 when(authenticator.doAuthenticate(any(Authenticator.Context.class))).thenReturn(false);
213 expectedException.expect(authenticationException().from(Source.realm(BASIC, REALM_NAME)).withLogin(LOGIN).andNoPublicMessage());
214 expectedException.expectMessage("Realm returned authenticate=false");
216 underTest.authenticate(new Credentials(LOGIN, PASSWORD), request, BASIC);
218 verifyZeroInteractions(authenticationEvent);
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));
228 when(externalUsersProvider.doGetUserDetails(any(ExternalUsersProvider.Context.class))).thenReturn(new UserDetails());
230 expectedException.expect(authenticationException().from(Source.realm(BASIC_TOKEN, REALM_NAME)).withLogin(LOGIN).andNoPublicMessage());
231 expectedException.expectMessage(expectedMessage);
233 underTest.authenticate(new Credentials(LOGIN, PASSWORD), request, BASIC_TOKEN);
235 verifyZeroInteractions(authenticationEvent);
240 public void return_empty_user_when_no_realm() {
241 assertThat(underTest.authenticate(new Credentials(LOGIN, PASSWORD), request, BASIC)).isEmpty();
242 verifyNoMoreInteractions(authenticationEvent);
246 public void fail_to_start_when_no_authenticator() {
247 when(realm.doGetAuthenticator()).thenReturn(null);
248 when(securityRealmFactory.getRealm()).thenReturn(realm);
250 expectedException.expect(NullPointerException.class);
251 expectedException.expectMessage("No authenticator available");
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);
261 expectedException.expect(NullPointerException.class);
262 expectedException.expectMessage("No users provider available");
266 private void executeStartWithoutGroupSync() {
267 when(realm.doGetAuthenticator()).thenReturn(authenticator);
268 when(realm.getUsersProvider()).thenReturn(externalUsersProvider);
269 when(securityRealmFactory.getRealm()).thenReturn(realm);
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);
281 private void executeAuthenticate() {
282 executeAuthenticate(LOGIN);
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);