]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-15137 Prevent users from associating their account with a new identity provider
authorJacek <jacek.poreda@sonarsource.com>
Wed, 7 Jul 2021 11:37:17 +0000 (13:37 +0200)
committersonartech <sonartech@sonarsource.com>
Mon, 12 Jul 2021 20:08:36 +0000 (20:08 +0000)
(cherry picked from commit aa293bae8cf08737d0e3dc856f40847021fad514)

22 files changed:
server/sonar-web/src/main/js/apps/sessions/components/EmailAlreadyExists.tsx [deleted file]
server/sonar-web/src/main/js/apps/sessions/components/__tests__/EmailAlreadyExists-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/EmailAlreadyExists-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/sessions/routes.ts
server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/BaseContextFactory.java
server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/CredentialsExternalAuthentication.java
server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/HttpHeadersAuthentication.java
server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/InitFilter.java
server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/OAuth2AuthenticationParameters.java
server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/OAuth2AuthenticationParametersImpl.java
server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/OAuth2CallbackFilter.java
server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/OAuth2ContextFactory.java
server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/UserRegistrarImpl.java
server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/UserRegistration.java
server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/exception/EmailAlreadyExistsRedirectionException.java [deleted file]
server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/CredentialsExternalAuthenticationTest.java
server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/InitFilterTest.java
server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/OAuth2AuthenticationParametersImplTest.java
server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/OAuth2CallbackFilterTest.java
server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/OAuth2ContextFactoryTest.java
server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/UserRegistrarImplTest.java
sonar-core/src/main/resources/org/sonar/l10n/core.properties

