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.server.authentication.UserIdentity;
28 import org.sonar.api.utils.System2;
29 import org.sonar.api.utils.internal.AlwaysIncreasingSystem2;
30 import org.sonar.core.util.stream.MoreCollectors;
31 import org.sonar.db.DbTester;
32 import org.sonar.db.organization.OrganizationDto;
33 import org.sonar.db.user.GroupDto;
34 import org.sonar.db.user.UserDto;
35 import org.sonar.server.organization.DefaultOrganizationProvider;
36 import org.sonar.server.organization.OrganizationCreation;
37 import org.sonar.server.organization.TestDefaultOrganizationProvider;
38 import org.sonar.server.organization.TestOrganizationFlags;
39 import org.sonar.server.user.NewUserNotifier;
40 import org.sonar.server.user.UserUpdater;
41 import org.sonar.server.user.index.UserIndexer;
42 import org.sonar.server.usergroups.DefaultGroupFinder;
44 import static com.google.common.collect.Sets.newHashSet;
45 import static java.util.Arrays.stream;
46 import static org.assertj.core.api.Assertions.assertThat;
47 import static org.mockito.Mockito.mock;
48 import static org.sonar.db.user.UserTesting.newUserDto;
49 import static org.sonar.server.authentication.event.AuthenticationEvent.Method;
50 import static org.sonar.server.authentication.event.AuthenticationEvent.Source;
51 import static org.sonar.server.authentication.event.AuthenticationExceptionMatcher.authenticationException;
53 public class UserIdentityAuthenticatorTest {
55 private static String USER_LOGIN = "github-johndoo";
57 private static UserIdentity USER_IDENTITY = UserIdentity.builder()
58 .setProviderLogin("johndoo")
61 .setEmail("john@email.com")
64 private static TestIdentityProvider IDENTITY_PROVIDER = new TestIdentityProvider()
66 .setName("name of github")
68 .setAllowsUsersToSignUp(true);
71 public ExpectedException thrown = ExpectedException.none();
74 public DbTester db = DbTester.create(new AlwaysIncreasingSystem2());
76 private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
77 private OrganizationCreation organizationCreation = mock(OrganizationCreation.class);
78 private TestOrganizationFlags organizationFlags = TestOrganizationFlags.standalone();
80 private UserUpdater userUpdater = new UserUpdater(
81 mock(NewUserNotifier.class),
83 mock(UserIndexer.class),
86 defaultOrganizationProvider,
88 new DefaultGroupFinder(db.getDbClient()));
89 private UserIdentityAuthenticator underTest = new UserIdentityAuthenticator(db.getDbClient(), userUpdater, defaultOrganizationProvider, organizationFlags,
90 new DefaultGroupFinder(db.getDbClient()));
93 public void authenticate_new_user() throws Exception {
94 organizationFlags.setEnabled(true);
95 underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER, Source.realm(Method.BASIC, IDENTITY_PROVIDER.getName()));
97 UserDto user = db.users().selectUserByLogin(USER_LOGIN).get();
98 assertThat(user).isNotNull();
99 assertThat(user.isActive()).isTrue();
100 assertThat(user.getName()).isEqualTo("John");
101 assertThat(user.getEmail()).isEqualTo("john@email.com");
102 assertThat(user.getExternalIdentity()).isEqualTo("johndoo");
103 assertThat(user.getExternalIdentityProvider()).isEqualTo("github");
104 assertThat(user.isRoot()).isFalse();
106 checkGroupMembership(user);
110 public void authenticate_new_user_with_groups() throws Exception {
111 organizationFlags.setEnabled(true);
112 GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
113 GroupDto group2 = db.users().insertGroup(db.getDefaultOrganization(), "group2");
115 authenticate(USER_LOGIN, "group1", "group2", "group3");
117 Optional<UserDto> user = db.users().selectUserByLogin(USER_LOGIN);
118 checkGroupMembership(user.get(), group1, group2);
122 public void authenticate_new_user_and_force_default_group_when_organizations_are_disabled() throws Exception {
123 organizationFlags.setEnabled(false);
124 UserDto user = db.users().insertUser();
125 GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
126 GroupDto defaultGroup = insertDefaultGroup();
127 db.users().insertMember(group1, user);
128 db.users().insertMember(defaultGroup, user);
130 authenticate(user.getLogin(), "group1");
132 checkGroupMembership(user, group1, defaultGroup);
136 public void does_not_force_default_group_when_authenticating_new_user_if_organizations_are_enabled() throws Exception {
137 organizationFlags.setEnabled(true);
138 UserDto user = db.users().insertUser();
139 GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
140 GroupDto defaultGroup = insertDefaultGroup();
141 db.users().insertMember(group1, user);
142 db.users().insertMember(defaultGroup, user);
144 authenticate(user.getLogin(), "group1");
146 checkGroupMembership(user, group1);
150 public void authenticate_existing_user() throws Exception {
151 db.users().insertUser(newUserDto()
152 .setLogin(USER_LOGIN)
155 .setEmail("Old email")
156 .setExternalIdentity("old identity")
157 .setExternalIdentityProvider("old provide"));
159 underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER, Source.local(Method.BASIC));
161 UserDto userDto = db.users().selectUserByLogin(USER_LOGIN).get();
162 assertThat(userDto.isActive()).isTrue();
163 assertThat(userDto.getName()).isEqualTo("John");
164 assertThat(userDto.getEmail()).isEqualTo("john@email.com");
165 assertThat(userDto.getExternalIdentity()).isEqualTo("johndoo");
166 assertThat(userDto.getExternalIdentityProvider()).isEqualTo("github");
167 assertThat(userDto.isRoot()).isFalse();
171 public void authenticate_existing_disabled_user() throws Exception {
172 organizationFlags.setEnabled(true);
173 db.users().insertUser(newUserDto()
174 .setLogin(USER_LOGIN)
177 .setEmail("Old email")
178 .setExternalIdentity("old identity")
179 .setExternalIdentityProvider("old provide"));
181 underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER, Source.local(Method.BASIC_TOKEN));
183 UserDto userDto = db.users().selectUserByLogin(USER_LOGIN).get();
184 assertThat(userDto.isActive()).isTrue();
185 assertThat(userDto.getName()).isEqualTo("John");
186 assertThat(userDto.getEmail()).isEqualTo("john@email.com");
187 assertThat(userDto.getExternalIdentity()).isEqualTo("johndoo");
188 assertThat(userDto.getExternalIdentityProvider()).isEqualTo("github");
189 assertThat(userDto.isRoot()).isFalse();
193 public void authenticate_existing_user_and_add_new_groups() throws Exception {
194 organizationFlags.setEnabled(true);
195 UserDto user = db.users().insertUser(newUserDto()
196 .setLogin(USER_LOGIN)
199 GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
200 GroupDto group2 = db.users().insertGroup(db.getDefaultOrganization(), "group2");
202 authenticate(USER_LOGIN, "group1", "group2", "group3");
204 checkGroupMembership(user, group1, group2);
208 public void authenticate_existing_user_and_remove_groups() throws Exception {
209 organizationFlags.setEnabled(true);
210 UserDto user = db.users().insertUser(newUserDto()
211 .setLogin(USER_LOGIN)
214 GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
215 GroupDto group2 = db.users().insertGroup(db.getDefaultOrganization(), "group2");
216 db.users().insertMember(group1, user);
217 db.users().insertMember(group2, user);
219 authenticate(USER_LOGIN, "group1");
221 checkGroupMembership(user, group1);
225 public void authenticate_existing_user_and_remove_all_groups_expect_default_when_organizations_are_disabled() throws Exception {
226 organizationFlags.setEnabled(false);
227 UserDto user = db.users().insertUser();
228 GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
229 GroupDto group2 = db.users().insertGroup(db.getDefaultOrganization(), "group2");
230 GroupDto defaultGroup = insertDefaultGroup();
231 db.users().insertMember(group1, user);
232 db.users().insertMember(group2, user);
233 db.users().insertMember(defaultGroup, user);
235 authenticate(user.getLogin());
237 checkGroupMembership(user, defaultGroup);
241 public void does_not_force_default_group_when_authenticating_existing_user_when_organizations_are_enabled() throws Exception {
242 organizationFlags.setEnabled(true);
243 UserDto user = db.users().insertUser();
244 GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
245 GroupDto defaultGroup = insertDefaultGroup();
246 db.users().insertMember(group1, user);
247 db.users().insertMember(defaultGroup, user);
249 authenticate(user.getLogin(), "group1");
251 checkGroupMembership(user, group1);
255 public void ignore_groups_on_non_default_organizations() throws Exception {
256 organizationFlags.setEnabled(true);
257 OrganizationDto org = db.organizations().insert();
258 UserDto user = db.users().insertUser(newUserDto()
259 .setLogin(USER_LOGIN)
262 String groupName = "a-group";
263 GroupDto groupInDefaultOrg = db.users().insertGroup(db.getDefaultOrganization(), groupName);
264 GroupDto groupInOrg = db.users().insertGroup(org, groupName);
266 // adding a group with the same name than in non-default organization
267 underTest.authenticate(UserIdentity.builder()
268 .setProviderLogin("johndoo")
269 .setLogin(user.getLogin())
270 .setName(user.getName())
271 .setGroups(newHashSet(groupName))
272 .build(), IDENTITY_PROVIDER, Source.sso());
274 checkGroupMembership(user, groupInDefaultOrg);
278 public void fail_to_authenticate_new_user_when_allow_users_to_signup_is_false() throws Exception {
279 TestIdentityProvider identityProvider = new TestIdentityProvider()
283 .setAllowsUsersToSignUp(false);
284 Source source = Source.realm(Method.FORM, identityProvider.getName());
286 thrown.expect(authenticationException().from(source).withLogin(USER_IDENTITY.getLogin()).andPublicMessage("'github' users are not allowed to sign up"));
287 thrown.expectMessage("User signup disabled for provider 'github'");
288 underTest.authenticate(USER_IDENTITY, identityProvider, source);
292 public void fail_to_authenticate_new_user_when_email_already_exists() throws Exception {
293 db.users().insertUser(newUserDto()
294 .setLogin("Existing user with same email")
296 .setEmail("john@email.com"));
297 Source source = Source.realm(Method.FORM, IDENTITY_PROVIDER.getName());
299 thrown.expect(authenticationException().from(source)
300 .withLogin(USER_IDENTITY.getLogin())
301 .andPublicMessage("You can't sign up because email 'john@email.com' is already used by an existing user. " +
302 "This means that you probably already registered with another account."));
303 thrown.expectMessage("Email 'john@email.com' is already used");
304 underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER, source);
307 private void authenticate(String login, String... groups) {
308 underTest.authenticate(UserIdentity.builder()
309 .setProviderLogin("johndoo")
313 .setGroups(stream(groups).collect(MoreCollectors.toSet()))
314 .build(), IDENTITY_PROVIDER, Source.sso());
317 private void checkGroupMembership(UserDto user, GroupDto... expectedGroups) {
318 assertThat(db.users().selectGroupIdsOfUser(user)).containsOnly(stream(expectedGroups).map(GroupDto::getId).collect(Collectors.toList()).toArray(new Integer[] {}));
321 private GroupDto insertDefaultGroup() {
322 return db.users().insertDefaultGroup(db.getDefaultOrganization(), "sonar-users");