3 * Copyright (C) 2009-2016 SonarSource SA
4 * mailto:contact 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 static com.google.common.collect.Sets.newHashSet;
23 import static java.util.Collections.singletonList;
24 import static org.assertj.core.api.Assertions.assertThat;
25 import static org.mockito.Mockito.mock;
26 import static org.mockito.Mockito.verify;
27 import static org.mockito.Mockito.when;
29 import java.util.Collections;
30 import java.util.HashSet;
32 import javax.servlet.http.HttpServletRequest;
33 import javax.servlet.http.HttpServletResponse;
34 import javax.servlet.http.HttpSession;
35 import org.junit.Before;
36 import org.junit.Rule;
37 import org.junit.Test;
38 import org.junit.rules.ExpectedException;
39 import org.sonar.api.config.Settings;
40 import org.sonar.api.server.authentication.UnauthorizedException;
41 import org.sonar.api.server.authentication.UserIdentity;
42 import org.sonar.api.utils.System2;
43 import org.sonar.db.DbClient;
44 import org.sonar.db.DbSession;
45 import org.sonar.db.DbTester;
46 import org.sonar.db.user.GroupDao;
47 import org.sonar.db.user.GroupDto;
48 import org.sonar.db.user.UserDao;
49 import org.sonar.db.user.UserDto;
50 import org.sonar.db.user.UserGroupDto;
51 import org.sonar.db.user.UserTesting;
52 import org.sonar.server.user.NewUserNotifier;
53 import org.sonar.server.user.UserUpdater;
54 import org.sonar.server.user.index.UserIndexer;
56 public class UserIdentityAuthenticatorTest {
58 static String USER_LOGIN = "github-johndoo";
60 static String DEFAULT_GROUP = "default";
62 static UserIdentity USER_IDENTITY = UserIdentity.builder()
63 .setProviderLogin("johndoo")
66 .setEmail("john@email.com")
69 static TestIdentityProvider IDENTITY_PROVIDER = new TestIdentityProvider()
72 .setAllowsUsersToSignUp(true);
75 public ExpectedException thrown = ExpectedException.none();
77 System2 system2 = mock(System2.class);
80 public DbTester dbTester = DbTester.create(system2);
82 DbClient dbClient = dbTester.getDbClient();
83 DbSession dbSession = dbTester.getSession();
84 UserDao userDao = dbClient.userDao();
85 GroupDao groupDao = dbClient.groupDao();
86 Settings settings = new Settings();
87 JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class);
89 HttpServletRequest request = mock(HttpServletRequest.class);
90 HttpServletResponse response = mock(HttpServletResponse.class);
91 HttpSession httpSession = mock(HttpSession.class);
93 UserUpdater userUpdater = new UserUpdater(
94 mock(NewUserNotifier.class),
97 mock(UserIndexer.class),
101 UserIdentityAuthenticator underTest = new UserIdentityAuthenticator(dbClient, userUpdater, jwtHttpHandler);
104 public void setUp() throws Exception {
105 settings.setProperty("sonar.defaultGroup", DEFAULT_GROUP);
106 addGroup(DEFAULT_GROUP);
107 when(request.getSession()).thenReturn(httpSession);
111 public void authenticate_new_user() throws Exception {
112 underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER, request, response);
115 UserDto userDto = userDao.selectByLogin(dbSession, USER_LOGIN);
116 assertThat(userDto).isNotNull();
117 assertThat(userDto.isActive()).isTrue();
118 assertThat(userDto.getName()).isEqualTo("John");
119 assertThat(userDto.getEmail()).isEqualTo("john@email.com");
120 assertThat(userDto.getExternalIdentity()).isEqualTo("johndoo");
121 assertThat(userDto.getExternalIdentityProvider()).isEqualTo("github");
123 verifyUserGroups(USER_LOGIN, DEFAULT_GROUP);
127 public void authenticate_new_user_with_groups() throws Exception {
131 underTest.authenticate(UserIdentity.builder()
132 .setProviderLogin("johndoo")
133 .setLogin(USER_LOGIN)
135 // group3 doesn't exist in db, it will be ignored
136 .setGroups(newHashSet("group1", "group2", "group3"))
137 .build(), IDENTITY_PROVIDER, request, response);
140 UserDto userDto = userDao.selectByLogin(dbSession, USER_LOGIN);
141 assertThat(userDto).isNotNull();
143 verifyUserGroups(USER_LOGIN, "group1", "group2");
147 public void authenticate_existing_user() throws Exception {
148 userDao.insert(dbSession, new UserDto()
149 .setLogin(USER_LOGIN)
152 .setEmail("Old email")
153 .setExternalIdentity("old identity")
154 .setExternalIdentityProvider("old provide")
158 underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER, request, response);
161 UserDto userDto = userDao.selectByLogin(dbSession, USER_LOGIN);
162 assertThat(userDto).isNotNull();
163 assertThat(userDto.isActive()).isTrue();
164 assertThat(userDto.getName()).isEqualTo("John");
165 assertThat(userDto.getEmail()).isEqualTo("john@email.com");
166 assertThat(userDto.getExternalIdentity()).isEqualTo("johndoo");
167 assertThat(userDto.getExternalIdentityProvider()).isEqualTo("github");
171 public void authenticate_existing_disabled_user() throws Exception {
172 userDao.insert(dbSession, new UserDto()
173 .setLogin(USER_LOGIN)
176 .setEmail("Old email")
177 .setExternalIdentity("old identity")
178 .setExternalIdentityProvider("old provide")
182 underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER, request, response);
185 UserDto userDto = userDao.selectByLogin(dbSession, USER_LOGIN);
186 assertThat(userDto).isNotNull();
187 assertThat(userDto.isActive()).isTrue();
188 assertThat(userDto.getName()).isEqualTo("John");
189 assertThat(userDto.getEmail()).isEqualTo("john@email.com");
190 assertThat(userDto.getExternalIdentity()).isEqualTo("johndoo");
191 assertThat(userDto.getExternalIdentityProvider()).isEqualTo("github");
195 public void authenticate_existing_user_and_add_new_groups() throws Exception {
196 userDao.insert(dbSession, new UserDto()
197 .setLogin(USER_LOGIN)
205 underTest.authenticate(UserIdentity.builder()
206 .setProviderLogin("johndoo")
207 .setLogin(USER_LOGIN)
209 // group3 doesn't exist in db, it will be ignored
210 .setGroups(newHashSet("group1", "group2", "group3"))
211 .build(), IDENTITY_PROVIDER, request, response);
214 Set<String> userGroups = new HashSet<>(dbClient.groupMembershipDao().selectGroupsByLogins(dbSession, singletonList(USER_LOGIN)).get(USER_LOGIN));
215 assertThat(userGroups).containsOnly("group1", "group2");
219 public void authenticate_existing_user_and_remove_groups() throws Exception {
220 UserDto user = new UserDto()
221 .setLogin(USER_LOGIN)
224 userDao.insert(dbSession, user);
226 GroupDto group1 = addGroup("group1");
227 GroupDto group2 = addGroup("group2");
228 dbClient.userGroupDao().insert(dbSession, new UserGroupDto().setUserId(user.getId()).setGroupId(group1.getId()));
229 dbClient.userGroupDao().insert(dbSession, new UserGroupDto().setUserId(user.getId()).setGroupId(group2.getId()));
232 Set<String> userGroups = new HashSet<>(dbClient.groupMembershipDao().selectGroupsByLogins(dbSession, singletonList(USER_LOGIN)).get(USER_LOGIN));
233 assertThat(userGroups).containsOnly("group1", "group2");
235 underTest.authenticate(UserIdentity.builder()
236 .setProviderLogin("johndoo")
237 .setLogin(USER_LOGIN)
239 // Only group1 is returned by the id provider => group2 will be removed
240 .setGroups(newHashSet("group1"))
241 .build(), IDENTITY_PROVIDER, request, response);
244 verifyUserGroups(USER_LOGIN, "group1");
248 public void authenticate_existing_user_and_remove_all_groups() throws Exception {
249 UserDto user = new UserDto()
250 .setLogin(USER_LOGIN)
253 userDao.insert(dbSession, user);
255 GroupDto group1 = addGroup("group1");
256 GroupDto group2 = addGroup("group2");
257 dbClient.userGroupDao().insert(dbSession, new UserGroupDto().setUserId(user.getId()).setGroupId(group1.getId()));
258 dbClient.userGroupDao().insert(dbSession, new UserGroupDto().setUserId(user.getId()).setGroupId(group2.getId()));
261 Set<String> userGroups = new HashSet<>(dbClient.groupMembershipDao().selectGroupsByLogins(dbSession, singletonList(USER_LOGIN)).get(USER_LOGIN));
262 assertThat(userGroups).containsOnly("group1", "group2");
264 underTest.authenticate(UserIdentity.builder()
265 .setProviderLogin("johndoo")
266 .setLogin(USER_LOGIN)
268 // No group => group1 and group2 will be removed
269 .setGroups(Collections.<String>emptySet())
270 .build(), IDENTITY_PROVIDER, request, response);
273 verifyNoUserGroups(USER_LOGIN);
277 public void update_session_for_rails() throws Exception {
278 UserDto userDto = UserTesting.newUserDto().setLogin(USER_LOGIN);
279 userDao.insert(dbSession, userDto);
282 underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER, request, response);
284 verify(httpSession).setAttribute("user_id", userDto.getId());
288 public void create_jwt_token() throws Exception {
289 UserDto userDto = UserTesting.newUserDto().setLogin(USER_LOGIN);
290 userDao.insert(dbSession, userDto);
293 underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER, request, response);
295 verify(httpSession).setAttribute("user_id", userDto.getId());
296 verify(jwtHttpHandler).generateToken(USER_LOGIN, response);
300 public void fail_to_authenticate_new_user_when_allow_users_to_signup_is_false() throws Exception {
301 TestIdentityProvider identityProvider = new TestIdentityProvider()
305 .setAllowsUsersToSignUp(false);
307 thrown.expect(UnauthorizedException.class);
308 thrown.expectMessage("'github' users are not allowed to sign up");
309 underTest.authenticate(USER_IDENTITY, identityProvider, request, response);
313 public void fail_to_authenticate_new_user_when_email_already_exists() throws Exception {
314 UserDto userDto = UserTesting.newUserDto()
315 .setLogin("Existing user with same email")
317 .setEmail("john@email.com");
318 userDao.insert(dbSession, userDto);
321 thrown.expect(UnauthorizedException.class);
322 thrown.expectMessage("You can't sign up because email 'john@email.com' is already used by an existing user. " +
323 "This means that you probably already registered with another account.");
324 underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER, request, response);
327 private void verifyUserGroups(String userLogin, String... groups) {
328 Set<String> userGroups = new HashSet<>(dbClient.groupMembershipDao().selectGroupsByLogins(dbSession, singletonList(USER_LOGIN)).get(userLogin));
329 assertThat(userGroups).containsOnly(groups);
332 private void verifyNoUserGroups(String userLogin) {
333 Set<String> userGroups = new HashSet<>(dbClient.groupMembershipDao().selectGroupsByLogins(dbSession, singletonList(USER_LOGIN)).get(userLogin));
334 assertThat(userGroups).isEmpty();
337 private GroupDto addGroup(String name) {
338 GroupDto group = new GroupDto().setName(name);
339 groupDao.insert(dbSession, group);