Browse Source

SONAR-10652 Correctly fail when authentication and several emails exists

tags/7.5
Julien Lancelot 6 years ago
parent
commit
b7976ff3de
27 changed files with 464 additions and 833 deletions
  1. 8
    9
      server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java
  2. 1
    2
      server/sonar-db-dao/src/main/java/org/sonar/db/user/UserMapper.java
  3. 5
    4
      server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDaoTest.java
  4. 2
    2
      server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/GroupTester.java
  5. 5
    0
      server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/LoginPage.java
  6. 1
    1
      server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationModule.java
  7. 4
    4
      server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticatorsImpl.java
  8. 5
    5
      server/sonar-server/src/main/java/org/sonar/server/authentication/HttpHeadersAuthenticator.java
  9. 22
    9
      server/sonar-server/src/main/java/org/sonar/server/authentication/UserIdentityAuthenticatorImpl.java
  10. 7
    7
      server/sonar-server/src/test/java/org/sonar/server/authentication/AuthenticatorsImplTest.java
  11. 2
    2
      server/sonar-server/src/test/java/org/sonar/server/authentication/HttpHeadersAuthenticatorTest.java
  12. 23
    4
      server/sonar-server/src/test/java/org/sonar/server/authentication/UserIdentityAuthenticatorImplTest.java
  13. 2
    2
      tests/src/test/java/org/sonarqube/tests/Category5Suite.java
  14. 46
    14
      tests/src/test/java/org/sonarqube/tests/user/BaseIdentityProviderTest.java
  15. 191
    0
      tests/src/test/java/org/sonarqube/tests/user/HttpHeadersAuthenticationTest.java
  16. 39
    11
      tests/src/test/java/org/sonarqube/tests/user/OAuth2IdentityProviderTest.java
  17. 101
    175
      tests/src/test/java/org/sonarqube/tests/user/RealmAuthenticationTest.java
  18. 0
    166
      tests/src/test/java/org/sonarqube/tests/user/SsoAuthenticationTest.java
  19. 0
    44
      tests/src/test/resources/user/BaseIdentityProviderTest/display_message_in_ui_but_not_in_log_when_unauthorized_exception.html
  20. 0
    39
      tests/src/test/resources/user/BaseIdentityProviderTest/display_unauthorized_page_when_authentication_failed.html
  21. 0
    39
      tests/src/test/resources/user/BaseIdentityProviderTest/fail_to_authenticate_when_not_allowed_to_sign_up.html
  22. 0
    44
      tests/src/test/resources/user/BaseIdentityProviderTest/fail_when_email_already_exists.html
  23. 0
    64
      tests/src/test/resources/user/ExternalAuthenticationTest/external-user-details.html
  24. 0
    64
      tests/src/test/resources/user/ExternalAuthenticationTest/external-user-details2.html
  25. 0
    44
      tests/src/test/resources/user/OAuth2IdentityProviderTest/display_message_in_ui_but_not_in_log_when_unauthorized_exception.html
  26. 0
    39
      tests/src/test/resources/user/OAuth2IdentityProviderTest/display_unauthorized_page_when_authentication_failed.html
  27. 0
    39
      tests/src/test/resources/user/OAuth2IdentityProviderTest/fail_to_authenticate_when_not_allowed_to_sign_up.html

+ 8
- 9
server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java View File

@@ -22,7 +22,6 @@ package org.sonar.db.user;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
@@ -38,15 +37,16 @@ import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.organization.OrganizationDto;

import static java.util.Locale.ENGLISH;
import static org.sonar.db.DatabaseUtils.executeLargeInputs;
import static org.sonar.db.DatabaseUtils.executeLargeInputsWithoutOutput;
import static org.sonar.db.user.UserDto.SCM_ACCOUNTS_SEPARATOR;