diff --git a/server/sonar-web/src/main/js/apps/sessions/components/EmailAlreadyExists.tsx b/server/sonar-web/src/main/js/apps/sessions/components/EmailAlreadyExists.tsx
deleted file mode 100644 (file)
index 08d5157..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2021 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.
- */
-import * as React from 'react';
-import { FormattedMessage } from 'react-intl';
-import { Alert } from 'sonar-ui-common/components/ui/Alert';
-import { getTextColor } from 'sonar-ui-common/helpers/colors';
-import { getCookie } from 'sonar-ui-common/helpers/cookies';
-import { translate } from 'sonar-ui-common/helpers/l10n';
-import { getBaseUrl } from 'sonar-ui-common/helpers/urls';
-import { getIdentityProviders } from '../../../api/users';
-import { colors } from '../../../app/theme';
-
-interface State {
-  identityProviders: T.IdentityProvider[];
-}
-
-export default class EmailAlreadyExists extends React.PureComponent<{}, State> {
-  mounted = false;
-  state: State = { identityProviders: [] };
-
-  componentDidMount() {
-    this.mounted = true;
-    this.fetchIdentityProviders();
-  }
-
-  componentWillUnmount() {
-    this.mounted = false;
-  }
-
-  fetchIdentityProviders = () => {
-    getIdentityProviders().then(
-      ({ identityProviders }) => {
-        if (this.mounted) {
-          this.setState({ identityProviders });
-        }
-      },
-      () => {}
-    );
-  };
-
-  getAuthError = (): {
-    email?: string;
-    login?: string;
-    provider?: string;
-    existingLogin?: string;
-    existingProvider?: string;
-  } => {
-    const cookie = getCookie('AUTHENTICATION-ERROR');
-    if (cookie) {
-      return JSON.parse(decodeURIComponent(cookie));
-    }
-    return {};
-  };
-
-  renderIdentityProvier = (provider?: string, login?: string) => {
-    const identityProvider = this.state.identityProviders.find(p => p.key === provider);
-
-    return identityProvider ? (
-      <div
-        className="identity-provider"
-        style={{
-          backgroundColor: identityProvider.backgroundColor,
-          color: getTextColor(identityProvider.backgroundColor, colors.secondFontColor)
-        }}>
-        <img
-          alt={identityProvider.name}
-          className="little-spacer-right"
-          height="14"
-          src={getBaseUrl() + identityProvider.iconPath}
-          width="14"
-        />
-        {login}
-      </div>
-    ) : (
-      <div>
-        {provider !== 'sonarqube' && provider} {login}
-      </div>
-    );
-  };
-
-  render() {
-    const authError = this.getAuthError();
-    return (
-      <div className="page-wrapper-simple" id="bd">
-        <div className="page-simple" id="nonav">
-          <div className="big-spacer-bottom js-existing-account">
-            <p className="little-spacer-bottom">
-              <FormattedMessage
-                defaultMessage={translate('sessions.email_already_exists.1')}
-                id="sessions.email_already_exists.1"
-                values={{ email: <strong>{authError.email}</strong> }}
-              />
-            </p>
-            {this.renderIdentityProvier(authError.existingProvider, authError.existingLogin)}
-          </div>
-
-          <div className="big-spacer-bottom js-new-account">
-            <p className="little-spacer-bottom">{translate('sessions.email_already_exists.2')}</p>
-            {this.renderIdentityProvier(authError.provider, authError.login)}
-          </div>
-
-          <Alert variant="warning">
-            {translate('sessions.email_already_exists.3')}
-            <ul className="list-styled">
-              <li className="spacer-top">{translate('sessions.email_already_exists.4')}</li>
-              <li className="spacer-top">{translate('sessions.email_already_exists.5')}</li>
-              <li className="spacer-top">{translate('sessions.email_already_exists.6')}</li>
-            </ul>
-          </Alert>
-
-          <div className="big-spacer-top text-right">
-            <a
-              className="button js-continue"
-              href={`${getBaseUrl()}/sessions/init/${authError.provider}?allowEmailShift=true`}>
-              {translate('continue')}
-            </a>
-            <a className="big-spacer-left js-cancel" href={getBaseUrl() + '/'}>
-              {translate('cancel')}
-            </a>
-          </div>
-        </div>
-      </div>
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/sessions/components/__tests__/EmailAlreadyExists-test.tsx b/server/sonar-web/src/main/js/apps/sessions/components/__tests__/EmailAlreadyExists-test.tsx
deleted file mode 100644 (file)
index 411c333..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2021 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.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
-import EmailAlreadyExists from '../EmailAlreadyExists';
-
-jest.mock('../../../../api/users', () => ({
-  getIdentityProviders: () =>
-    Promise.resolve({
-      identityProviders: [
-        {
-          key: 'bitbucket',
-          name: 'Bitbucket',
-          iconPath: '/static/authbitbucket/bitbucket.svg',
-          backgroundColor: '#0052cc'
-        },
-        {
-          key: 'github',
-          name: 'GitHub',
-          iconPath: '/static/authgithub/github.svg',
-          backgroundColor: '#444444'
-        }
-      ]
-    })
-}));
-
-jest.mock('sonar-ui-common/helpers/cookies', () => ({
-  getCookie: jest
-    .fn()
-    .mockReturnValue(
-      '%7B%22email%22%3A%22mail%40example.com%22%2C%22login%22%3A%22foo%22%2C%22provider%22%3A%22github%22%2C%22existingLogin%22%3A%22bar%22%2C%22existingProvider%22%3A%22bitbucket%22%7D'
-    )
-}));
-
-it('render', async () => {
-  const wrapper = shallow(<EmailAlreadyExists />);
-  (wrapper.instance() as EmailAlreadyExists).mounted = true;
-  (wrapper.instance() as EmailAlreadyExists).fetchIdentityProviders();
-  await waitAndUpdate(wrapper);
-  expect(wrapper).toMatchSnapshot();
-});
diff --git a/server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/EmailAlreadyExists-test.tsx.snap b/server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/EmailAlreadyExists-test.tsx.snap
deleted file mode 100644 (file)
index 26c56ed..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`render 1`] = `
-<div
-  className="page-wrapper-simple"
-  id="bd"
->
-  <div
-    className="page-simple"
-    id="nonav"
-  >
-    <div
-      className="big-spacer-bottom js-existing-account"
-    >
-      <p
-        className="little-spacer-bottom"
-      >
-        <FormattedMessage
-          defaultMessage="sessions.email_already_exists.1"
-          id="sessions.email_already_exists.1"
-          values={
-            Object {
-              "email": <strong>
-                mail@example.com
-              </strong>,
-            }
-          }
-        />
-      </p>
-      <div
-        className="identity-provider"
-        style={
-          Object {
-            "backgroundColor": "#0052cc",
-            "color": "#fff",
-          }
-        }
-      >
-        <img
-          alt="Bitbucket"
-          className="little-spacer-right"
-          height="14"
-          src="/static/authbitbucket/bitbucket.svg"
-          width="14"
-        />
-        bar
-      </div>
-    </div>
-    <div
-      className="big-spacer-bottom js-new-account"
-    >
-      <p
-        className="little-spacer-bottom"
-      >
-        sessions.email_already_exists.2
-      </p>
-      <div
-        className="identity-provider"
-        style={
-          Object {
-            "backgroundColor": "#444444",
-            "color": "#fff",
-          }
-        }
-      >
-        <img
-          alt="GitHub"
-          className="little-spacer-right"
-          height="14"
-          src="/static/authgithub/github.svg"
-          width="14"
-        />
-        foo
-      </div>
-    </div>
-    <Alert
-      variant="warning"
-    >
-      sessions.email_already_exists.3
-      <ul
-        className="list-styled"
-      >
-        <li
-          className="spacer-top"
-        >
-          sessions.email_already_exists.4
-        </li>
-        <li
-          className="spacer-top"
-        >
-          sessions.email_already_exists.5
-        </li>
-        <li
-          className="spacer-top"
-        >
-          sessions.email_already_exists.6
-        </li>
-      </ul>
-    </Alert>
-    <div
-      className="big-spacer-top text-right"
-    >
-      <a
-        className="button js-continue"
-        href="/sessions/init/github?allowEmailShift=true"
-      >
-        continue
-      </a>
-      <a
-        className="big-spacer-left js-cancel"
-        href="/"
-      >
-        cancel
-      </a>
-    </div>
-  </div>
-</div>
-`;
index f3197bbb219020cfdebed41bcc130e7602839ccf..ec67f583593c3ed311d91c0895a1114c451f2de1 100644 (file)
@@ -31,10 +31,6 @@ const routes = [
   {
     path: 'unauthorized',
     component: lazyLoadComponent(() => import('./components/Unauthorized'))
-  },
-  {
-    path: 'email_already_exists',
-    component: lazyLoadComponent(() => import('./components/EmailAlreadyExists'))
   }
 ];
 
index 011e13f6cb744bbb88d8de7dcd8a2d15cd5f19be..2466985f22d1707cf61b3adf9d2aefb10bb147e0 100644 (file)
@@ -25,7 +25,6 @@ import org.sonar.api.platform.Server;
 import org.sonar.api.server.authentication.BaseIdentityProvider;
 import org.sonar.api.server.authentication.UserIdentity;
 import org.sonar.db.user.UserDto;
-import org.sonar.server.authentication.UserRegistration.ExistingEmailStrategy;
 import org.sonar.server.authentication.event.AuthenticationEvent.Source;
 import org.sonar.server.user.ThreadLocalUserSession;
 import org.sonar.server.user.UserSessionFactory;
@@ -84,7 +83,6 @@ public class BaseContextFactory {
           .setUserIdentity(userIdentity)
           .setProvider(identityProvider)
           .setSource(Source.external(identityProvider))
-          .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
           .build());
       jwtHttpHandler.generateToken(userDto, request, response);
       threadLocalUserSession.set(userSessionFactory.create(userDto));
index 85aa225f74611078de03d7e30f0c187a0e862380..4a1780fccdd8104d05d72e553443881986397ed0 100644 (file)
@@ -37,7 +37,6 @@ import org.sonar.api.server.authentication.UserIdentity;
 import org.sonar.api.utils.log.Logger;
 import org.sonar.api.utils.log.Loggers;
 import org.sonar.db.user.UserDto;
-import org.sonar.server.authentication.UserRegistration.ExistingEmailStrategy;
 import org.sonar.server.authentication.event.AuthenticationEvent;
 import org.sonar.server.authentication.event.AuthenticationEvent.Source;
 import org.sonar.server.authentication.event.AuthenticationException;
@@ -146,7 +145,6 @@ public class CredentialsExternalAuthentication implements Startable {
         .setUserIdentity(userIdentityBuilder.build())
         .setProvider(new ExternalIdentityProvider())
         .setSource(realmEventSource(method))
-        .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
         .build());
   }
 
index 2e367ecd799e1d676b1cf6fe5c2d9d175b7d195d..fdbcba6864e51f782523394f85941122e7af07b2 100644 (file)
@@ -42,7 +42,6 @@ import org.sonar.api.utils.log.Logger;
 import org.sonar.api.utils.log.Loggers;
 import org.sonar.db.user.UserDto;
 import org.sonar.process.ProcessProperties;
-import org.sonar.server.authentication.UserRegistration.ExistingEmailStrategy;
 import org.sonar.server.authentication.event.AuthenticationEvent;
 import org.sonar.server.authentication.event.AuthenticationEvent.Source;
 import org.sonar.server.authentication.event.AuthenticationException;
@@ -169,7 +168,6 @@ public class HttpHeadersAuthentication implements Startable {
         .setUserIdentity(userIdentityBuilder.build())
         .setProvider(new SsoIdentityProvider())
         .setSource(Source.sso())
-        .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
         .build());
   }
 
index 496c4bdaef4552cc772d6ca6e76c017e2ae6902a..33eadc4ccc922579721c009f749153738d706ebb 100644 (file)
@@ -31,12 +31,10 @@ import org.sonar.api.server.authentication.OAuth2IdentityProvider;
 import org.sonar.api.server.authentication.UnauthorizedException;
 import org.sonar.server.authentication.event.AuthenticationEvent;
 import org.sonar.server.authentication.event.AuthenticationException;
-import org.sonar.server.authentication.exception.EmailAlreadyExistsRedirectionException;
 
 import static java.lang.String.format;
 import static org.sonar.server.authentication.AuthenticationError.handleAuthenticationError;
 import static org.sonar.server.authentication.AuthenticationError.handleError;
