3 * Copyright (C) 2009-2017 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 java.util.Optional;
23 import java.util.stream.Collectors;
24 import org.junit.Rule;
25 import org.junit.Test;
26 import org.junit.rules.ExpectedException;
27 import org.sonar.api.config.MapSettings;
28 import org.sonar.api.server.authentication.UserIdentity;
29 import org.sonar.api.utils.System2;
30 import org.sonar.api.utils.internal.AlwaysIncreasingSystem2;
31 import org.sonar.core.util.stream.MoreCollectors;
32 import org.sonar.db.DbTester;
33 import org.sonar.db.organization.OrganizationDto;
34 import org.sonar.db.user.GroupDto;
35 import org.sonar.db.user.UserDto;
36 import org.sonar.server.organization.DefaultOrganizationProvider;
37 import org.sonar.server.organization.OrganizationCreation;
38 import org.sonar.server.organization.TestDefaultOrganizationProvider;
39 import org.sonar.server.organization.TestOrganizationFlags;
40 import org.sonar.server.user.NewUserNotifier;
41 import org.sonar.server.user.UserUpdater;
42 import org.sonar.server.user.index.UserIndexer;
43 import org.sonar.server.usergroups.DefaultGroupFinder;
45 import static com.google.common.collect.Sets.newHashSet;
46 import static java.util.Arrays.stream;
47 import static org.assertj.core.api.Assertions.assertThat;
48 import static org.mockito.Mockito.mock;
49 import static org.sonar.core.config.CorePropertyDefinitions.ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS;
50 import static org.sonar.db.user.UserTesting.newUserDto;
51 import static org.sonar.server.authentication.event.AuthenticationEvent.Method;
52 import static org.sonar.server.authentication.event.AuthenticationEvent.Source;
53 import static org.sonar.server.authentication.event.AuthenticationExceptionMatcher.authenticationException;
55 public class UserIdentityAuthenticatorTest {
57 private static String USER_LOGIN = "github-johndoo";
59 private static UserIdentity USER_IDENTITY = UserIdentity.builder()
60 .setProviderLogin("johndoo")
63 .setEmail("john@email.com")
66 private static TestIdentityProvider IDENTITY_PROVIDER = new TestIdentityProvider()
68 .setName("name of github")
70 .setAllowsUsersToSignUp(true);
73 public ExpectedException thrown = ExpectedException.none();
76 public DbTester db = DbTester.create(new AlwaysIncreasingSystem2());
78 private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
79 private OrganizationCreation organizationCreation = mock(OrganizationCreation.class);
80 private TestOrganizationFlags organizationFlags = TestOrganizationFlags.standalone();
81 private MapSettings settings = new MapSettings();
83 private UserUpdater userUpdater = new UserUpdater(
84 mock(NewUserNotifier.class),
86 mock(UserIndexer.class),
89 defaultOrganizationProvider,
91 new DefaultGroupFinder(db.getDbClient()),
93 private UserIdentityAuthenticator underTest = new UserIdentityAuthenticator(db.getDbClient(), userUpdater, defaultOrganizationProvider, organizationFlags,
94 new DefaultGroupFinder(db.getDbClient()));
97 public void authenticate_new_user() throws Exception {
98 organizationFlags.setEnabled(true);
99 underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER, Source.realm(Method.BASIC, IDENTITY_PROVIDER.getName()));
101 UserDto user = db.users().selectUserByLogin(USER_LOGIN).get();
102 assertThat(user).isNotNull();
103 assertThat(user.isActive()).isTrue();
104 assertThat(user.getName()).isEqualTo("John");
105 assertThat(user.getEmail()).isEqualTo("john@email.com");
106 assertThat(user.getExternalIdentity()).isEqualTo("johndoo");
107 assertThat(user.getExternalIdentityProvider()).isEqualTo("github");
108 assertThat(user.isRoot()).isFalse();
110 checkGroupMembership(user);
114 public void authenticate_new_user_with_groups() throws Exception {
115 organizationFlags.setEnabled(true);
116 GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
117 GroupDto group2 = db.users().insertGroup(db.getDefaultOrganization(), "group2");
119 authenticate(USER_LOGIN, "group1", "group2", "group3");
121 Optional<UserDto> user = db.users().selectUserByLogin(USER_LOGIN);
122 checkGroupMembership(user.get(), group1, group2);
126 public void authenticate_new_user_and_force_default_group_when_organizations_are_disabled() throws Exception {
127 organizationFlags.setEnabled(false);
128 UserDto user = db.users().insertUser();
129 GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
130 GroupDto defaultGroup = insertDefaultGroup();
131 db.users().insertMember(group1, user);
132 db.users().insertMember(defaultGroup, user);
134 authenticate(user.getLogin(), "group1");
136 checkGroupMembership(user, group1, defaultGroup);
140 public void does_not_force_default_group_when_authenticating_new_user_if_organizations_are_enabled() throws Exception {
141 organizationFlags.setEnabled(true);
142 UserDto user = db.users().insertUser();
143 GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
144 GroupDto defaultGroup = insertDefaultGroup();
145 db.users().insertMember(group1, user);
146 db.users().insertMember(defaultGroup, user);
148 authenticate(user.getLogin(), "group1");
150 checkGroupMembership(user, group1);
154 public void authenticate_new_user_sets_onboarded_flag_to_false_when_onboarding_setting_is_set_to_true() {
155 organizationFlags.setEnabled(true);
156 settings.setProperty(ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS, true);
158 underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER, Source.realm(Method.BASIC, IDENTITY_PROVIDER.getName()));
160 assertThat(db.users().selectUserByLogin(USER_LOGIN).get().isOnboarded()).isFalse();
164 public void authenticate_new_user_sets_onboarded_flag_to_true_when_onboarding_setting_is_set_to_false() {
165 organizationFlags.setEnabled(true);
166 settings.setProperty(ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS, false);
168 underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER, Source.realm(Method.BASIC, IDENTITY_PROVIDER.getName()));
170 assertThat(db.users().selectUserByLogin(USER_LOGIN).get().isOnboarded()).isTrue();
174 public void authenticate_existing_user() throws Exception {
175 db.users().insertUser(newUserDto()
176 .setLogin(USER_LOGIN)
179 .setEmail("Old email")
180 .setExternalIdentity("old identity")
181 .setExternalIdentityProvider("old provide"));
183 underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER, Source.local(Method.BASIC));
185 UserDto userDto = db.users().selectUserByLogin(USER_LOGIN).get();
186 assertThat(userDto.isActive()).isTrue();
187 assertThat(userDto.getName()).isEqualTo("John");
188 assertThat(userDto.getEmail()).isEqualTo("john@email.com");
189 assertThat(userDto.getExternalIdentity()).isEqualTo("johndoo");
190 assertThat(userDto.getExternalIdentityProvider()).isEqualTo("github");
191 assertThat(userDto.isRoot()).isFalse();
195 public void authenticate_existing_disabled_user() throws Exception {
196 organizationFlags.setEnabled(true);
197 db.users().insertUser(newUserDto()
198 .setLogin(USER_LOGIN)
201 .setEmail("Old email")
202 .setExternalIdentity("old identity")
203 .setExternalIdentityProvider("old provide"));
205 underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER, Source.local(Method.BASIC_TOKEN));
207 UserDto userDto = db.users().selectUserByLogin(USER_LOGIN).get();
208 assertThat(userDto.isActive()).isTrue();
209 assertThat(userDto.getName()).isEqualTo("John");
210 assertThat(userDto.getEmail()).isEqualTo("john@email.com");
211 assertThat(userDto.getExternalIdentity()).isEqualTo("johndoo");
212 assertThat(userDto.getExternalIdentityProvider()).isEqualTo("github");
213 assertThat(userDto.isRoot()).isFalse();
217 public void authenticate_existing_user_and_add_new_groups() throws Exception {
218 organizationFlags.setEnabled(true);
219 UserDto user = db.users().insertUser(newUserDto()
220 .setLogin(USER_LOGIN)
223 GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
224 GroupDto group2 = db.users().insertGroup(db.getDefaultOrganization(), "group2");
226 authenticate(USER_LOGIN, "group1", "group2", "group3");
228 checkGroupMembership(user, group1, group2);
232 public void authenticate_existing_user_and_remove_groups() throws Exception {
233 organizationFlags.setEnabled(true);
234 UserDto user = db.users().insertUser(newUserDto()
235 .setLogin(USER_LOGIN)
238 GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
239 GroupDto group2 = db.users().insertGroup(db.getDefaultOrganization(), "group2");
240 db.users().insertMember(group1, user);
241 db.users().insertMember(group2, user);
243 authenticate(USER_LOGIN, "group1");
245 checkGroupMembership(user, group1);
249 public void authenticate_existing_user_and_remove_all_groups_expect_default_when_organizations_are_disabled() throws Exception {
250 organizationFlags.setEnabled(false);
251 UserDto user = db.users().insertUser();
252 GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
253 GroupDto group2 = db.users().insertGroup(db.getDefaultOrganization(), "group2");
254 GroupDto defaultGroup = insertDefaultGroup();
255 db.users().insertMember(group1, user);
256 db.users().insertMember(group2, user);
257 db.users().insertMember(defaultGroup, user);
259 authenticate(user.getLogin());
261 checkGroupMembership(user, defaultGroup);
265 public void does_not_force_default_group_when_authenticating_existing_user_when_organizations_are_enabled() throws Exception {
266 organizationFlags.setEnabled(true);
267 UserDto user = db.users().insertUser();
268 GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
269 GroupDto defaultGroup = insertDefaultGroup();
270 db.users().insertMember(group1, user);
271 db.users().insertMember(defaultGroup, user);
273 authenticate(user.getLogin(), "group1");
275 checkGroupMembership(user, group1);
279 public void ignore_groups_on_non_default_organizations() throws Exception {
280 organizationFlags.setEnabled(true);
281 OrganizationDto org = db.organizations().insert();
282 UserDto user = db.users().insertUser(newUserDto()
283 .setLogin(USER_LOGIN)
286 String groupName = "a-group";
287 GroupDto groupInDefaultOrg = db.users().insertGroup(db.getDefaultOrganization(), groupName);
288 GroupDto groupInOrg = db.users().insertGroup(org, groupName);
290 // adding a group with the same name than in non-default organization
291 underTest.authenticate(UserIdentity.builder()
292 .setProviderLogin("johndoo")
293 .setLogin(user.getLogin())
294 .setName(user.getName())
295 .setGroups(newHashSet(groupName))
296 .build(), IDENTITY_PROVIDER, Source.sso());
298 checkGroupMembership(user, groupInDefaultOrg);
302 public void fail_to_authenticate_new_user_when_allow_users_to_signup_is_false() throws Exception {
303 TestIdentityProvider identityProvider = new TestIdentityProvider()
307 .setAllowsUsersToSignUp(false);
308 Source source = Source.realm(Method.FORM, identityProvider.getName());
310 thrown.expect(authenticationException().from(source).withLogin(USER_IDENTITY.getLogin()).andPublicMessage("'github' users are not allowed to sign up"));
311 thrown.expectMessage("User signup disabled for provider 'github'");
312 underTest.authenticate(USER_IDENTITY, identityProvider, source);
316 public void fail_to_authenticate_new_user_when_email_already_exists() throws Exception {
317 db.users().insertUser(newUserDto()
318 .setLogin("Existing user with same email")
320 .setEmail("john@email.com"));
321 Source source = Source.realm(Method.FORM, IDENTITY_PROVIDER.getName());
323 thrown.expect(authenticationException().from(source)
324 .withLogin(USER_IDENTITY.getLogin())
325 .andPublicMessage("You can't sign up because email 'john@email.com' is already used by an existing user. " +
326 "This means that you probably already registered with another account."));
327 thrown.expectMessage("Email 'john@email.com' is already used");
328 underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER, source);
331 private void authenticate(String login, String... groups) {
332 underTest.authenticate(UserIdentity.builder()
333 .setProviderLogin("johndoo")
337 .setGroups(stream(groups).collect(MoreCollectors.toSet()))
338 .build(), IDENTITY_PROVIDER, Source.sso());
341 private void checkGroupMembership(UserDto user, GroupDto... expectedGroups) {
342 assertThat(db.users().selectGroupIdsOfUser(user)).containsOnly(stream(expectedGroups).map(GroupDto::getId).collect(Collectors.toList()).toArray(new Integer[] {}));
345 private GroupDto insertDefaultGroup() {
346 return db.users().insertDefaultGroup(db.getDefaultOrganization(), "sonar-users");