public class UserDao implements Dao {

private final System2 system2;
private final UuidFactory uuidFactory;


public UserDao(System2 system2, UuidFactory uuidFactory) {
this.system2 = system2;
this.uuidFactory = uuidFactory;
@@ -154,19 +154,18 @@ public class UserDao implements Dao {

public List<UserDto> selectByScmAccountOrLoginOrEmail(DbSession session, String scmAccountOrLoginOrEmail) {
String like = new StringBuilder().append("%")
.append(UserDto.SCM_ACCOUNTS_SEPARATOR).append(scmAccountOrLoginOrEmail)
.append(UserDto.SCM_ACCOUNTS_SEPARATOR).append("%").toString();
.append(SCM_ACCOUNTS_SEPARATOR).append(scmAccountOrLoginOrEmail)
.append(SCM_ACCOUNTS_SEPARATOR).append("%").toString();
return mapper(session).selectNullableByScmAccountOrLoginOrEmail(scmAccountOrLoginOrEmail, like);
}

/**
* Search for an active user with the given email exits in database
* Search for an active user with the given emailCaseInsensitive exits in database
*
* Please note that email is case insensitive, result for searching 'mail@email.com' or 'Mail@Email.com' will be the same
* Select is case insensitive. Result for searching 'mail@emailCaseInsensitive.com' or 'Mail@Email.com' is the same
*/
@CheckForNull
public UserDto selectByEmail(DbSession dbSession, String email) {
return mapper(dbSession).selectByEmail(email.toLowerCase(Locale.ENGLISH));
public List<UserDto> selectByEmail(DbSession dbSession, String emailCaseInsensitive) {
return mapper(dbSession).selectByEmail(emailCaseInsensitive.toLowerCase(ENGLISH));
}

@CheckForNull

+ 1
- 2
server/sonar-db-dao/src/main/java/org/sonar/db/user/UserMapper.java View File

@@ -57,8 +57,7 @@ public interface UserMapper {

List<UserDto> selectByIds(@Param("ids") List<Integer> ids);

@CheckForNull
UserDto selectByEmail(String email);
List<UserDto> selectByEmail(String email);

@CheckForNull
UserDto selectByExternalIdAndIdentityProvider(@Param("externalId") String externalId, @Param("externalIdentityProvider") String externalExternalIdentityProvider);

+ 5
- 4
server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDaoTest.java View File

@@ -585,12 +585,13 @@ public class UserDaoTest {

@Test
public void select_by_email() {
UserDto activeUser = db.users().insertUser();
UserDto activeUser1 = db.users().insertUser(u -> u.setEmail("user1@email.com"));
UserDto activeUser2 = db.users().insertUser(u -> u.setEmail("user1@email.com"));
UserDto disableUser = db.users().insertUser(u -> u.setActive(false));

assertThat(underTest.selectByEmail(session, activeUser.getEmail())).isNotNull();
assertThat(underTest.selectByEmail(session, disableUser.getEmail())).isNull();
assertThat(underTest.selectByEmail(session, "unknown")).isNull();
assertThat(underTest.selectByEmail(session, "user1@email.com")).hasSize(2);
assertThat(underTest.selectByEmail(session, disableUser.getEmail())).isEmpty();
assertThat(underTest.selectByEmail(session, "unknown")).isEmpty();
}

@Test

+ 2
- 2
server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/GroupTester.java View File

@@ -70,11 +70,11 @@ public class GroupTester {
return response.getGroupsList();
}

public GroupTester addMemberToGroups(Organizations.Organization organization, String userLogin, String... groups) {
public GroupTester addMemberToGroups(@Nullable Organizations.Organization organization, String userLogin, String... groups) {
for (String group : groups) {
AddUserRequest request = new AddUserRequest()
.setLogin(userLogin)
.setOrganization(organization.getKey())
.setOrganization(organization != null ? organization.getKey() : null)
.setName(group);
session.wsClient().userGroups().addUser(request);
}

+ 5
- 0
server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/LoginPage.java View File

@@ -45,6 +45,11 @@ public class LoginPage {
return Selenide.page(Navigation.class);
}

public Navigation useBaseAuth() {
Selenide.$(".oauth-providers a").click();
return Selenide.page(Navigation.class);
}

public LoginPage submitWrongCredentials(String login, String password) {
Selenide.$("#login").val(login);
Selenide.$("#password").val(password);

+ 1
- 1
server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationModule.java View File

@@ -51,7 +51,7 @@ public class AuthenticationModule extends Module {
RealmAuthenticator.class,
BasicAuthenticator.class,
ValidateAction.class,
SsoAuthenticator.class,
HttpHeadersAuthenticator.class,
AuthenticatorsImpl.class);
}
}

+ 4
- 4
server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticatorsImpl.java View File

@@ -28,19 +28,19 @@ public class AuthenticatorsImpl implements Authenticators {

private final JwtHttpHandler jwtHttpHandler;
private final BasicAuthenticator basicAuthenticator;
private final SsoAuthenticator ssoAuthenticator;
private final HttpHeadersAuthenticator httpHeadersAuthenticator;

public AuthenticatorsImpl(JwtHttpHandler jwtHttpHandler, BasicAuthenticator basicAuthenticator, SsoAuthenticator ssoAuthenticator) {
public AuthenticatorsImpl(JwtHttpHandler jwtHttpHandler, BasicAuthenticator basicAuthenticator, HttpHeadersAuthenticator httpHeadersAuthenticator) {
this.jwtHttpHandler = jwtHttpHandler;
this.basicAuthenticator = basicAuthenticator;
this.ssoAuthenticator = ssoAuthenticator;
this.httpHeadersAuthenticator = httpHeadersAuthenticator;
}

// Try first to authenticate from SSO, then JWT token, then try from basic http header
@Override
public Optional<UserDto> authenticate(HttpServletRequest request, HttpServletResponse response) {
// SSO authentication should come first in order to update JWT if user from header is not the same is user from JWT
Optional<UserDto> user = ssoAuthenticator.authenticate(request, response);
Optional<UserDto> user = httpHeadersAuthenticator.authenticate(request, response);
if (user.isPresent()) {
return user;
}

server/sonar-server/src/main/java/org/sonar/server/authentication/SsoAuthenticator.java → server/sonar-server/src/main/java/org/sonar/server/authentication/HttpHeadersAuthenticator.java View File

@@ -58,9 +58,9 @@ import static org.sonar.process.ProcessProperties.Property.SONAR_WEB_SSO_NAME_HE
import static org.sonar.process.ProcessProperties.Property.SONAR_WEB_SSO_REFRESH_INTERVAL_IN_MINUTES;
import static org.sonar.server.user.ExternalIdentity.SQ_AUTHORITY;

public class SsoAuthenticator implements Startable {
public class HttpHeadersAuthenticator implements Startable {

private static final Logger LOG = Loggers.get(SsoAuthenticator.class);
private static final Logger LOG = Loggers.get(HttpHeadersAuthenticator.class);

private static final Splitter COMA_SPLITTER = Splitter.on(",").trimResults().omitEmptyStrings();

@@ -82,8 +82,8 @@ public class SsoAuthenticator implements Startable {
private boolean enabled = false;
private Map<String, String> settingsByKey = new HashMap<>();

public SsoAuthenticator(System2 system2, Configuration config, UserIdentityAuthenticator userIdentityAuthenticator,
JwtHttpHandler jwtHttpHandler, AuthenticationEvent authenticationEvent) {
public HttpHeadersAuthenticator(System2 system2, Configuration config, UserIdentityAuthenticator userIdentityAuthenticator,
JwtHttpHandler jwtHttpHandler, AuthenticationEvent authenticationEvent) {
this.system2 = system2;
this.config = config;
this.userIdentityAuthenticator = userIdentityAuthenticator;
@@ -94,7 +94,7 @@ public class SsoAuthenticator implements Startable {
@Override
public void start() {
if (config.getBoolean(SONAR_WEB_SSO_ENABLE.getKey()).orElse(false)) {
LOG.info("SSO Authentication enabled");
LOG.info("HTTP headers authentication enabled");
enabled = true;
SETTINGS.forEach(entry -> settingsByKey.put(entry.getKey(), config.get(entry.getKey()).orElse(entry.getDefaultValue())));
}

+ 22
- 9
server/sonar-server/src/main/java/org/sonar/server/authentication/UserIdentityAuthenticatorImpl.java View File

@@ -23,6 +23,7 @@ import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
@@ -135,7 +136,15 @@ public class UserIdentityAuthenticatorImpl implements UserIdentityAuthenticator
if (email == null) {
return Optional.empty();
}
UserDto existingUser = dbClient.userDao().selectByEmail(dbSession, email);
List<UserDto> existingUsers = dbClient.userDao().selectByEmail(dbSession, email);
if (existingUsers.isEmpty()) {
return Optional.empty();
}
if (existingUsers.size() > 1) {
throw generateExistingEmailError(authenticatorParameters, email);
}

UserDto existingUser = existingUsers.get(0);
if (existingUser == null
|| Objects.equals(existingUser.getLogin(), authenticatorParameters.getUserIdentity().getLogin())
|| (Objects.equals(existingUser.getExternalId(), authenticatorParameters.getUserIdentity().getProviderId())
@@ -151,14 +160,7 @@ public class UserIdentityAuthenticatorImpl implements UserIdentityAuthenticator
case WARN:
throw new EmailAlreadyExistsRedirectionException(email, existingUser, authenticatorParameters.getUserIdentity(), authenticatorParameters.getProvider());
case FORBID:
throw AuthenticationException.newBuilder()
.setSource(authenticatorParameters.getSource())
.setLogin(authenticatorParameters.getUserIdentity().getLogin())
.setMessage(format("Email '%s' is already used", email))
.setPublicMessage(format(
"You can't sign up because email '%s' is already used by an existing user. This means that you probably already registered with another account.",
email))
.build();
throw generateExistingEmailError(authenticatorParameters, email);
default:
throw new IllegalStateException(format("Unknown strategy %s", existingEmailStrategy));
}
@@ -264,4 +266,15 @@ public class UserIdentityAuthenticatorImpl implements UserIdentityAuthenticator
return userDto.map(u -> new UserDto[] {u}).orElse(new UserDto[] {});
}

private static AuthenticationException generateExistingEmailError(UserIdentityAuthenticatorParameters authenticatorParameters, String email) {
return AuthenticationException.newBuilder()
.setSource(authenticatorParameters.getSource())
.setLogin(authenticatorParameters.getUserIdentity().getLogin())
.setMessage(format("Email '%s' is already used", email))
.setPublicMessage(format(
"You can't sign up because email '%s' is already used by an existing user. This means that you probably already registered with another account.",
email))
.build();
}

}

+ 7
- 7
server/sonar-server/src/test/java/org/sonar/server/authentication/AuthenticatorsImplTest.java View File

@@ -40,12 +40,12 @@ public class AuthenticatorsImplTest {
private HttpServletResponse response = mock(HttpServletResponse.class);
private JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class);
private BasicAuthenticator basicAuthenticator = mock(BasicAuthenticator.class);
private SsoAuthenticator ssoAuthenticator = mock(SsoAuthenticator.class);
private Authenticators underTest = new AuthenticatorsImpl(jwtHttpHandler, basicAuthenticator, ssoAuthenticator);
private HttpHeadersAuthenticator httpHeadersAuthenticator = mock(HttpHeadersAuthenticator.class);
private Authenticators underTest = new AuthenticatorsImpl(jwtHttpHandler, basicAuthenticator, httpHeadersAuthenticator);

@Test
public void authenticate_from_jwt_token() {
when(ssoAuthenticator.authenticate(request, response)).thenReturn(Optional.empty());
when(httpHeadersAuthenticator.authenticate(request, response)).thenReturn(Optional.empty());
when(jwtHttpHandler.validateToken(request, response)).thenReturn(Optional.of(user));

assertThat(underTest.authenticate(request, response)).hasValue(user);
@@ -55,7 +55,7 @@ public class AuthenticatorsImplTest {
@Test
public void authenticate_from_basic_header() {
when(basicAuthenticator.authenticate(request)).thenReturn(Optional.of(user));
when(ssoAuthenticator.authenticate(request, response)).thenReturn(Optional.empty());
when(httpHeadersAuthenticator.authenticate(request, response)).thenReturn(Optional.empty());
when(jwtHttpHandler.validateToken(request, response)).thenReturn(Optional.empty());

assertThat(underTest.authenticate(request, response)).hasValue(user);
@@ -67,12 +67,12 @@ public class AuthenticatorsImplTest {

@Test
public void authenticate_from_sso() {
when(ssoAuthenticator.authenticate(request, response)).thenReturn(Optional.of(user));
when(httpHeadersAuthenticator.authenticate(request, response)).thenReturn(Optional.of(user));
when(jwtHttpHandler.validateToken(request, response)).thenReturn(Optional.empty());

assertThat(underTest.authenticate(request, response)).hasValue(user);

verify(ssoAuthenticator).authenticate(request, response);
verify(httpHeadersAuthenticator).authenticate(request, response);
verify(jwtHttpHandler, never()).validateToken(request, response);
verify(response, never()).setStatus(anyInt());
}
@@ -80,7 +80,7 @@ public class AuthenticatorsImplTest {
@Test
public void return_empty_if_not_authenticated() {
when(jwtHttpHandler.validateToken(request, response)).thenReturn(Optional.empty());
when(ssoAuthenticator.authenticate(request, response)).thenReturn(Optional.empty());
when(httpHeadersAuthenticator.authenticate(request, response)).thenReturn(Optional.empty());
when(basicAuthenticator.authenticate(request)).thenReturn(Optional.empty());

assertThat(underTest.authenticate(request, response)).isEmpty();

server/sonar-server/src/test/java/org/sonar/server/authentication/SsoAuthenticatorTest.java → server/sonar-server/src/test/java/org/sonar/server/authentication/HttpHeadersAuthenticatorTest.java View File

@@ -64,7 +64,7 @@ import static org.mockito.Mockito.when;
import static org.sonar.db.user.UserTesting.newUserDto;
import static org.sonar.server.authentication.event.AuthenticationExceptionMatcher.authenticationException;

public class SsoAuthenticatorTest {
public class HttpHeadersAuthenticatorTest {

private MapSettings settings = new MapSettings();

@@ -113,7 +113,7 @@ public class SsoAuthenticatorTest {
private JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class);
private AuthenticationEvent authenticationEvent = mock(AuthenticationEvent.class);

private SsoAuthenticator underTest = new SsoAuthenticator(system2, settings.asConfig(), userIdentityAuthenticator, jwtHttpHandler, authenticationEvent);
private HttpHeadersAuthenticator underTest = new HttpHeadersAuthenticator(system2, settings.asConfig(), userIdentityAuthenticator, jwtHttpHandler, authenticationEvent);

@Before
public void setUp() throws Exception {

+ 23
- 4
server/sonar-server/src/test/java/org/sonar/server/authentication/UserIdentityAuthenticatorImplTest.java View File

@@ -277,10 +277,29 @@ public class UserIdentityAuthenticatorImplTest {

@Test
public void throw_AuthenticationException_when_authenticating_new_user_when_email_already_exists_and_strategy_is_FORBID() {
db.users().insertUser(newUserDto()
.setLogin("Existing user with same email")
.setActive(true)
.setEmail("john@email.com"));
db.users().insertUser(u -> u.setEmail("john@email.com"));
Source source = Source.realm(AuthenticationEvent.Method.FORM, IDENTITY_PROVIDER.getName());

expectedException.expect(authenticationException().from(source)
.withLogin(USER_IDENTITY.getLogin())
.andPublicMessage("You can't sign up because email 'john@email.com' is already used by an existing user. " +
"This means that you probably already registered with another account."));
expectedException.expectMessage("Email 'john@email.com' is already used");

underTest.authenticate(UserIdentityAuthenticatorParameters.builder()
.setUserIdentity(USER_IDENTITY)
.setProvider(IDENTITY_PROVIDER)
.setSource(source)
.setExistingEmailStrategy(FORBID)
.setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
.setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
.build());
}

@Test
public void throw_AuthenticationException_when_authenticating_new_user_and_email_already_exists_multiple_times() {
db.users().insertUser(u -> u.setEmail("john@email.com"));
db.users().insertUser(u -> u.setEmail("john@email.com"));
Source source = Source.realm(AuthenticationEvent.Method.FORM, IDENTITY_PROVIDER.getName());

expectedException.expect(authenticationException().from(source)

+ 2
- 2
tests/src/test/java/org/sonarqube/tests/Category5Suite.java View File

@@ -37,9 +37,9 @@ import org.sonarqube.tests.settings.SettingsTestRestartingOrchestrator;
import org.sonarqube.tests.startup.StartupIndexationTest;
import org.sonarqube.tests.telemetry.TelemetryOptOutTest;
import org.sonarqube.tests.telemetry.TelemetryUploadTest;
import org.sonarqube.tests.user.HttpHeadersAuthenticationTest;
import org.sonarqube.tests.user.OnboardingTest;
import org.sonarqube.tests.user.RealmAuthenticationTest;
import org.sonarqube.tests.user.SsoAuthenticationTest;
import org.sonarqube.tests.user.UserEsResilienceTest;

/**
@@ -61,7 +61,7 @@ import org.sonarqube.tests.user.UserEsResilienceTest;
// update center
UpdateCenterTest.class,
RealmAuthenticationTest.class,
SsoAuthenticationTest.class,
HttpHeadersAuthenticationTest.class,
OnboardingTest.class,
BuiltInQualityProfilesNotificationTest.class,
ActiveRuleEsResilienceTest.class,

+ 46
- 14
tests/src/test/java/org/sonarqube/tests/user/BaseIdentityProviderTest.java View File

@@ -19,14 +19,13 @@
*/
package org.sonarqube.tests.user;

import com.codeborne.selenide.Condition;
import com.google.common.base.Joiner;
import com.sonar.orchestrator.Orchestrator;
import java.io.File;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.apache.commons.io.FileUtils;
import org.junit.After;
import org.junit.ClassRule;
import org.junit.Rule;
@@ -41,10 +40,13 @@ import org.sonarqube.ws.client.users.DeactivateRequest;
import org.sonarqube.ws.client.users.GroupsRequest;
import org.sonarqube.ws.client.users.SearchRequest;

import static com.codeborne.selenide.Selenide.$;
import static java.lang.String.format;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.commons.io.FileUtils.readFileToString;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.tuple;
import static org.sonarqube.ws.UserGroups.Group;
import static util.selenium.Selenese.runSelenese;

public class BaseIdentityProviderTest {

@@ -105,8 +107,11 @@ public class BaseIdentityProviderTest {
// As this property is null, the plugin will throw an exception
tester.settings().setGlobalSettings("sonar.auth.fake-base-id-provider.user", null);

runSelenese(ORCHESTRATOR, "/user/BaseIdentityProviderTest/display_unauthorized_page_when_authentication_failed.html");
tester.openBrowser()
.logIn()
.useBaseAuth();

$("#bd").shouldHave(Condition.text("You're not authorized to access this page. Please contact the administrator"));
assertThat(tester.users().getByLogin(USER_LOGIN)).isNotPresent();
}

@@ -116,21 +121,45 @@ public class BaseIdentityProviderTest {
setUserCreatedByAuthPlugin(USER_LOGIN, USER_PROVIDER_ID, USER_PROVIDER_LOGIN, USER_NAME, USER_EMAIL);
tester.users().generate(u -> u.setLogin("another").setName("Another").setEmail(USER_EMAIL).setPassword("another"));

runSelenese(ORCHESTRATOR, "/user/BaseIdentityProviderTest/fail_when_email_already_exists.html");
tester.openBrowser()
.logIn()
.useBaseAuth();

File logFile = ORCHESTRATOR.getServer().getWebLogs();
assertThat(FileUtils.readFileToString(logFile))
$("#bd").shouldHave(Condition.text(
format("You can't sign up because email '%s' is already used by an existing user. This means that you probably already registered with another account.", USER_EMAIL)));
assertThat(readFileToString(ORCHESTRATOR.getServer().getWebLogs(), UTF_8))
.doesNotContain("You can't sign up because email 'john@email.com' is already used by an existing user. This means that you probably already registered with another account");
}

@Test
public void fail_to_authenticate_user_when_email_already_exists_on_several_users() {
enablePlugin();
setUserCreatedByAuthPlugin(USER_LOGIN, USER_PROVIDER_ID, USER_PROVIDER_LOGIN, USER_NAME, USER_EMAIL);
tester.users().generate(u -> u.setEmail(USER_EMAIL));
tester.users().generate(u -> u.setEmail(USER_EMAIL));

tester.openBrowser()
.logIn()
.useBaseAuth();

$("#bd").shouldHave(Condition.text(
format("You can't sign up because email '%s' is already used by an existing user. This means that you probably already registered with another account.", USER_EMAIL)));
assertThat(tester.users().getByLogin(USER_LOGIN)).isNotPresent();
}

@Test
public void fail_to_authenticate_when_not_allowed_to_sign_up() {
enablePlugin();
setUserCreatedByAuthPlugin(USER_LOGIN, USER_PROVIDER_ID, USER_PROVIDER_LOGIN, USER_NAME, USER_EMAIL);
tester.settings().setGlobalSettings("sonar.auth.fake-base-id-provider.allowsUsersToSignUp", "false");

runSelenese(ORCHESTRATOR, "/user/BaseIdentityProviderTest/fail_to_authenticate_when_not_allowed_to_sign_up.html");
tester.openBrowser()
.logIn()
.useBaseAuth();

$("#bd")
.shouldHave(Condition.text("You're not authorized to access this page. Please contact the administrator."))
.shouldHave(Condition.text(format("Reason: '%s' users are not allowed to sign up", FAKE_PROVIDER_KEY)));
assertThat(tester.users().getByLogin(USER_LOGIN)).isNotPresent();
}

@@ -190,13 +219,16 @@ public class BaseIdentityProviderTest {
setUserCreatedByAuthPlugin(USER_LOGIN, USER_PROVIDER_ID, USER_PROVIDER_LOGIN, USER_NAME, USER_EMAIL);
tester.settings().setGlobalSettings("sonar.auth.fake-base-id-provider.throwUnauthorizedMessage", "true");

runSelenese(ORCHESTRATOR,
"/user/BaseIdentityProviderTest/display_message_in_ui_but_not_in_log_when_unauthorized_exception.html");

File logFile = ORCHESTRATOR.getServer().getWebLogs();
assertThat(FileUtils.readFileToString(logFile)).doesNotContain("A functional error has happened");
assertThat(FileUtils.readFileToString(logFile)).doesNotContain("UnauthorizedException");
tester.openBrowser()
.logIn()
.useBaseAuth();

$("#bd")
.shouldHave(Condition.text("You're not authorized to access this page. Please contact the administrator"))
.shouldHave(Condition.text("Reason: A functional error has happened"));
assertThat(readFileToString(ORCHESTRATOR.getServer().getWebLogs(), UTF_8))
.doesNotContain("A functional error has happened")
.doesNotContain("UnauthorizedException");
assertThat(tester.users().getByLogin(USER_LOGIN)).isNotPresent();
}


+ 191
- 0
tests/src/test/java/org/sonarqube/tests/user/HttpHeadersAuthenticationTest.java View File

@@ -0,0 +1,191 @@
/*
* SonarQube
* Copyright (C) 2009-2018 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonarqube.tests.user;

import com.sonar.orchestrator.Orchestrator;
import java.net.URLEncoder;
import java.util.List;
import javax.annotation.Nullable;
import okhttp3.Response;
import org.apache.commons.io.FileUtils;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.sonarqube.qa.util.Tester;
import org.sonarqube.ws.UserGroups.Group;
import org.sonarqube.ws.Users.CreateWsResponse.User;
import org.sonarqube.ws.Users.SearchWsResponse;
import org.sonarqube.ws.client.users.SearchRequest;

import static java.lang.String.format;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.groups.Tuple.tuple;
import static util.ItUtils.call;
import static util.ItUtils.newOrchestratorBuilder;

/**
* Test authentication using HTTP headers.
* <p>
* It starts its own server as it's using a different authentication system
*/
public class HttpHeadersAuthenticationTest {

private static final String LOGIN_HEADER = "H-Login";
private static final String NAME_HEADER = "H-Name";
private static final String EMAIL_HEADER = "H-Email";
private static final String GROUPS_HEADER = "H-Groups";

@ClassRule
public static final Orchestrator orchestrator = newOrchestratorBuilder()
.setServerProperty("sonar.web.sso.enable", "true")
.setServerProperty("sonar.web.sso.loginHeader", LOGIN_HEADER)
.setServerProperty("sonar.web.sso.nameHeader", NAME_HEADER)
.setServerProperty("sonar.web.sso.emailHeader", EMAIL_HEADER)
.setServerProperty("sonar.web.sso.groupsHeader", GROUPS_HEADER)
.build();

@Rule
public Tester tester = new Tester(orchestrator).disableOrganizations();

@Test
public void authenticate() {
String login = tester.users().generateLogin();

doCall(login, "Tester", "tester@email.com", null);

verifyUser(login, "Tester", "tester@email.com");
}

@Test
public void authenticate_with_only_login() {
String login = tester.users().generateLogin();

doCall(login, null, null, null);

assertThat(tester.wsClient().users().search(new SearchRequest().setQ(login)).getUsersList())
.extracting(SearchWsResponse.User::getLogin, SearchWsResponse.User::getName, SearchWsResponse.User::hasEmail)
.containsExactlyInAnyOrder(tuple(login, login, false));
}

@Test
public void update_user_when_headers_are_updated() {
String login = tester.users().generateLogin();
doCall(login, "Tester", "tester@email.com", null);
verifyUser(login, "Tester", "tester@email.com");

// As we don't keep the JWT token is the test, the user is updated
doCall(login, "new name", "new email", null);
verifyUser(login, "new name", "new email");
}

@Test
public void authenticate_with_groups() {
String login = tester.users().generateLogin();
Group group = tester.groups().generate();

doCall(login, null, null, group.getName());

assertThat(tester.wsClient().users().search(new SearchRequest().setQ(login)).getUsersList())
.extracting(SearchWsResponse.User::getLogin, u -> u.getGroups().getGroupsList())
.containsExactlyInAnyOrder(tuple(login, asList(group.getName(), "sonar-users")));
}

@Test
public void synchronize_groups_when_authenticating_existing_user() {
User user = tester.users().generate();
Group group1 = tester.groups().generate();
Group group2 = tester.groups().generate();
Group group3 = tester.groups().generate();
tester.groups().addMemberToGroups(null, user.getLogin(), group1.getName(), group2.getName());

doCall(user.getLogin(), null, null, group2.getName() + "," + group3.getName());

assertThat(tester.wsClient().users().search(new SearchRequest().setQ(user.getLogin())).getUsersList())
.extracting(SearchWsResponse.User::getLogin, u -> u.getGroups().getGroupsList())
.containsExactlyInAnyOrder(tuple(user.getLogin(), asList(group2.getName(), group3.getName(), "sonar-users")));
}

@Test
public void authentication_with_local_user_is_possible_when_no_header() {
User user = tester.users().generate();

// Check any ws, no error should be thrown
tester.as(user.getLogin(), user.getLogin()).wsClient().system().ping();
}

@Test
public void display_message_in_ui_but_not_in_log_when_unauthorized_exception() throws Exception {
String login = "invalid login $";
Response response = doCall(login, null, null, null);

assertThat(response.code()).isEqualTo(200);
assertThat(response.request().url().toString()).contains("sessions/unauthorized");

List<String> logsLines = FileUtils.readLines(orchestrator.getServer().getWebLogs(), UTF_8);
assertThat(logsLines).doesNotContain("org.sonar.server.exceptions.BadRequestException: Use only letters, numbers, and .-_@ please.");
assertThat(tester.wsClient().users().search(new SearchRequest().setQ(login)).getUsersList()).isEmpty();
}

@Test
public void fail_when_email_already_exists() throws Exception {
String login = tester.users().generateLogin();
User existingUser = tester.users().generate();

Response response = doCall(login, "Tester", existingUser.getEmail(), null);

String expectedError = format("You can't sign up because email '%s' is already used by an existing user. This means that you probably already registered with another account",
existingUser.getEmail());
assertThat(response.code()).isEqualTo(200);
assertThat(response.request().url().toString()).contains(URLEncoder.encode(expectedError, UTF_8.name()));
assertThat(FileUtils.readLines(orchestrator.getServer().getWebLogs(), UTF_8)).doesNotContain(expectedError);
}

@Test
public void fail_to_authenticate_user_when_email_already_exists_on_several_users() throws Exception {
String login = tester.users().generateLogin();
tester.users().generate(u -> u.setEmail("john@email.com"));
tester.users().generate(u -> u.setEmail("john@email.com"));

Response response = doCall(login, "Tester", "john@email.com", null);

String expectedError = "You can't sign up because email 'john@email.com' is already used by an existing user. This means that you probably already registered with another account";
assertThat(response.code()).isEqualTo(200);
assertThat(response.request().url().toString()).contains(URLEncoder.encode(expectedError, UTF_8.name()));
assertThat(FileUtils.readLines(orchestrator.getServer().getWebLogs(), UTF_8)).doesNotContain(expectedError);
}

private static Response doCall(String login, @Nullable String name, @Nullable String email, @Nullable String groups) {
return call(orchestrator.getServer().getUrl(),
LOGIN_HEADER, login,
NAME_HEADER, name,
EMAIL_HEADER, email,
GROUPS_HEADER, groups);
}

private void verifyUser(String login, String name, String email) {
assertThat(tester.wsClient().users().search(new SearchRequest().setQ(login)).getUsersList())
.extracting(SearchWsResponse.User::getLogin, SearchWsResponse.User::getName, SearchWsResponse.User::getEmail,
SearchWsResponse.User::getExternalIdentity, SearchWsResponse.User::getExternalProvider, SearchWsResponse.User::getLocal)
.containsExactlyInAnyOrder(tuple(login, name, email, login, "sonarqube", false));
}

}

+ 39
- 11
tests/src/test/java/org/sonarqube/tests/user/OAuth2IdentityProviderTest.java View File

@@ -21,11 +21,9 @@ package org.sonarqube.tests.user;

import com.codeborne.selenide.Condition;
import com.sonar.orchestrator.Orchestrator;
import java.io.File;
import java.net.HttpURLConnection;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import org.apache.commons.io.FileUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
@@ -37,10 +35,12 @@ import org.sonarqube.ws.Users.SearchWsResponse.User;
import org.sonarqube.ws.client.GetRequest;
import org.sonarqube.ws.client.permissions.AddUserRequest;
import org.sonarqube.ws.client.users.CreateRequest;
import util.selenium.Selenese;

import static com.codeborne.selenide.Condition.visible;
import static com.codeborne.selenide.Selenide.$;
import static java.lang.String.format;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.commons.io.FileUtils.readFileToString;
import static org.assertj.core.api.Assertions.assertThat;

/**
@@ -137,8 +137,11 @@ public class OAuth2IdentityProviderTest {
// As this property is null, the plugin will throw an exception
tester.settings().setGlobalSettings("sonar.auth.fake-oauth2-id-provider.user", null);

Selenese.runSelenese(orchestrator, "/user/OAuth2IdentityProviderTest/display_unauthorized_page_when_authentication_failed.html");
tester.openBrowser()
.logIn()
.useOAuth2();

$("#bd").shouldHave(Condition.text("You're not authorized to access this page. Please contact the administrator"));
assertThatUserDoesNotExist(USER_LOGIN);
}

@@ -148,8 +151,13 @@ public class OAuth2IdentityProviderTest {
enablePlugin();
tester.settings().setGlobalSettings("sonar.auth.fake-oauth2-id-provider.allowsUsersToSignUp", "false");

Selenese.runSelenese(orchestrator, "/user/OAuth2IdentityProviderTest/fail_to_authenticate_when_not_allowed_to_sign_up.html");
tester.openBrowser()
.logIn()
.useOAuth2();

$("#bd")
.shouldHave(Condition.text("You're not authorized to access this page. Please contact the administrator."))
.shouldHave(Condition.text(format("Reason: '%s' users are not allowed to sign up", FAKE_PROVIDER_KEY)));
assertThatUserDoesNotExist(USER_LOGIN);
}

@@ -159,12 +167,16 @@ public class OAuth2IdentityProviderTest {
enablePlugin();
tester.settings().setGlobalSettings("sonar.auth.fake-oauth2-id-provider.throwUnauthorizedMessage", "true");

Selenese.runSelenese(orchestrator, "/user/OAuth2IdentityProviderTest/display_message_in_ui_but_not_in_log_when_unauthorized_exception.html");

File logFile = orchestrator.getServer().getWebLogs();
assertThat(FileUtils.readFileToString(logFile)).doesNotContain("A functional error has happened");
assertThat(FileUtils.readFileToString(logFile)).doesNotContain("UnauthorizedException");

tester.openBrowser()
.logIn()
.useOAuth2();

$("#bd")
.shouldHave(Condition.text("You're not authorized to access this page. Please contact the administrator"))
.shouldHave(Condition.text("Reason: A functional error has happened"));
assertThat(readFileToString(orchestrator.getServer().getWebLogs(), UTF_8))
.doesNotContain("A functional error has happened")
.doesNotContain("UnauthorizedException");
assertThatUserDoesNotExist(USER_LOGIN);
}

@@ -205,6 +217,22 @@ public class OAuth2IdentityProviderTest {
assertThat(tester.users().getByLogin("another").get().getEmail()).isNullOrEmpty();
}

@Test
public void fail_to_authenticate_user_when_email_already_exists_on_several_users() {
simulateRedirectionToCallback();
enablePlugin();
tester.users().generate(u -> u.setEmail(USER_EMAIL));
tester.users().generate(u -> u.setEmail(USER_EMAIL));

tester.openBrowser()
.logIn()
.useOAuth2();

$("#bd").shouldHave(Condition.text(
format("You can't sign up because email '%s' is already used by an existing user. This means that you probably already registered with another account.", USER_EMAIL)));
assertThatUserDoesNotExist(USER_LOGIN);
}

@Test
public void provision_user_before_authentication() {
simulateRedirectionToCallback();

+ 101
- 175
tests/src/test/java/org/sonarqube/tests/user/RealmAuthenticationTest.java View File

@@ -21,43 +21,30 @@ package org.sonarqube.tests.user;

import com.codeborne.selenide.Condition;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.sonar.orchestrator.Orchestrator;
import java.util.Collections;
import java.util.Map;
import javax.annotation.CheckForNull;
import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.wsclient.Host;
import org.sonar.wsclient.Sonar;
import org.sonar.wsclient.base.HttpException;
import org.sonar.wsclient.connectors.HttpClient4Connector;
import org.sonar.wsclient.services.AuthenticationQuery;
import org.sonar.wsclient.user.UserParameters;
import org.sonarqube.qa.util.Tester;
import org.sonarqube.qa.util.pageobjects.Navigation;
import org.sonarqube.qa.util.pageobjects.SystemInfoPage;
import org.sonarqube.qa.util.pageobjects.UsersManagementPage;
import org.sonarqube.ws.UserGroups.Group;
import org.sonarqube.ws.Users;
import org.sonarqube.ws.Users.SearchWsResponse.User;
import org.sonarqube.ws.client.GetRequest;
import org.sonarqube.ws.client.WsResponse;
import org.sonarqube.ws.client.users.CreateRequest;
import org.sonarqube.ws.client.users.ChangePasswordRequest;
import org.sonarqube.ws.client.users.SearchRequest;
import util.user.UserRule;

import static java.net.HttpURLConnection.HTTP_OK;
import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.groups.Tuple.tuple;
import static org.junit.Assert.fail;
import static util.ItUtils.expectHttpError;
import static util.ItUtils.newOrchestratorBuilder;
import static util.ItUtils.newUserWsClient;
import static util.ItUtils.pluginArtifact;
import static util.ItUtils.resetSettings;
import static util.selenium.Selenese.runSelenese;

/**
* Test REALM authentication.
@@ -66,72 +53,46 @@ import static util.selenium.Selenese.runSelenese;
*/
public class RealmAuthenticationTest {

private static final String TECH_USER = "techUser";
private static final String USER_LOGIN = "tester";
private static final String ADMIN_USER_LOGIN = "admin-user";

@Rule
public ExpectedException thrown = ExpectedException.none();

/**
* Property from security-plugin for user management.
*/
private static final String USERS_PROPERTY = "sonar.fakeauthenticator.users";

@ClassRule
public static final Orchestrator orchestrator = newOrchestratorBuilder()
.addPlugin(pluginArtifact("security-plugin"))
.setServerProperty("sonar.security.realm", "FakeRealm")
.build();

@Rule
public UserRule userRule = UserRule.from(orchestrator);

@Rule
public Tester tester = new Tester(orchestrator).disableOrganizations();

@Before
@After
public void resetData() {
resetSettings(orchestrator, null, USERS_PROPERTY, "sonar.security.updateUserAttributes");
}

@Before
public void initAdminUser() {
userRule.createAdminUser(ADMIN_USER_LOGIN, ADMIN_USER_LOGIN);
}

@After
public void deleteAdminUser() {
userRule.resetUsers();
}

/**
* SONAR-3137, SONAR-2292
* Restriction on password length (minimum 4 characters) should be disabled, when external system enabled.
*/
@Test
public void shouldSynchronizeDetailsAndGroups() {
public void synchronize_details_and_groups() {
// Given clean Sonar installation and no users in external system
String username = USER_LOGIN;
String username = tester.users().generateLogin();
String password = "123";
Map<String, String> users = Maps.newHashMap();
Group group = tester.groups().generate();

// When user created in external system
users.put(username + ".password", password);
users.put(username + ".name", "Tester Testerovich");
users.put(username + ".email", "tester@example.org");
users.put(username + ".groups", "sonar-user");
updateUsersInExtAuth(users);
updateUsersInExtAuth(ImmutableMap.of(
username + ".password", password,
username + ".name", "Tester Testerovich",
username + ".email", "tester@example.org",
username + ".groups", group.getName()));

// Then
verifyAuthenticationIsOk(username, password);
verifyUser();
// with external details and groups
runSelenese(orchestrator, "/user/ExternalAuthenticationTest/external-user-details.html");
assertThat(tester.wsClient().users().search(new SearchRequest().setQ(username)).getUsersList())
.extracting(User::getLogin, User::getName, User::getEmail,
User::getExternalIdentity, User::getExternalProvider, User::getLocal, u -> u.getGroups().getGroupsList())
.containsExactlyInAnyOrder(tuple(username, "Tester Testerovich", "tester@example.org", username, "sonarqube", false, asList(group.getName(), "sonar-users")));

// SONAR-4462
SystemInfoPage page = tester.openBrowser().logIn().submitCredentials(ADMIN_USER_LOGIN).openSystemInfo();
Users.CreateWsResponse.User adminUser = tester.users().generateAdministrator();
SystemInfoPage page = tester.openBrowser().logIn().submitCredentials(adminUser.getLogin()).openSystemInfo();
page.getCardItem("System").shouldHaveFieldWithValue("External User Authentication", "FakeRealm");
}

@@ -139,56 +100,54 @@ public class RealmAuthenticationTest {
* SONAR-4034
*/
@Test
public void shouldUpdateDetailsByDefault() {
public void update_details_by_default() {
// Given clean Sonar installation and no users in external system
String username = USER_LOGIN;
String username = tester.users().generateLogin();
String password = "123";
Map<String, String> users = Maps.newHashMap();

// When user created in external system
users.put(username + ".password", password);
users.put(username + ".name", "Tester Testerovich");
users.put(username + ".email", "tester@example.org");
users.put(username + ".groups", "sonar-user");
updateUsersInExtAuth(users);
updateUsersInExtAuth(ImmutableMap.of(
username + ".password", password,
username + ".name", "Tester Testerovich",
username + ".email", "tester@example.org"));
// Then
verifyAuthenticationIsOk(username, password);
verifyUser();

// with external details and groups
// TODO replace by WS ? Or with new Selenese utils
runSelenese(orchestrator, "/user/ExternalAuthenticationTest/external-user-details.html");
assertThat(tester.wsClient().users().search(new SearchRequest().setQ(username)).getUsersList())
.extracting(User::getLogin, User::getName, User::getEmail)
.containsExactlyInAnyOrder(tuple(username, "Tester Testerovich", "tester@example.org"));

// Now update user details
users.put(username + ".name", "Tester2 Testerovich");
users.put(username + ".email", "tester2@example.org");
updateUsersInExtAuth(users);
updateUsersInExtAuth(ImmutableMap.of(
username + ".password", password,
username + ".name", "Tester2 Testerovich",
username + ".email", "tester2@example.org"));

// Then
verifyAuthenticationIsOk(username, password);

// with external details and groups updated
runSelenese(orchestrator, "/user/ExternalAuthenticationTest/external-user-details2.html");
assertThat(tester.wsClient().users().search(new SearchRequest().setQ(username)).getUsersList())
.extracting(User::getLogin, User::getName, User::getEmail)
.containsExactlyInAnyOrder(tuple(username, "Tester2 Testerovich", "tester2@example.org"));
}

/**
* SONAR-3138
*/
@Test
public void shouldNotFallback() {
public void does_not_fallback() {
// Given clean Sonar installation and no users in external system
String login = USER_LOGIN;
String login = tester.users().generateLogin();
String password = "1234567";
Map<String, String> users = Maps.newHashMap();

// When user created in external system
users.put(login + ".password", password);
updateUsersInExtAuth(users);
updateUsersInExtAuth(ImmutableMap.of(login + ".password", password));
// Then
verifyAuthenticationIsOk(login, password);

// When external system does not work
users.remove(login + ".password");
updateUsersInExtAuth(users);
updateUsersInExtAuth(Collections.emptyMap());
// Then
verifyAuthenticationIsNotOk(login, password);
}
@@ -197,16 +156,14 @@ public class RealmAuthenticationTest {
* SONAR-4543
*/
@Test
public void adminIsLocalAccountByDefault() {
public void admin_is_local_account_by_default() {
// Given clean Sonar installation and no users in external system
String login = "admin";
String localPassword = "admin";
String remotePassword = "nimda";
Map<String, String> users = Maps.newHashMap();

// When admin created in external system with a different password
users.put(login + ".password", remotePassword);
updateUsersInExtAuth(users);
updateUsersInExtAuth(ImmutableMap.of(login + ".password", remotePassword));

// Then this is local DB that should be used
verifyAuthenticationIsNotOk(login, remotePassword);
@@ -217,52 +174,49 @@ public class RealmAuthenticationTest {
* SONAR-1334, SONAR-3185 (createUsers=true is default)
*/
@Test
public void shouldCreateNewUsers() {
public void create_new_users() {
// Given clean Sonar installation and no users in external system
String username = USER_LOGIN;
String username = tester.users().generateLogin();
String password = "1234567";
Map<String, String> users = Maps.newHashMap();

// When user not exists in external system
// Then
verifyAuthenticationIsNotOk(username, password);

// When user created in external system
users.put(username + ".password", password);
updateUsersInExtAuth(users);
updateUsersInExtAuth(ImmutableMap.of(username + ".password", password));
// Then
verifyAuthenticationIsOk(username, password);
verifyUser();
verifyUser(username);
verifyAuthenticationIsNotOk(username, "wrong");
}

// SONAR-3258
@Test
public void shouldAutomaticallyReactivateDeletedUser() {
public void reactivate_deleted_user() {
// Given clean Sonar installation and no users in external system
Users.CreateWsResponse.User adminUser = tester.users().generateAdministrator();

// Let's create and delete the user "tester" in Sonar DB
String login = tester.users().generateLogin();
Navigation nav = tester.openBrowser();
UsersManagementPage page = nav.logIn().submitCredentials(ADMIN_USER_LOGIN).openUsersManagement();
UsersManagementPage page = nav.logIn().submitCredentials(adminUser.getLogin()).openUsersManagement();
page
.createUser(USER_LOGIN)
.createUser(login)
.hasUsersCount(3)
.getUser(USER_LOGIN)
.getUser(login)
.deactivateUser();
page.hasUsersCount(2);
nav.logOut()
.logIn().submitWrongCredentials(USER_LOGIN, USER_LOGIN)
.logIn().submitWrongCredentials(login, login)
.getErrorMessage().shouldHave(Condition.text("Authentication failed"));

// And now update the security with the user that was deleted
String login = USER_LOGIN;
String password = "1234567";
Map<String, String> users = Maps.newHashMap();
users.put(login + ".password", password);
updateUsersInExtAuth(users);
updateUsersInExtAuth(ImmutableMap.of(login + ".password", password));
// check that the deleted/deactivated user "tester" has been reactivated and can now log in
verifyAuthenticationIsOk(login, password);
verifyUser();
verifyUser(login);
}

/**
@@ -271,24 +225,20 @@ public class RealmAuthenticationTest {
@Test
public void update_password_of_technical_user() {
// Create user in external authentication
updateUsersInExtAuth(ImmutableMap.of(USER_LOGIN + ".password", USER_LOGIN));
verifyAuthenticationIsOk(USER_LOGIN, USER_LOGIN);
String login = tester.users().generateLogin();
updateUsersInExtAuth(ImmutableMap.of(login + ".password", login));
verifyAuthenticationIsOk(login, login);

// Create technical user in db
createUserInDb(TECH_USER, "old_password");
assertThat(checkAuthenticationThroughWebService(TECH_USER, "old_password")).isTrue();
Users.CreateWsResponse.User techUser = tester.users().generate(u -> u.setPassword("old_password"));
verifyAuthenticationIsOk(techUser.getLogin(), "old_password");

// Updating password of technical user is allowed
updateUserPasswordInDb(TECH_USER, "new_password");
assertThat(checkAuthenticationThroughWebService(TECH_USER, "new_password")).isTrue();
tester.users().service().changePassword(new ChangePasswordRequest().setLogin(techUser.getLogin()).setPassword("new_password"));
verifyAuthenticationIsOk(techUser.getLogin(), "new_password");

// But updating password of none local user is not allowed
try {
updateUserPasswordInDb(USER_LOGIN, "new_password");
fail();
} catch (HttpException e) {
verifyHttpException(e, 400);
}
expectHttpError(400, () -> tester.users().service().changePassword(new ChangePasswordRequest().setLogin(login).setPassword("new_password")));
}

/**
@@ -297,16 +247,14 @@ public class RealmAuthenticationTest {
@Test
public void authentication_with_ws() {
// Given clean Sonar installation and no users in external system
String login = USER_LOGIN;
String login = tester.users().generateLogin();
String password = "1234567";
Map<String, String> users = Maps.newHashMap();

// When user created in external system
users.put(login + ".password", password);
updateUsersInExtAuth(users);
updateUsersInExtAuth(ImmutableMap.of(login + ".password", password));

verifyAuthenticationIsOk(login, password);
verifyUser();
verifyUser(login);
verifyAuthenticationIsNotOk("wrong", password);
verifyAuthenticationIsNotOk(login, "wrong");
verifyAuthenticationIsNotOk(login, null);
@@ -332,71 +280,54 @@ public class RealmAuthenticationTest {

@Test
public void provision_user_before_authentication() {
tester.wsClient().users().create(new CreateRequest()
.setLogin(USER_LOGIN)
.setName("Tester Testerovich")
Users.CreateWsResponse.User user = tester.users().generate(u -> u.setName("Tester Testerovich")
.setEmail("tester@example.org")
.setPassword(null)
.setLocal("false"));
// The user is created in SonarQube but doesn't exist yet in external authentication system
verifyAuthenticationIsNotOk(USER_LOGIN, "123");
verifyAuthenticationIsNotOk(user.getLogin(), "123");

updateUsersInExtAuth(ImmutableMap.of(
USER_LOGIN + ".password", "123",
USER_LOGIN + ".name", "Tester Testerovich",
USER_LOGIN + ".email", "tester@example.org"));
user.getLogin() + ".password", "123",
user.getLogin() + ".name", "Tester Testerovich",
user.getLogin() + ".email", "tester@example.org"));

verifyAuthenticationIsOk(USER_LOGIN, "123");
verifyUser();
verifyAuthenticationIsOk(user.getLogin(), "123");
verifyUser(user.getLogin());
}

@Test
public void fail_to_authenticate_user_when_email_already_exists() {
userRule.createUser("another", "Another", "tester@example.org", "another");

String username = USER_LOGIN;
Users.CreateWsResponse.User user = tester.users().generate();
String username = tester.users().generateLogin();
String password = "123";
Map<String, String> users = Maps.newHashMap();
users.put(username + ".password", password);
users.put(username + ".name", "Tester Testerovich");
users.put(username + ".email", "tester@example.org");
users.put(username + ".groups", "sonar-user");
updateUsersInExtAuth(users);

updateUsersInExtAuth(ImmutableMap.of(
username + ".password", password,
username + ".email", user.getEmail()));

verifyAuthenticationIsNotOk(username, password);
}

private void verifyHttpException(Exception e, int expectedCode) {
assertThat(e).isInstanceOf(HttpException.class);
HttpException exception = (HttpException) e;
assertThat(exception.status()).isEqualTo(expectedCode);
}
@Test
public void fail_to_authenticate_user_when_email_already_exists_on_several_users() {
Users.CreateWsResponse.User user1 = tester.users().generate(u -> u.setEmail("user@email.com"));
Users.CreateWsResponse.User user2 = tester.users().generate(u -> u.setEmail("user@email.com"));
String username = tester.users().generateLogin();
String password = "123";

private boolean checkAuthenticationThroughWebService(String login, String password) {
return createWsClient(login, password).find(new AuthenticationQuery()).isValid();
updateUsersInExtAuth(ImmutableMap.of(
username + ".password", password,
username + ".email", "user@email.com"));

verifyAuthenticationIsNotOk(username, password);
}

/**
* Updates information about users in security-plugin.
*/
private void updateUsersInExtAuth(Map<String, String> users) {
tester.settings().setGlobalSettings(USERS_PROPERTY, format(users));
}

private void createUserInDb(String login, String password) {
orchestrator.getServer().adminWsClient().userClient().create(UserParameters.create().login(login).name(login)
.password(password).passwordConfirmation(password));
}

private void updateUserPasswordInDb(String login, String newPassword) {
orchestrator.getServer().adminWsClient().post("/api/users/change_password", "login", login, "password", newPassword);
}

/**
* Utility method to create {@link Sonar} with specified {@code username} and {@code password}.
* Orchestrator does not provide such method.
*/
private Sonar createWsClient(String username, String password) {
return new Sonar(new HttpClient4Connector(new Host(orchestrator.getServer().getUrl(), username, password)));
tester.settings().setGlobalSettings("sonar.fakeauthenticator.users", format(users));
}

@CheckForNull
@@ -412,22 +343,17 @@ public class RealmAuthenticationTest {
}

private void verifyAuthenticationIsOk(String login, String password) {
assertThat(checkAuthenticationWithWebService(login, password).code()).isEqualTo(HTTP_OK);
tester.as(login, password).wsClient().system().ping();
}

private void verifyAuthenticationIsNotOk(String login, String password) {
assertThat(checkAuthenticationWithWebService(login, password).code()).isEqualTo(HTTP_UNAUTHORIZED);
}

private void verifyUser(){
assertThat(tester.wsClient().users().search(new SearchRequest().setQ(USER_LOGIN)).getUsersList())
.extracting(User::getLogin, User::getExternalIdentity, User::getExternalProvider, User::getLocal)
.containsExactlyInAnyOrder(tuple(USER_LOGIN, USER_LOGIN, "sonarqube", false));
expectHttpError(401, () -> tester.as(login, password).wsClient().system().ping());
}

private WsResponse checkAuthenticationWithWebService(String login, String password) {
// Call any WS
return newUserWsClient(orchestrator, login, password).wsConnector().call(new GetRequest("api/rules/search"));
private void verifyUser(String login) {
assertThat(tester.wsClient().users().search(new SearchRequest().setQ(login)).getUsersList())
.extracting(User::getLogin, User::getExternalIdentity, User::getExternalProvider, User::getLocal)
.containsExactlyInAnyOrder(tuple(login, login, "sonarqube", false));
}

}

+ 0
- 166
tests/src/test/java/org/sonarqube/tests/user/SsoAuthenticationTest.java View File

@@ -1,166 +0,0 @@
/*
* SonarQube
* Copyright (C) 2009-2018 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonarqube.tests.user;

import com.sonar.orchestrator.Orchestrator;
import java.net.URLEncoder;
import java.util.List;
import javax.annotation.Nullable;
import okhttp3.Response;
import org.apache.commons.io.FileUtils;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import util.user.UserRule;

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import static util.ItUtils.call;
import static util.ItUtils.newOrchestratorBuilder;

/**
* Test SSO authentication (using HTTP headers).
* <p>
* It starts its own server as it's using a different authentication system
*/
public class SsoAuthenticationTest {

private static final String LOGIN_HEADER = "H-Login";
private static final String NAME_HEADER = "H-Name";
private static final String EMAIL_HEADER = "H-Email";
private static final String GROUPS_HEADER = "H-Groups";

static final String USER_LOGIN = "tester";
static final String USER_NAME = "Tester";
static final String USER_EMAIL = "tester@email.com";

static final String GROUP_1 = "group-1";
static final String GROUP_2 = "group-2";
static final String GROUP_3 = "group-3";

@ClassRule
public static final Orchestrator orchestrator = newOrchestratorBuilder()
.setServerProperty("sonar.web.sso.enable", "true")
.setServerProperty("sonar.web.sso.loginHeader", LOGIN_HEADER)
.setServerProperty("sonar.web.sso.nameHeader", NAME_HEADER)
.setServerProperty("sonar.web.sso.emailHeader", EMAIL_HEADER)
.setServerProperty("sonar.web.sso.groupsHeader", GROUPS_HEADER)
.build();

private static UserRule userRule = UserRule.from(orchestrator);

@ClassRule
public static RuleChain ruleChain = RuleChain.outerRule(orchestrator).around(userRule);

@Before
public void resetData() {
userRule.resetUsers();
}

@Test
public void authenticate() {
doCall(USER_LOGIN, USER_NAME, USER_EMAIL, null);

userRule.verifyUserExists(USER_LOGIN, USER_NAME, USER_EMAIL);
}

@Test
public void authenticate_with_only_login() {
doCall(USER_LOGIN, null, null, null);

userRule.verifyUserExists(USER_LOGIN, USER_LOGIN, null);
}

@Test
public void update_user_when_headers_are_updated() {
doCall(USER_LOGIN, USER_NAME, USER_EMAIL, null);
userRule.verifyUserExists(USER_LOGIN, USER_NAME, USER_EMAIL);

// As we don't keep the JWT token is the test, the user is updated
doCall(USER_LOGIN, "new name", "new email", null);
userRule.verifyUserExists(USER_LOGIN, "new name", "new email");
}

@Test
public void authenticate_with_groups() {
doCall(USER_LOGIN, null, null, GROUP_1);

userRule.verifyUserGroupMembership(USER_LOGIN, GROUP_1, "sonar-users");
}

@Test
public void synchronize_groups_when_authenticating_existing_user() {
userRule.createGroup(GROUP_1);
userRule.createGroup(GROUP_2);
userRule.createGroup(GROUP_3);
userRule.createUser(USER_LOGIN, "password");
userRule.associateGroupsToUser(USER_LOGIN, GROUP_1, GROUP_2);

doCall(USER_LOGIN, null, null, GROUP_2 + "," + GROUP_3);

userRule.verifyUserGroupMembership(USER_LOGIN, GROUP_2, GROUP_3, "sonar-users");
}

@Test
public void authentication_with_local_user_is_possible_when_no_header() {
userRule.createUser(USER_LOGIN, "password");

checkLocalAuthentication(USER_LOGIN, "password");
}

@Test
public void display_message_in_ui_but_not_in_log_when_unauthorized_exception() throws Exception {
Response response = doCall("invalid login $", null, null, null);

assertThat(response.code()).isEqualTo(200);
assertThat(response.request().url().toString()).contains("sessions/unauthorized");

List<String> logsLines = FileUtils.readLines(orchestrator.getServer().getWebLogs(), UTF_8);
assertThat(logsLines).doesNotContain("org.sonar.server.exceptions.BadRequestException: Use only letters, numbers, and .-_@ please.");
userRule.verifyUserDoesNotExist(USER_LOGIN);
}

@Test
public void fail_when_email_already_exists() throws Exception {
userRule.createUser("another", "Another", USER_EMAIL, "another");

Response response = doCall(USER_LOGIN, USER_NAME, USER_EMAIL, null);

String expectedError = "You can't sign up because email 'tester@email.com' is already used by an existing user. This means that you probably already registered with another account";
assertThat(response.code()).isEqualTo(200);
assertThat(response.request().url().toString()).contains(URLEncoder.encode(expectedError, UTF_8.name()));
assertThat(FileUtils.readLines(orchestrator.getServer().getWebLogs(), UTF_8)).doesNotContain(expectedError);
}

private static Response doCall(String login, @Nullable String name, @Nullable String email, @Nullable String groups) {
return call(orchestrator.getServer().getUrl(),
LOGIN_HEADER, login,
NAME_HEADER, name,
EMAIL_HEADER, email,
GROUPS_HEADER, groups);
}

private boolean checkLocalAuthentication(String login, String password) {
String result = orchestrator.getServer().wsClient(login, password).get("/api/authentication/validate");
return result.contains("{\"valid\":true}");
}

}

+ 0
- 44
tests/src/test/resources/user/BaseIdentityProviderTest/display_message_in_ui_but_not_in_log_when_unauthorized_exception.html View File

@@ -1,44 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head profile="http://selenium-ide.openqa.org/profiles/test-case">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>fail_to_authenticate_when_not_allowed_to_sign_up</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr>
<td rowspan="1" colspan="3">french</td>
</tr>
</thead>
<tbody>
<tr>
<td>open</td>
<td>/sessions/new</td>
<td></td>
</tr>
<tr>
<td>waitForText</td>
<td>content</td>
<td>*Log in with Fake base identity provider*</td>
</tr>
<tr>
<td>click</td>
<td>css=.oauth-providers a</td>
<td></td>
</tr>
<tr>
<td>waitForText</td>
<td>bd</td>
<td>*You're not authorized to access this page. Please contact the administrator.*</td>
</tr>
<tr>
<td>assertText</td>
<td>bd</td>
<td>*Reason: A functional error has happened*</td>
</tr>
</tbody>
</table>
</body>
</html>

+ 0
- 39
tests/src/test/resources/user/BaseIdentityProviderTest/display_unauthorized_page_when_authentication_failed.html View File

@@ -1,39 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head profile="http://selenium-ide.openqa.org/profiles/test-case">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>display_unauthorized_page_when_authentication_failed</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr>
<td rowspan="1" colspan="3">french</td>
</tr>
</thead>
<tbody>
<tr>
<td>open</td>
<td>/sessions/new</td>
<td></td>
</tr>
<tr>
<td>waitForText</td>
<td>content</td>
<td>*Log in with Fake base identity provider*</td>
</tr>
<tr>
<td>click</td>
<td>css=.oauth-providers a</td>
<td></td>
</tr>
<tr>
<td>waitForText</td>
<td>bd</td>
<td>*You're not authorized to access this page. Please contact the administrator.*</td>
</tr>
</tbody>
</table>
</body>
</html>

+ 0
- 39
tests/src/test/resources/user/BaseIdentityProviderTest/fail_to_authenticate_when_not_allowed_to_sign_up.html View File

@@ -1,39 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head profile="http://selenium-ide.openqa.org/profiles/test-case">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>fail_to_authenticate_when_not_allowed_to_sign_up</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr>
<td rowspan="1" colspan="3">french</td>
</tr>
</thead>
<tbody>
<tr>
<td>open</td>
<td>/sessions/new</td>
<td></td>
</tr>
<tr>
<td>waitForText</td>
<td>content</td>
<td>*Log in with Fake base identity provider*</td>
</tr>
<tr>
<td>click</td>
<td>css=.oauth-providers a</td>
<td></td>
</tr>
<tr>
<td>waitForText</td>
<td>bd</td>
<td>*You're not authorized to access this page. Please contact the administrator.*Reason: 'fake-base-id-provider' users are not allowed to sign up*</td>
</tr>
</tbody>
</table>
</body>
</html>

+ 0
- 44
tests/src/test/resources/user/BaseIdentityProviderTest/fail_when_email_already_exists.html View File

@@ -1,44 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head profile="http://selenium-ide.openqa.org/profiles/test-case">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>fail_when_email_already_exists</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr>
<td rowspan="1" colspan="3">french</td>
</tr>
</thead>
<tbody>
<tr>
<td>open</td>
<td>/sessions/new</td>
<td></td>
</tr>
<tr>
<td>waitForText</td>
<td>content</td>
<td>*Log in with Fake base identity provider*</td>
</tr>
<tr>
<td>click</td>
<td>css=.oauth-providers a</td>
<td></td>
</tr>
<tr>
<td>waitForText</td>
<td>bd</td>
<td>*You're not authorized to access this page. Please contact the administrator.*</td>
</tr>
<tr>
<td>assertText</td>
<td>bd</td>
<td>*You can't sign up because email 'john@email.com' is already used by an existing user. This means that you probably already registered with another account*</td>
</tr>
</tbody>
</table>
</body>
</html>

+ 0
- 64
tests/src/test/resources/user/ExternalAuthenticationTest/external-user-details.html View File

@@ -1,64 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-/W3C/DTD XHTML 1.0 Strict/EN" "http:/www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http:/www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head profile="http:/selenium-ide.openqa.org/profiles/test-case">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>external_user_details</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr>
<td rowspan="1" colspan="3">external_user_details</td>
</tr>
</thead>
<tbody>
<tr>
<td>open</td>
<td>/sessions/new</td>
<td></td>
</tr>
<tr>
<td>type</td>
<td>login</td>
<td>tester</td>
</tr>
<tr>
<td>type</td>
<td>password</td>
<td>123</td>
</tr>
<tr>
<td>clickAndWait</td>
<td>[type=submit]</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>css=.js-user-authenticated</td>
<td></td>
</tr>
<tr>
<td>open</td>
<td>/account/</td>
<td></td>
</tr>
<tr>
<td>waitForText</td>
<td>login</td>
<td>tester</td>
</tr>
<tr>
<td>waitForText</td>
<td>id=name</td>
<td>Tester Testerovich</td>
</tr>
<tr>
<td>waitForText</td>
<td>id=email</td>
<td>tester@example.org</td>
</tr>
</tbody>
</table>
</body>
</html>

+ 0
- 64
tests/src/test/resources/user/ExternalAuthenticationTest/external-user-details2.html View File

@@ -1,64 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-/W3C/DTD XHTML 1.0 Strict/EN" "http:/www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http:/www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head profile="http:/selenium-ide.openqa.org/profiles/test-case">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>external_user_details</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr>
<td rowspan="1" colspan="3">external_user_details</td>
</tr>
</thead>
<tbody>
<tr>
<td>open</td>
<td>/sessions/new</td>
<td></td>
</tr>
<tr>
<td>type</td>
<td>login</td>
<td>tester</td>
</tr>
<tr>
<td>type</td>
<td>password</td>
<td>123</td>
</tr>
<tr>
<td>clickAndWait</td>
<td>[type=submit]</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>css=.js-user-authenticated</td>
<td></td>
</tr>
<tr>
<td>open</td>
<td>/account</td>
<td></td>
</tr>
<tr>
<td>waitForText</td>
<td>login</td>
<td>tester</td>
</tr>
<tr>
<td>waitForText</td>
<td>id=name</td>
<td>Tester2 Testerovich</td>
</tr>
<tr>
<td>waitForText</td>
<td>id=email</td>
<td>tester2@example.org</td>
</tr>
</tbody>
</table>
</body>
</html>

+ 0
- 44
tests/src/test/resources/user/OAuth2IdentityProviderTest/display_message_in_ui_but_not_in_log_when_unauthorized_exception.html View File

@@ -1,44 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head profile="http://selenium-ide.openqa.org/profiles/test-case">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>fail_to_authenticate_when_not_allowed_to_sign_up</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr>
<td rowspan="1" colspan="3">french</td>
</tr>
</thead>
<tbody>
<tr>
<td>open</td>
<td>/sessions/new</td>
<td></td>
</tr>
<tr>
<td>waitForText</td>
<td>content</td>
<td>*Log in with Fake oauth2 identity provider*</td>
</tr>
<tr>
<td>click</td>
<td>css=.oauth-providers a</td>
<td></td>
</tr>
<tr>
<td>waitForText</td>
<td>bd</td>
<td>*You're not authorized to access this page. Please contact the administrator.*</td>
</tr>
<tr>
<td>assertText</td>
<td>bd</td>
<td>*Reason: A functional error has happened*</td>
</tr>
</tbody>
</table>
</body>
</html>

+ 0
- 39
tests/src/test/resources/user/OAuth2IdentityProviderTest/display_unauthorized_page_when_authentication_failed.html View File

@@ -1,39 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head profile="http://selenium-ide.openqa.org/profiles/test-case">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>display_unauthorized_page_when_authentication_failed</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr>
<td rowspan="1" colspan="3">french</td>
</tr>
</thead>
<tbody>
<tr>
<td>open</td>
<td>/sessions/new</td>
<td></td>
</tr>
<tr>
<td>waitForText</td>
<td>content</td>
<td>*Log in with Fake oauth2 identity provider*</td>
</tr>
<tr>
<td>click</td>
<td>css=.oauth-providers a</td>
<td></td>
</tr>
<tr>
<td>waitForText</td>
<td>bd</td>
<td>*You're not authorized to access this page. Please contact the administrator.*</td>
</tr>
</tbody>
</table>
</body>
</html>

+ 0
- 39
tests/src/test/resources/user/OAuth2IdentityProviderTest/fail_to_authenticate_when_not_allowed_to_sign_up.html View File

@@ -1,39 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head profile="http://selenium-ide.openqa.org/profiles/test-case">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>fail_to_authenticate_when_not_allowed_to_sign_up</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr>
<td rowspan="1" colspan="3">french</td>
</tr>
</thead>
<tbody>
<tr>
<td>open</td>
<td>/sessions/new</td>
<td></td>
</tr>
<tr>
<td>waitForText</td>
<td>content</td>
<td>*Log in with Fake oauth2 identity provider*</td>
</tr>
<tr>
<td>click</td>
<td>css=.oauth-providers a</td>
<td></td>
</tr>
<tr>
<td>waitForText</td>
<td>bd</td>
<td>*You're not authorized to access this page. Please contact the administrator.*Reason: 'fake-oauth2-id-provider' users are not allowed to sign up*</td>
</tr>
</tbody>
</table>
</body>
</html>

Loading…
Cancel
Save