-import static org.sonar.server.authentication.AuthenticationRedirection.redirectTo;
 import static org.sonar.server.authentication.event.AuthenticationEvent.Source;
 
 public class InitFilter extends AuthenticationFilter {
@@ -83,10 +81,6 @@ public class InitFilter extends AuthenticationFilter {
       } else {
         handleError(request, response, format("Unsupported IdentityProvider class: %s", provider.getClass()));
       }
-    } catch (EmailAlreadyExistsRedirectionException e) {
-      oAuthOAuth2AuthenticationParameters.delete(request, response);
-      e.addCookie(request, response);
-      redirectTo(response, e.getPath(request.getContextPath()));
     } catch (AuthenticationException e) {
       oAuthOAuth2AuthenticationParameters.delete(request, response);
       authenticationEvent.loginFailure(request, e);
index 5575f5ec94d05b34a88ad5f287ce4af702673037..2dfdde2d9fa6c8da32a6a0b41a011beb79a24e3c 100644 (file)
@@ -37,10 +37,6 @@ public interface OAuth2AuthenticationParameters {
 
   Optional<String> getReturnTo(HttpServletRequest request);
 
-  Optional<Boolean> getAllowEmailShift(HttpServletRequest request);
-
-  Optional<Boolean> getAllowUpdateLogin(HttpServletRequest request);
-
   void delete(HttpServletRequest request, HttpServletResponse response);
 
 }
index b6ee5091abb6e6c8566eb93add03776f90a1f065..207d2736f27dfbbf13405a7bd2edff5c0f760755 100644 (file)
@@ -36,7 +36,6 @@ import javax.servlet.http.HttpServletResponse;
 import static java.net.URLDecoder.decode;
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.util.Optional.empty;
-import static org.apache.commons.lang.StringUtils.isNotBlank;
 import static org.sonar.server.authentication.AuthenticationRedirection.encodeMessage;
 import static org.sonar.server.authentication.Cookies.findCookie;
 import static org.sonar.server.authentication.Cookies.newCookieBuilder;
@@ -53,11 +52,6 @@ public class OAuth2AuthenticationParametersImpl implements OAuth2AuthenticationP
    */
   private static final String RETURN_TO_PARAMETER = "return_to";
 
-  /**
-   * This parameter is used to allow the shift of email from an existing user to the authenticating user
-   */
-  private static final String ALLOW_EMAIL_SHIFT_PARAMETER = "allowEmailShift";
-
   /**
    * This parameter is used to allow the update of login
    */
@@ -69,13 +63,9 @@ public class OAuth2AuthenticationParametersImpl implements OAuth2AuthenticationP
   @Override
   public void init(HttpServletRequest request, HttpServletResponse response) {
     String returnTo = request.getParameter(RETURN_TO_PARAMETER);
-    String allowEmailShift = request.getParameter(ALLOW_EMAIL_SHIFT_PARAMETER);
     Map<String, String> parameters = new HashMap<>();
     Optional<String> sanitizeRedirectUrl = sanitizeRedirectUrl(returnTo);
     sanitizeRedirectUrl.ifPresent(s -> parameters.put(RETURN_TO_PARAMETER, s));
-    if (isNotBlank(allowEmailShift)) {
-      parameters.put(ALLOW_EMAIL_SHIFT_PARAMETER, allowEmailShift);
-    }
     if (parameters.isEmpty()) {
       return;
     }
@@ -93,18 +83,6 @@ public class OAuth2AuthenticationParametersImpl implements OAuth2AuthenticationP
       .flatMap(OAuth2AuthenticationParametersImpl::sanitizeRedirectUrl);
   }
 
-  @Override
-  public Optional<Boolean> getAllowEmailShift(HttpServletRequest request) {
-    Optional<String> parameter = getParameter(request, ALLOW_EMAIL_SHIFT_PARAMETER);
-    return parameter.map(Boolean::parseBoolean);
-  }
-
-  @Override
-  public Optional<Boolean> getAllowUpdateLogin(HttpServletRequest request) {
-    Optional<String> parameter = getParameter(request, ALLOW_LOGIN_UPDATE_PARAMETER);
-    return parameter.map(Boolean::parseBoolean);
-  }
-
   private static Optional<String> getParameter(HttpServletRequest request, String parameterKey) {
     Optional<javax.servlet.http.Cookie> cookie = findCookie(AUTHENTICATION_COOKIE_NAME, request);
     if (!cookie.isPresent()) {
index e32e6425d2d8806a343921b59b41a864f6eb7118..25490db8aa855a0375cb484232c607edb11bf6e6 100644 (file)
@@ -30,13 +30,11 @@ import org.sonar.api.server.authentication.OAuth2IdentityProvider;
 import org.sonar.api.server.authentication.UnauthorizedException;
 import org.sonar.server.authentication.event.AuthenticationEvent;
 import org.sonar.server.authentication.event.AuthenticationException;
-import org.sonar.server.authentication.exception.EmailAlreadyExistsRedirectionException;
 import org.sonar.server.user.ThreadLocalUserSession;
 
 import static java.lang.String.format;
 import static org.sonar.server.authentication.AuthenticationError.handleAuthenticationError;
 import static org.sonar.server.authentication.AuthenticationError.handleError;
-import static org.sonar.server.authentication.AuthenticationRedirection.redirectTo;
 import static org.sonar.server.authentication.event.AuthenticationEvent.Source;
 
 public class OAuth2CallbackFilter extends AuthenticationFilter {
@@ -78,10 +76,6 @@ public class OAuth2CallbackFilter extends AuthenticationFilter {
       } else {
         handleError(request, response, format("Not an OAuth2IdentityProvider: %s", provider.getClass()));
       }
-    } catch (EmailAlreadyExistsRedirectionException e) {
-      oauth2Parameters.delete(request, response);
-      e.addCookie(request, response);
-      redirectTo(response, e.getPath(request.getContextPath()));
     } catch (AuthenticationException e) {
       oauth2Parameters.delete(request, response);
       authenticationEvent.loginFailure(request, e);
index 915257bb2d464e4c87a5efe3385426f48ad6b8ae..0299db307aef4cb290dd6a84a2183cae55d010ce 100644 (file)
@@ -30,7 +30,6 @@ import org.sonar.api.server.ServerSide;
 import org.sonar.api.server.authentication.OAuth2IdentityProvider;
 import org.sonar.api.server.authentication.UserIdentity;
 import org.sonar.db.user.UserDto;
-import org.sonar.server.authentication.UserRegistration.ExistingEmailStrategy;
 import org.sonar.server.authentication.event.AuthenticationEvent;
 import org.sonar.server.user.ThreadLocalUserSession;
 import org.sonar.server.user.UserSessionFactory;
@@ -137,13 +136,11 @@ public class OAuth2ContextFactory {
 
     @Override
     public void authenticate(UserIdentity userIdentity, @Nullable Set<String> organizationAlmIds) {
-      Boolean allowEmailShift = oAuthParameters.getAllowEmailShift(request).orElse(false);
       UserDto userDto = userRegistrar.register(
         UserRegistration.builder()
           .setUserIdentity(userIdentity)
           .setProvider(identityProvider)
           .setSource(AuthenticationEvent.Source.oauth2(identityProvider))
-          .setExistingEmailStrategy(allowEmailShift ? ExistingEmailStrategy.ALLOW : ExistingEmailStrategy.WARN)
           .setOrganizationAlmIds(organizationAlmIds)
           .build());
       jwtHttpHandler.generateToken(userDto, request, response);
index 5fd6d5b591953be1a7d8937e6f2ea1a2b0c4d841..e98100e99d714d4e55c24a650188204a67053f24 100644 (file)
@@ -40,9 +40,7 @@ import org.sonar.db.DbSession;
 import org.sonar.db.user.GroupDto;
 import org.sonar.db.user.UserDto;
 import org.sonar.db.user.UserGroupDto;
-import org.sonar.server.authentication.UserRegistration.ExistingEmailStrategy;
 import org.sonar.server.authentication.event.AuthenticationException;
-import org.sonar.server.authentication.exception.EmailAlreadyExistsRedirectionException;
 import org.sonar.server.user.ExternalIdentity;
 import org.sonar.server.user.NewUser;
 import org.sonar.server.user.UpdateUser;
@@ -136,19 +134,7 @@ public class UserRegistrarImpl implements UserRegistrar {
     if (existingUser == null || isSameUser(existingUser, authenticatorParameters)) {
       return Optional.empty();
     }
-    ExistingEmailStrategy existingEmailStrategy = authenticatorParameters.getExistingEmailStrategy();
-    switch (existingEmailStrategy) {
-      case ALLOW:
-        existingUser.setEmail(null);
-        dbClient.userDao().update(dbSession, existingUser);
-        return Optional.of(existingUser);
-      case WARN:
-        throw new EmailAlreadyExistsRedirectionException(email, existingUser, authenticatorParameters.getUserIdentity(), authenticatorParameters.getProvider());
-      case FORBID:
-        throw generateExistingEmailError(authenticatorParameters, email);
-      default:
-        throw new IllegalStateException(format("Unknown strategy %s", existingEmailStrategy));
-    }
+    throw generateExistingEmailError(authenticatorParameters, email);
   }
 
   private static boolean isSameUser(UserDto existingUser, UserRegistration authenticatorParameters) {
@@ -236,9 +222,10 @@ public class UserRegistrarImpl implements UserRegistrar {
       .setSource(authenticatorParameters.getSource())
       .setLogin(authenticatorParameters.getUserIdentity().getProviderLogin())
       .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))
+      .setPublicMessage(
+        "This account is already associated with another authentication method. "
+          + "Sign in using the current authentication method, "
+          + "or contact your administrator to transfer your account to a different authentication method.")
       .build();
   }
 
index cd3144532d76e3fa757970a29a5333fbbd830d0d..249316e5a7466f1639f2a4a18b402965fe49af8b 100644 (file)
@@ -30,35 +30,15 @@ import static java.util.Objects.requireNonNull;
 
 class UserRegistration {
 
-  /**
-   * Strategy to be executed when the email of the user is already used by another user
-   */
-  enum ExistingEmailStrategy {
-    /**
-     * Authentication is allowed, the email is moved from other user to current user
-     */
-    ALLOW,
-    /**
-     * Authentication process is stopped, the user is redirected to a page explaining that the email is already used
-     */
-    WARN,
-    /**
-     * Forbid authentication of the user
-     */
-    FORBID
-  }
-
   private final UserIdentity userIdentity;
   private final IdentityProvider provider;
   private final AuthenticationEvent.Source source;
-  private final ExistingEmailStrategy existingEmailStrategy;
   private final Set<String> organizationAlmIds;
 
   UserRegistration(Builder builder) {
     this.userIdentity = builder.userIdentity;
     this.provider = builder.provider;
     this.source = builder.source;
-    this.existingEmailStrategy = builder.existingEmailStrategy;
     this.organizationAlmIds = builder.organizationAlmIds;
   }
 
@@ -74,10 +54,6 @@ class UserRegistration {
     return source;
   }
 
-  public ExistingEmailStrategy getExistingEmailStrategy() {
-    return existingEmailStrategy;
-  }
-
   @CheckForNull
   public Set<String> getOrganizationAlmIds() {
     return organizationAlmIds;
@@ -91,7 +67,6 @@ class UserRegistration {
     private UserIdentity userIdentity;
     private IdentityProvider provider;
     private AuthenticationEvent.Source source;
-    private ExistingEmailStrategy existingEmailStrategy;
     private Set<String> organizationAlmIds;
 
     public Builder setUserIdentity(UserIdentity userIdentity) {
@@ -109,14 +84,6 @@ class UserRegistration {
       return this;
     }
 
-    /**
-     * Strategy to be executed when the email of the user is already used by another user
-     */
-    public Builder setExistingEmailStrategy(ExistingEmailStrategy existingEmailStrategy) {
-      this.existingEmailStrategy = existingEmailStrategy;
-      return this;
-    }
-
     /**
      * List of ALM organization the user is member of.
      * When set to null, it means that no organization membership synchronization should be done.
@@ -130,7 +97,6 @@ class UserRegistration {
       requireNonNull(userIdentity, "userIdentity must be set");
       requireNonNull(provider, "identityProvider must be set");
       requireNonNull(source, "Source must be set");
-      requireNonNull(existingEmailStrategy, "existingEmailStrategy must be set ");
       return new UserRegistration(this);
     }
   }
diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/exception/EmailAlreadyExistsRedirectionException.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/exception/EmailAlreadyExistsRedirectionException.java
deleted file mode 100644 (file)
index 4a14541..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2021 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.sonar.server.authentication.exception;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.sonar.api.server.authentication.IdentityProvider;
-import org.sonar.api.server.authentication.UserIdentity;
-import org.sonar.db.user.UserDto;
-
-import static org.sonar.server.authentication.AuthenticationError.addErrorCookie;
-
-/**
- * This exception is used to redirect the user to a page explaining him that his email is already used by another account,
- * and where he has the ability to authenticate by "steeling" this email.
- */
-public class EmailAlreadyExistsRedirectionException extends RedirectionException {
-
-  private static final String PATH = "/sessions/email_already_exists";
-  private static final String EMAIL_FIELD = "email";
-  private static final String LOGIN_FIELD = "login";
-  private static final String PROVIDER_FIELD = "provider";
-  private static final String EXISTING_LOGIN_FIELD = "existingLogin";
-  private static final String EXISTING_PROVIDER_FIELD = "existingProvider";
-
-  private final String email;
-  private final UserDto existingUser;
-  private final UserIdentity userIdentity;
-  private final IdentityProvider provider;
-
-  public EmailAlreadyExistsRedirectionException(String email, UserDto existingUser, UserIdentity userIdentity, IdentityProvider provider) {
-    this.email = email;
-    this.existingUser = existingUser;
-    this.userIdentity = userIdentity;
-    this.provider = provider;
-  }
-
-  public void addCookie(HttpServletRequest request, HttpServletResponse response) {
-    Gson gson = new GsonBuilder().create();
-    String message = gson.toJson(ImmutableMap.of(
-      EMAIL_FIELD, email,
-      LOGIN_FIELD, userIdentity.getProviderLogin(),
-      PROVIDER_FIELD, provider.getKey(),
-      EXISTING_LOGIN_FIELD, existingUser.getExternalLogin(),
-      EXISTING_PROVIDER_FIELD, existingUser.getExternalIdentityProvider()));
-    addErrorCookie(request, response, message);
-  }
-
-  @Override
-  public String getPath(String contextPath) {
-    return contextPath + PATH;
-  }
-}
index d336ff32970a540838f3306ae0e4add90c239ee7..61074bea7682b1e714bb276004a3f311fc4ccb68 100644 (file)
@@ -44,7 +44,6 @@ import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
-import static org.sonar.server.authentication.UserRegistration.ExistingEmailStrategy.FORBID;
 import static org.sonar.server.authentication.event.AuthenticationEvent.Method.BASIC;
 import static org.sonar.server.authentication.event.AuthenticationEvent.Method.BASIC_TOKEN;
 import static org.sonar.server.authentication.event.AuthenticationExceptionMatcher.authenticationException;
@@ -91,7 +90,6 @@ public class CredentialsExternalAuthenticationTest {
     underTest.authenticate(new Credentials(LOGIN, PASSWORD), request, BASIC);
 
     assertThat(userIdentityAuthenticator.isAuthenticated()).isTrue();
-    assertThat(userIdentityAuthenticator.getAuthenticatorParameters().getExistingEmailStrategy()).isEqualTo(FORBID);
     assertThat(userIdentityAuthenticator.getAuthenticatorParameters().getUserIdentity().getProviderLogin()).isEqualTo(LOGIN);
     assertThat(userIdentityAuthenticator.getAuthenticatorParameters().getUserIdentity().getProviderId()).isNull();
     assertThat(userIdentityAuthenticator.getAuthenticatorParameters().getUserIdentity().getName()).isEqualTo("name");
index b65f067eebb34d7660c426ff15c31e135727a450..1a450e5f9e46785055e3aa8d551e7ca4440dde1b 100644 (file)
@@ -33,13 +33,10 @@ import org.sonar.api.server.authentication.Display;
 import org.sonar.api.server.authentication.IdentityProvider;
 import org.sonar.api.server.authentication.OAuth2IdentityProvider;
 import org.sonar.api.server.authentication.UnauthorizedException;
-import org.sonar.api.server.authentication.UserIdentity;
 import org.sonar.api.utils.log.LogTester;
 import org.sonar.api.utils.log.LoggerLevel;
-import org.sonar.db.user.UserDto;
 import org.sonar.server.authentication.event.AuthenticationEvent;
 import org.sonar.server.authentication.event.AuthenticationException;
-import org.sonar.server.authentication.exception.EmailAlreadyExistsRedirectionException;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.ArgumentMatchers.eq;
@@ -48,7 +45,6 @@ import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
-import static org.sonar.db.user.UserTesting.newUserDto;
 
 public class InitFilterTest {
 
@@ -225,27 +221,6 @@ public class InitFilterTest {
     verify(response).sendRedirect("/sonarqube/sessions/unauthorized");
   }
 
-  @Test
-  public void redirect_contains_cookie_when_failing_because_of_EmailAlreadyExistException() throws Exception {
-    UserDto existingUser = newUserDto().setEmail("john@email.com").setExternalLogin("john.bitbucket").setExternalIdentityProvider("bitbucket");
-    FailWithEmailAlreadyExistException identityProvider = new FailWithEmailAlreadyExistException("failing", existingUser);
-    when(request.getRequestURI()).thenReturn("/sessions/init/" + identityProvider.getKey());
-    identityProviderRepository.addIdentityProvider(identityProvider);
-
-    underTest.doFilter(request, response, chain);
-
-    verify(response).sendRedirect("/sessions/email_already_exists");
-    verify(auth2AuthenticationParameters).delete(eq(request), eq(response));
-    verify(response).addCookie(cookieArgumentCaptor.capture());
-    Cookie cookie = cookieArgumentCaptor.getValue();
-    assertThat(cookie.getName()).isEqualTo("AUTHENTICATION-ERROR");
-    assertThat(cookie.getValue()).contains("john%40email.com");
-    assertThat(cookie.getPath()).isEqualTo("/");
-    assertThat(cookie.isHttpOnly()).isFalse();
-    assertThat(cookie.getMaxAge()).isEqualTo(300);
-    assertThat(cookie.getSecure()).isFalse();
-  }
-
   @Test
   public void redirect_when_failing_because_of_Exception() throws Exception {
     IdentityProvider identityProvider = new FailWithIllegalStateException("failing");
@@ -315,25 +290,6 @@ public class InitFilterTest {
     }
   }
 
-  private static class FailWithEmailAlreadyExistException extends FakeBasicIdentityProvider {
-
-    private final UserDto existingUser;
-
-    public FailWithEmailAlreadyExistException(String key, UserDto existingUser) {
-      super(key, true);
-      this.existingUser = existingUser;
-    }
-
-    @Override
-    public void init(Context context) {
-      throw new EmailAlreadyExistsRedirectionException(existingUser.getEmail(), existingUser, UserIdentity.builder()
-        .setProviderLogin("john.github")
-        .setName(existingUser.getName())
-        .setEmail(existingUser.getEmail())
-        .build(), this);
-    }
-  }
-
   private static class UnsupportedIdentityProvider implements IdentityProvider {
     private final String unsupportedKey;
 
index aae20a5c38f04d8e46c5eb64eb384c2a7c5ea22d..7ca012795bd2067569f1421adb7b6c7ab7c8c4bf 100644 (file)
@@ -105,7 +105,7 @@ public class OAuth2AuthenticationParametersImplTest {
 
   @Test
   public void get_return_to_parameter() {
-    when(request.getCookies()).thenReturn(new Cookie[]{new Cookie(AUTHENTICATION_COOKIE_NAME, "{\"return_to\":\"/settings\"}")});
+    when(request.getCookies()).thenReturn(new Cookie[] {new Cookie(AUTHENTICATION_COOKIE_NAME, "{\"return_to\":\"/settings\"}")});
 
     Optional<String> redirection = underTest.getReturnTo(request);
 
@@ -114,7 +114,7 @@ public class OAuth2AuthenticationParametersImplTest {
 
   @Test
   public void get_return_to_is_empty_when_no_cookie() {
-    when(request.getCookies()).thenReturn(new Cookie[]{});
+    when(request.getCookies()).thenReturn(new Cookie[] {});
 
     Optional<String> redirection = underTest.getReturnTo(request);
 
@@ -123,62 +123,16 @@ public class OAuth2AuthenticationParametersImplTest {
 
   @Test
   public void get_return_to_is_empty_when_no_value() {
-    when(request.getCookies()).thenReturn(new Cookie[]{new Cookie(AUTHENTICATION_COOKIE_NAME, "{}")});
+    when(request.getCookies()).thenReturn(new Cookie[] {new Cookie(AUTHENTICATION_COOKIE_NAME, "{}")});
 
     Optional<String> redirection = underTest.getReturnTo(request);
 
     assertThat(redirection).isEmpty();
   }
 
-  @Test
-  public void get_allowEmailShift_parameter() {
-    when(request.getCookies()).thenReturn(new Cookie[]{new Cookie(AUTHENTICATION_COOKIE_NAME, "{\"allowEmailShift\":\"true\"}")});
-
-    Optional<Boolean> allowEmailShift = underTest.getAllowEmailShift(request);
-
-    assertThat(allowEmailShift).isNotEmpty();
-    assertThat(allowEmailShift.get()).isTrue();
-  }
-
-  @Test
-  public void get_allowEmailShift_is_empty_when_no_cookie() {
-    when(request.getCookies()).thenReturn(new Cookie[]{});
-
-    Optional<Boolean> allowEmailShift = underTest.getAllowEmailShift(request);
-
-    assertThat(allowEmailShift).isEmpty();
-  }
-
-  @Test
-  public void get_allowEmailShift_is_empty_when_no_value() {
-    when(request.getCookies()).thenReturn(new Cookie[]{new Cookie(AUTHENTICATION_COOKIE_NAME, "{}")});
-
-    Optional<Boolean> allowEmailShift = underTest.getAllowEmailShift(request);
-
-    assertThat(allowEmailShift).isEmpty();
-  }
-
-  @Test
-  public void getAllowUpdateLogin_is_empty_when_no_cookie() {
-    when(request.getCookies()).thenReturn(new Cookie[]{});
-
-    Optional<Boolean> allowLoginUpdate = underTest.getAllowUpdateLogin(request);
-
-    assertThat(allowLoginUpdate).isEmpty();
-  }
-
-  @Test
-  public void getAllowUpdateLogin_is_empty_when_no_value() {
-    when(request.getCookies()).thenReturn(new Cookie[]{new Cookie(AUTHENTICATION_COOKIE_NAME, "{}")});
-
-    Optional<Boolean> allowLoginUpdate = underTest.getAllowUpdateLogin(request);
-
-    assertThat(allowLoginUpdate).isEmpty();
-  }
-
   @Test
   public void delete() {
-    when(request.getCookies()).thenReturn(new Cookie[]{new Cookie(AUTHENTICATION_COOKIE_NAME, "{\"return_to\":\"/settings\"}")});
+    when(request.getCookies()).thenReturn(new Cookie[] {new Cookie(AUTHENTICATION_COOKIE_NAME, "{\"return_to\":\"/settings\"}")});
 
     underTest.delete(request, response);
 
index e693f0143c46dac72d7322dd35cf87aa3174f30d..6c45615d3bfdf7b87cf616ad4b436e09dbc1d315 100644 (file)
@@ -33,10 +33,8 @@ import org.sonar.api.server.authentication.UnauthorizedException;
 import org.sonar.api.server.authentication.UserIdentity;
 import org.sonar.api.utils.log.LogTester;
 import org.sonar.api.utils.log.LoggerLevel;
-import org.sonar.db.user.UserDto;
 import org.sonar.server.authentication.event.AuthenticationEvent;
 import org.sonar.server.authentication.event.AuthenticationException;
-import org.sonar.server.authentication.exception.EmailAlreadyExistsRedirectionException;
 import org.sonar.server.user.ThreadLocalUserSession;
 
 import static org.assertj.core.api.Assertions.assertThat;
@@ -45,7 +43,6 @@ import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
-import static org.sonar.db.user.UserTesting.newUserDto;
 import static org.sonar.server.authentication.event.AuthenticationEvent.Source;
 
 public class OAuth2CallbackFilterTest {
@@ -221,27 +218,6 @@ public class OAuth2CallbackFilterTest {
     verify(response).sendRedirect("/sonarqube/sessions/unauthorized");
   }
 
-  @Test
-  public void redirect_when_failing_because_of_EmailAlreadyExistException() throws Exception {
-    UserDto existingUser = newUserDto().setEmail("john@email.com").setExternalLogin("john.bitbucket").setExternalIdentityProvider("bitbucket");
-    FailWithEmailAlreadyExistException identityProvider = new FailWithEmailAlreadyExistException(existingUser);
-    when(request.getRequestURI()).thenReturn("/oauth2/callback/" + identityProvider.getKey());
-    identityProviderRepository.addIdentityProvider(identityProvider);
-
-    underTest.doFilter(request, response, chain);
-
-    verify(response).sendRedirect("/sessions/email_already_exists");
-    verify(oAuthRedirection).delete(eq(request), eq(response));
-    verify(response).addCookie(cookieArgumentCaptor.capture());
-    Cookie cookie = cookieArgumentCaptor.getValue();
-    assertThat(cookie.getName()).isEqualTo("AUTHENTICATION-ERROR");
-    assertThat(cookie.getValue()).contains("john%40email.com");
-    assertThat(cookie.getPath()).isEqualTo("/");
-    assertThat(cookie.isHttpOnly()).isFalse();
-    assertThat(cookie.getMaxAge()).isEqualTo(300);
-    assertThat(cookie.getSecure()).isFalse();
-  }
-
   @Test
   public void fail_when_no_oauth2_provider_provided() throws Exception {
     when(request.getRequestURI()).thenReturn("/oauth2/callback");
@@ -277,24 +253,6 @@ public class OAuth2CallbackFilterTest {
     }
   }
 
-  private static class FailWithEmailAlreadyExistException extends FailingIdentityProvider {
-
-    private final UserDto existingUser;
-
-    public FailWithEmailAlreadyExistException(UserDto existingUser) {
-      this.existingUser = existingUser;
-    }
-
-    @Override
-    public void callback(CallbackContext context) {
-      throw new EmailAlreadyExistsRedirectionException(existingUser.getEmail(), existingUser, UserIdentity.builder()
-        .setProviderLogin("john.github")
-        .setName(existingUser.getName())
-        .setEmail(existingUser.getEmail())
-        .build(), this);
-    }
-  }
-
   private static abstract class FailingIdentityProvider extends TestIdentityProvider implements OAuth2IdentityProvider {
     FailingIdentityProvider() {
       this.setKey("failing");
index 4241ed798a7f2e9140d617cb4557d3f893481fb0..88bb0e0b719be0bc587b4b7e441e75c5e6ca8264 100644 (file)
@@ -35,7 +35,6 @@ import org.sonar.api.server.authentication.OAuth2IdentityProvider;
 import org.sonar.api.server.authentication.UserIdentity;
 import org.sonar.db.user.UserDto;
 import org.sonar.server.authentication.OAuth2ContextFactory.OAuthContextImpl;
-import org.sonar.server.authentication.UserRegistration.ExistingEmailStrategy;
 import org.sonar.server.user.TestUserSessionFactory;
 import org.sonar.server.user.ThreadLocalUserSession;
 import org.sonar.server.user.UserSession;
@@ -139,28 +138,6 @@ public class OAuth2ContextFactoryTest {
     assertThat(userArgumentCaptor.getValue().getExternalIdentityProvider()).isEqualTo(PROVIDER_KEY);
   }
 
-  @Test
-  public void authenticate_with_allow_email_shift() {
-    when(oAuthParameters.getAllowEmailShift(request)).thenReturn(Optional.of(true));
-    OAuth2IdentityProvider.CallbackContext callback = newCallbackContext();
-
-    callback.authenticate(USER_IDENTITY);
-
-    assertThat(userIdentityAuthenticator.isAuthenticated()).isTrue();
-    assertThat(userIdentityAuthenticator.getAuthenticatorParameters().getExistingEmailStrategy()).isEqualTo(ExistingEmailStrategy.ALLOW);
-  }
-
-  @Test
-  public void authenticate_without_email_shift() {
-    when(oAuthParameters.getAllowEmailShift(request)).thenReturn(Optional.of(false));
-    OAuth2IdentityProvider.CallbackContext callback = newCallbackContext();
-
-    callback.authenticate(USER_IDENTITY);
-
-    assertThat(userIdentityAuthenticator.isAuthenticated()).isTrue();
-    assertThat(userIdentityAuthenticator.getAuthenticatorParameters().getExistingEmailStrategy()).isEqualTo(ExistingEmailStrategy.WARN);
-  }
-
   @Test
   public void authenticate_with_organization_alm_ids() {
     OAuthContextImpl callback = (OAuthContextImpl) newCallbackContext();
index 280f8cb5c6c4fb684d2afb7e230294a2f7f0c2d6..1ab2f29b6627211c1a7edc4883b795240a8032ae 100644 (file)
@@ -32,10 +32,8 @@ import org.sonar.core.util.stream.MoreCollectors;
 import org.sonar.db.DbTester;
 import org.sonar.db.user.GroupDto;
 import org.sonar.db.user.UserDto;
-import org.sonar.server.authentication.UserRegistration.ExistingEmailStrategy;
 import org.sonar.server.authentication.event.AuthenticationEvent;
 import org.sonar.server.authentication.event.AuthenticationEvent.Source;
-import org.sonar.server.authentication.exception.EmailAlreadyExistsRedirectionException;
 import org.sonar.server.es.EsTester;
 import org.sonar.server.user.NewUserNotifier;
 import org.sonar.server.user.UserUpdater;
@@ -47,7 +45,6 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
 import static org.sonar.db.user.UserTesting.newUserDto;
 import static org.sonar.process.ProcessProperties.Property.ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS;
-import static org.sonar.server.authentication.UserRegistration.ExistingEmailStrategy.FORBID;
 import static org.sonar.server.authentication.event.AuthenticationEvent.Method.BASIC;
 import static org.sonar.server.authentication.event.AuthenticationExceptionMatcher.authenticationException;
 
@@ -100,7 +97,6 @@ public class UserRegistrarImplTest {
       .setUserIdentity(USER_IDENTITY)
       .setProvider(IDENTITY_PROVIDER)
       .setSource(Source.realm(BASIC, IDENTITY_PROVIDER.getName()))
-      .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
       .build());
 
     UserDto user = db.users().selectUserByLogin(createdUser.getLogin()).get();
@@ -127,7 +123,6 @@ public class UserRegistrarImplTest {
       .setUserIdentity(USER_IDENTITY)
       .setProvider(sqIdentityProvider)
       .setSource(Source.realm(BASIC, sqIdentityProvider.getName()))
-      .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
       .build());
 
     UserDto user = db.users().selectUserByLogin(createdUser.getLogin()).get();
@@ -155,7 +150,6 @@ public class UserRegistrarImplTest {
         .build())
       .setProvider(IDENTITY_PROVIDER)
       .setSource(Source.realm(BASIC, IDENTITY_PROVIDER.getName()))
-      .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
       .build());
 
     UserDto user = db.getDbClient().userDao().selectByEmail(db.getSession(), "john@email.com").get(0);
@@ -199,7 +193,6 @@ public class UserRegistrarImplTest {
       .setUserIdentity(USER_IDENTITY)
       .setProvider(IDENTITY_PROVIDER)
       .setSource(Source.local(BASIC))
-      .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
       .build());
 
     assertThat(db.users().selectUserByLogin(user.getLogin()).get().isOnboarded()).isFalse();
@@ -213,7 +206,6 @@ public class UserRegistrarImplTest {
       .setUserIdentity(USER_IDENTITY)
       .setProvider(IDENTITY_PROVIDER)
       .setSource(Source.local(BASIC))
-      .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
       .build());
 
     assertThat(db.users().selectUserByLogin(user.getLogin()).get().isOnboarded()).isTrue();
@@ -231,7 +223,6 @@ public class UserRegistrarImplTest {
       .setUserIdentity(newUser)
       .setProvider(IDENTITY_PROVIDER)
       .setSource(Source.local(BASIC))
-      .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
       .build());
 
     assertThat(db.users().selectUserByLogin(user.getLogin()).get())
@@ -240,63 +231,21 @@ public class UserRegistrarImplTest {
   }
 
   @Test
-  public void authenticate_new_user_update_existing_user_email_when_strategy_is_ALLOW() {
-    UserDto existingUser = db.users().insertUser(u -> u.setEmail("john@email.com"));
-    UserIdentity newUser = UserIdentity.builder()
-      .setProviderLogin("johndoo")
-      .setName(existingUser.getName())
-      .setEmail(existingUser.getEmail())
-      .build();
-
-    UserDto user = underTest.register(UserRegistration.builder()
-      .setUserIdentity(newUser)
-      .setProvider(IDENTITY_PROVIDER)
-      .setSource(Source.local(BASIC))
-      .setExistingEmailStrategy(ExistingEmailStrategy.ALLOW)
-      .build());
-
-    UserDto newUserReloaded = db.users().selectUserByLogin(user.getLogin()).get();
-    assertThat(newUserReloaded.getEmail()).isEqualTo(existingUser.getEmail());
-    UserDto existingUserReloaded = db.users().selectUserByLogin(existingUser.getLogin()).get();
-    assertThat(existingUserReloaded.getEmail()).isNull();
-  }
-
-  @Test
-  public void throw_EmailAlreadyExistException_when_authenticating_new_user_when_email_already_exists_and_strategy_is_WARN() {
-    UserDto existingUser = db.users().insertUser(u -> u.setEmail("john@email.com"));
-    UserIdentity newUser = UserIdentity.builder()
-      .setProviderLogin("johndoo")
-      .setName(existingUser.getName())
-      .setEmail(existingUser.getEmail())
-      .build();
-
-    expectedException.expect(EmailAlreadyExistsRedirectionException.class);
-
-    underTest.register(UserRegistration.builder()
-      .setUserIdentity(newUser)
-      .setProvider(IDENTITY_PROVIDER)
-      .setSource(Source.local(BASIC))
-      .setExistingEmailStrategy(ExistingEmailStrategy.WARN)
-      .build());
-  }
-
-  @Test
-  public void throw_AuthenticationException_when_authenticating_new_user_when_email_already_exists_and_strategy_is_FORBID() {
+  public void authenticate_new_user_throws_AuthenticationException_when_when_email_already_exists() {
     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.getProviderLogin())
-      .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."));
+      .andPublicMessage("This account is already associated with another authentication method."
+        + " Sign in using the current authentication method,"
+        + " or contact your administrator to transfer your account to a different authentication method."));
     expectedException.expectMessage("Email 'john@email.com' is already used");
 
     underTest.register(UserRegistration.builder()
       .setUserIdentity(USER_IDENTITY)
       .setProvider(IDENTITY_PROVIDER)
       .setSource(source)
-      .setExistingEmailStrategy(FORBID)
-      .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
       .build());
   }
 
@@ -308,16 +257,15 @@ public class UserRegistrarImplTest {
 
     expectedException.expect(authenticationException().from(source)
       .withLogin(USER_IDENTITY.getProviderLogin())
-      .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."));
+      .andPublicMessage("This account is already associated with another authentication method."
+        + " Sign in using the current authentication method,"
+        + " or contact your administrator to transfer your account to a different authentication method."));
     expectedException.expectMessage("Email 'john@email.com' is already used");
 
     underTest.register(UserRegistration.builder()
       .setUserIdentity(USER_IDENTITY)
       .setProvider(IDENTITY_PROVIDER)
       .setSource(source)
-      .setExistingEmailStrategy(FORBID)
-      .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
       .build());
   }
 
@@ -337,7 +285,6 @@ public class UserRegistrarImplTest {
       .setUserIdentity(USER_IDENTITY)
       .setProvider(identityProvider)
       .setSource(source)
-      .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
       .build());
   }
 
@@ -354,7 +301,6 @@ public class UserRegistrarImplTest {
       .setUserIdentity(USER_IDENTITY)
       .setProvider(IDENTITY_PROVIDER)
       .setSource(Source.local(BASIC))
-      .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
       .build());
 
     assertThat(db.users().selectUserByLogin(user.getLogin()).get())
@@ -376,7 +322,6 @@ public class UserRegistrarImplTest {
       .setUserIdentity(USER_IDENTITY)
       .setProvider(IDENTITY_PROVIDER)
       .setSource(Source.local(BASIC))
-      .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
       .build());
 
     assertThat(db.getDbClient().userDao().selectByUuid(db.getSession(), user.getUuid()))
@@ -399,7 +344,6 @@ public class UserRegistrarImplTest {
       .setUserIdentity(USER_IDENTITY)
       .setProvider(IDENTITY_PROVIDER)
       .setSource(Source.local(BASIC))
-      .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
       .build());
 
     assertThat(db.getDbClient().userDao().selectByUuid(db.getSession(), user.getUuid()))
@@ -422,7 +366,6 @@ public class UserRegistrarImplTest {
       .setUserIdentity(USER_IDENTITY)
       .setProvider(IDENTITY_PROVIDER)
       .setSource(Source.local(BASIC))
-      .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
       .build());
 
     // no new user should be created
@@ -453,7 +396,6 @@ public class UserRegistrarImplTest {
         .build())
       .setProvider(IDENTITY_PROVIDER)
       .setSource(Source.local(BASIC))
-      .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
       .build());
 
     assertThat(db.getDbClient().userDao().selectByUuid(db.getSession(), user.getUuid()))
@@ -476,7 +418,6 @@ public class UserRegistrarImplTest {
         .build())
       .setProvider(IDENTITY_PROVIDER)
       .setSource(Source.local(BASIC))
-      .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
       .build());
 
     // No new user is created
@@ -499,7 +440,6 @@ public class UserRegistrarImplTest {
       .setUserIdentity(USER_IDENTITY)
       .setProvider(IDENTITY_PROVIDER)
       .setSource(Source.local(BASIC))
-      .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
       .build());
 
     assertThat(db.getDbClient().userDao().selectByUuid(db.getSession(), user.getUuid()))
@@ -522,7 +462,6 @@ public class UserRegistrarImplTest {
       .setUserIdentity(USER_IDENTITY)
       .setProvider(IDENTITY_PROVIDER)
       .setSource(Source.local(BASIC))
-      .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
       .build());
 
     UserDto userDto = db.users().selectUserByLogin(USER_LOGIN).get();
@@ -536,53 +475,7 @@ public class UserRegistrarImplTest {
   }
 
   @Test
-  public void authenticate_existing_user_when_email_already_exists_and_strategy_is_ALLOW() {
-    UserDto existingUser = db.users().insertUser(u -> u.setEmail("john@email.com"));
-    UserDto currentUser = db.users().insertUser(u -> u.setExternalLogin("johndoo").setExternalIdentityProvider(IDENTITY_PROVIDER.getKey()).setEmail(null));
-
-    UserIdentity userIdentity = UserIdentity.builder()
-      .setProviderLogin(currentUser.getExternalLogin())
-      .setName("John")
-      .setEmail("john@email.com")
-      .build();
-
-    currentUser = underTest.register(UserRegistration.builder()
-      .setUserIdentity(userIdentity)
-      .setProvider(IDENTITY_PROVIDER)
-      .setSource(Source.local(BASIC))
-      .setExistingEmailStrategy(ExistingEmailStrategy.ALLOW)
-      .build());
-
-    UserDto existingUserReloaded = db.users().selectUserByLogin(existingUser.getLogin()).get();
-    assertThat(existingUserReloaded.getEmail()).isNull();
-
-    UserDto currentUserReloaded = db.users().selectUserByLogin(currentUser.getLogin()).get();
-    assertThat(currentUserReloaded.getEmail()).isEqualTo("john@email.com");
-
-  }
-
-  @Test
-  public void throw_EmailAlreadyExistException_when_authenticating_existing_user_when_email_already_exists_and_strategy_is_WARN() {
-    UserDto existingUser = db.users().insertUser(u -> u.setEmail("john@email.com"));
-    UserDto currentUser = db.users().insertUser(u -> u.setEmail(null));
-    UserIdentity userIdentity = UserIdentity.builder()
-      .setProviderLogin("johndoo")
-      .setName("John")
-      .setEmail("john@email.com")
-      .build();
-
-    expectedException.expect(EmailAlreadyExistsRedirectionException.class);
-
-    underTest.register(UserRegistration.builder()
-      .setUserIdentity(userIdentity)
-      .setProvider(IDENTITY_PROVIDER)
-      .setSource(Source.local(BASIC))
-      .setExistingEmailStrategy(ExistingEmailStrategy.WARN)
-      .build());
-  }
-
-  @Test
-  public void throw_AuthenticationException_when_authenticating_existing_user_when_email_already_exists_and_strategy_is_FORBID() {
+  public void authenticating_existing_user_throws_AuthenticationException_when_email_already_exists() {
     UserDto existingUser = db.users().insertUser(u -> u.setEmail("john@email.com"));
     UserDto currentUser = db.users().insertUser(u -> u.setEmail(null));
     UserIdentity userIdentity = UserIdentity.builder()
@@ -593,20 +486,20 @@ public class UserRegistrarImplTest {
 
     expectedException.expect(authenticationException().from(Source.realm(AuthenticationEvent.Method.FORM, IDENTITY_PROVIDER.getName()))
       .withLogin(userIdentity.getProviderLogin())
-      .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."));
+      .andPublicMessage("This account is already associated with another authentication method."
+        + " Sign in using the current authentication method,"
+        + " or contact your administrator to transfer your account to a different authentication method."));
     expectedException.expectMessage("Email 'john@email.com' is already used");
 
     underTest.register(UserRegistration.builder()
       .setUserIdentity(userIdentity)
       .setProvider(IDENTITY_PROVIDER)
       .setSource(Source.realm(AuthenticationEvent.Method.FORM, IDENTITY_PROVIDER.getName()))
-      .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
       .build());
   }
 
   @Test
-  public void does_not_fail_to_authenticate_user_when_email_has_not_changed_and_strategy_is_FORBID() {
+  public void authenticate_existing_user_succeeds_when_email_has_not_changed() {
     UserDto currentUser = db.users().insertUser(u -> u.setEmail("john@email.com")
       .setExternalIdentityProvider(IDENTITY_PROVIDER.getKey()));
     UserIdentity userIdentity = UserIdentity.builder()
@@ -620,7 +513,6 @@ public class UserRegistrarImplTest {
       .setUserIdentity(userIdentity)
       .setProvider(IDENTITY_PROVIDER)
       .setSource(Source.local(BASIC))
-      .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
       .build());
 
     UserDto currentUserReloaded = db.users().selectUserByLogin(currentUser.getLogin()).get();
@@ -685,7 +577,6 @@ public class UserRegistrarImplTest {
         .build())
       .setProvider(IDENTITY_PROVIDER)
       .setSource(Source.local(BASIC))
-      .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
       .build());
   }
 
index 8c0bf019fd204261b775d58e9d214d6afb68a0a0..293c587cd1ee260787a22ace588ec5a8f4423139 100644 (file)
@@ -682,13 +682,6 @@ process.fail=Failed
 #------------------------------------------------------------------------------
 
 sessions.log_in=Log in
-sessions.email_already_exists.1=The email address {email} is already associated to this user account:
-sessions.email_already_exists.2=By clicking on "Continue" you will associate this email address to another user account:
-sessions.email_already_exists.3=This means the following:
-sessions.email_already_exists.4=Your email address will be erased from the first account.
-sessions.email_already_exists.5=You will no longer receive email notifications from this account.
-sessions.email_already_exists.6=Issues won't be automatically assigned to this account anymore.
-
 #------------------------------------------------------------------------------
 #
 # HOTSPOTS