summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorClark Boylan <clark.boylan@gmail.com>2021-07-14 08:57:38 -0700
committerClark Boylan <clark.boylan@gmail.com>2021-07-22 16:13:38 -0700
commitf53508e594aa5ef23d8fbbf4d8205f997876d531 (patch)
tree6010a0e992925d4e497ddbf71997b5b30fa31d9a
parentde44654afccc2fd4b5f2e263871d17e930adcb09 (diff)
Prevent removing e-mail associated with OpenId accounts
Implement a Realm for OpenId accounts and override accountBelongsToRealm(). The new accountBelongsToRealm() implementation properly reports back if an external ID record belongs to an OpenId record. Without this users can delete their email associated with their OpenId account which also accidentally deletes their OpenId external ID record. When they log in next Gerrit creates a new account for them orphaning their actual account. Instead Gerrit should prevent the user from removing the email address associated with their OpenId account. If the user wants to delete this email address they will need to first update the email address on the OpenId side then login to update the email in their OpenId external ID record. Once that is done they can safely remove the email address from their account without orphaning the account. Bug: Issue 14776 Change-Id: I5ae8bc8cf98b0829c68840e66418999a17e48d60
-rw-r--r--Documentation/config-accounts.txt9
-rw-r--r--java/com/google/gerrit/server/account/externalids/ExternalId.java9
-rw-r--r--java/com/google/gerrit/server/auth/openid/OpenIdRealm.java49
-rw-r--r--java/com/google/gerrit/server/config/AuthModule.java9
-rw-r--r--javatests/com/google/gerrit/server/auth/ldap/LdapRealmTest.java6
-rw-r--r--javatests/com/google/gerrit/server/auth/oauth/OAuthRealmTest.java6
-rw-r--r--javatests/com/google/gerrit/server/auth/openid/OpenIdRealmTest.java93
7 files changed, 179 insertions, 2 deletions
diff --git a/Documentation/config-accounts.txt b/Documentation/config-accounts.txt
index b4a5ceffba..67159f45b3 100644
--- a/Documentation/config-accounts.txt
+++ b/Documentation/config-accounts.txt
@@ -359,6 +359,15 @@ branch. However Gerrit rejects pushes if:
* hashed passwords of external IDs with scheme `username` cannot be
decoded
+Users can edit some external IDs via the user settings page or the
+REST API. Note that email addresses cannot be deleted if they are
+associated with the user's login credentials external ID, for
+example the email address associated with an OpenId or OAUTH external
+ID. If users wish to remove these email addresses from Gerrit they must
+first update the external authentication record in that system,
+log in to Gerrit, then Gerrit will update the external ID record with
+the new email address.
+
[[starred-changes]]
== Starred Changes
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalId.java b/java/com/google/gerrit/server/account/externalids/ExternalId.java
index 2d501ad37f..1d31da9eef 100644
--- a/java/com/google/gerrit/server/account/externalids/ExternalId.java
+++ b/java/com/google/gerrit/server/account/externalids/ExternalId.java
@@ -136,6 +136,15 @@ public abstract class ExternalId implements Serializable {
/** Scheme for external auth used during authentication, e.g. OAuth Token */
public static final String SCHEME_EXTERNAL = "external";
+ /** Scheme for http resources. OpenID in particular makes use of these external IDs. */
+ public static final String SCHEME_HTTP = "http";
+
+ /** Scheme for https resources. OpenID in particular makes use of these external IDs. */
+ public static final String SCHEME_HTTPS = "https";
+
+ /** Scheme for xri resources. OpenID in particular makes use of these external IDs. */
+ public static final String SCHEME_XRI = "xri";
+
@AutoValue
public abstract static class Key implements Serializable {
private static final long serialVersionUID = 1L;
diff --git a/java/com/google/gerrit/server/auth/openid/OpenIdRealm.java b/java/com/google/gerrit/server/auth/openid/OpenIdRealm.java
new file mode 100644
index 0000000000..4e1a1cec37
--- /dev/null
+++ b/java/com/google/gerrit/server/auth/openid/OpenIdRealm.java
@@ -0,0 +1,49 @@
+// Copyright (C) 2021 Open Infrastructure Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.auth.openid;
+
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_HTTP;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_HTTPS;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_XRI;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.gerrit.server.account.DefaultRealm;
+import com.google.gerrit.server.account.EmailExpander;
+import com.google.gerrit.server.account.Emails;
+import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.config.AuthConfig;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import java.util.Collection;
+
+@Singleton
+public class OpenIdRealm extends DefaultRealm {
+ @Inject
+ @VisibleForTesting
+ public OpenIdRealm(EmailExpander emailExpander, Provider<Emails> emails, AuthConfig authConfig) {
+ super(emailExpander, emails, authConfig);
+ }
+
+ @Override
+ public boolean accountBelongsToRealm(Collection<ExternalId> externalIds) {
+ for (ExternalId id : externalIds) {
+ if (id.isScheme(SCHEME_HTTP) || id.isScheme(SCHEME_HTTPS) || id.isScheme(SCHEME_XRI)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/java/com/google/gerrit/server/config/AuthModule.java b/java/com/google/gerrit/server/config/AuthModule.java
index 5b0f73d6c2..56f2e2aee3 100644
--- a/java/com/google/gerrit/server/config/AuthModule.java
+++ b/java/com/google/gerrit/server/config/AuthModule.java
@@ -22,6 +22,7 @@ import com.google.gerrit.server.auth.AuthBackend;
import com.google.gerrit.server.auth.InternalAuthBackend;
import com.google.gerrit.server.auth.ldap.LdapModule;
import com.google.gerrit.server.auth.oauth.OAuthRealm;
+import com.google.gerrit.server.auth.openid.OpenIdRealm;
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
@@ -50,10 +51,14 @@ public class AuthModule extends AbstractModule {
case CUSTOM_EXTENSION:
break;
- case DEVELOPMENT_BECOME_ANY_ACCOUNT:
- case HTTP:
case OPENID:
case OPENID_SSO:
+ bind(Realm.class).to(OpenIdRealm.class);
+ DynamicSet.bind(binder(), AuthBackend.class).to(InternalAuthBackend.class);
+ break;
+
+ case DEVELOPMENT_BECOME_ANY_ACCOUNT:
+ case HTTP:
default:
bind(Realm.class).to(DefaultRealm.class);
DynamicSet.bind(binder(), AuthBackend.class).to(InternalAuthBackend.class);
diff --git a/javatests/com/google/gerrit/server/auth/ldap/LdapRealmTest.java b/javatests/com/google/gerrit/server/auth/ldap/LdapRealmTest.java
index ba40d8c211..042222fdc3 100644
--- a/javatests/com/google/gerrit/server/auth/ldap/LdapRealmTest.java
+++ b/javatests/com/google/gerrit/server/auth/ldap/LdapRealmTest.java
@@ -16,8 +16,11 @@ package com.google.gerrit.server.auth.ldap;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_GERRIT;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_HTTP;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_HTTPS;
import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_MAILTO;
import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_XRI;
import static com.google.gerrit.server.auth.ldap.LdapModule.GROUP_CACHE;
import static com.google.gerrit.server.auth.ldap.LdapModule.GROUP_EXIST_CACHE;
import static com.google.gerrit.server.auth.ldap.LdapModule.PARENT_GROUPS_CACHE;
@@ -90,5 +93,8 @@ public final class LdapRealmTest {
assertThat(accountBelongsToRealm(SCHEME_USERNAME, "xxgerritxx")).isFalse();
assertThat(accountBelongsToRealm(SCHEME_MAILTO, "gerrit.foo@bar.com")).isFalse();
assertThat(accountBelongsToRealm(SCHEME_MAILTO, "bar.gerrit@bar.com")).isFalse();
+ assertThat(accountBelongsToRealm(SCHEME_HTTP, "example.org/test")).isFalse();
+ assertThat(accountBelongsToRealm(SCHEME_HTTPS, "example.org/test")).isFalse();
+ assertThat(accountBelongsToRealm(SCHEME_XRI, "example.org/test")).isFalse();
}
}
diff --git a/javatests/com/google/gerrit/server/auth/oauth/OAuthRealmTest.java b/javatests/com/google/gerrit/server/auth/oauth/OAuthRealmTest.java
index dc62a61017..8f7cc35b24 100644
--- a/javatests/com/google/gerrit/server/auth/oauth/OAuthRealmTest.java
+++ b/javatests/com/google/gerrit/server/auth/oauth/OAuthRealmTest.java
@@ -16,8 +16,11 @@ package com.google.gerrit.server.auth.oauth;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_EXTERNAL;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_HTTP;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_HTTPS;
import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_MAILTO;
import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_XRI;
import com.google.gerrit.entities.Account;
import com.google.gerrit.server.account.externalids.ExternalId;
@@ -65,5 +68,8 @@ public final class OAuthRealmTest {
assertThat(accountBelongsToRealm(SCHEME_USERNAME, "xxexternalxx")).isFalse();
assertThat(accountBelongsToRealm(SCHEME_MAILTO, "external.foo@bar.com")).isFalse();
assertThat(accountBelongsToRealm(SCHEME_MAILTO, "bar.external@bar.com")).isFalse();
+ assertThat(accountBelongsToRealm(SCHEME_HTTP, "example.org/test")).isFalse();
+ assertThat(accountBelongsToRealm(SCHEME_HTTPS, "example.org/test")).isFalse();
+ assertThat(accountBelongsToRealm(SCHEME_XRI, "example.org/test")).isFalse();
}
}
diff --git a/javatests/com/google/gerrit/server/auth/openid/OpenIdRealmTest.java b/javatests/com/google/gerrit/server/auth/openid/OpenIdRealmTest.java
new file mode 100644
index 0000000000..f5373add86
--- /dev/null
+++ b/javatests/com/google/gerrit/server/auth/openid/OpenIdRealmTest.java
@@ -0,0 +1,93 @@
+// Copyright (C) 2021 Open Infrastructure Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.auth.openid;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_EXTERNAL;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_GERRIT;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_HTTP;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_HTTPS;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_MAILTO;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_XRI;
+
+import com.google.gerrit.entities.Account;
+import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.testing.InMemoryModule;
+import com.google.inject.Guice;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import java.util.Arrays;
+import org.junit.Before;
+import org.junit.Test;
+
+public final class OpenIdRealmTest {
+ @Inject private OpenIdRealm openidRealm = null;
+
+ @Before
+ public void setUpInjector() throws Exception {
+ Injector injector = Guice.createInjector(new InMemoryModule());
+ injector.injectMembers(this);
+ }
+
+ private ExternalId id(String scheme, String id) {
+ return ExternalId.create(scheme, id, Account.id(1000));
+ }
+
+ private boolean accountBelongsToRealm(ExternalId... ids) {
+ return openidRealm.accountBelongsToRealm(Arrays.asList(ids));
+ }
+
+ private boolean accountBelongsToRealm(String scheme, String id) {
+ return accountBelongsToRealm(id(scheme, id));
+ }
+
+ @Test
+ public void accountBelongsToRealm() throws Exception {
+ assertThat(accountBelongsToRealm(SCHEME_HTTP, "example.org/test")).isTrue();
+ assertThat(accountBelongsToRealm(SCHEME_HTTPS, "example.org/test")).isTrue();
+ assertThat(accountBelongsToRealm(SCHEME_XRI, "example.org/test")).isTrue();
+ assertThat(
+ accountBelongsToRealm(id(SCHEME_USERNAME, "test"), id(SCHEME_HTTP, "example.org/test")))
+ .isTrue();
+ assertThat(
+ accountBelongsToRealm(
+ id(SCHEME_USERNAME, "test"), id(SCHEME_HTTPS, "example.org/test")))
+ .isTrue();
+ assertThat(
+ accountBelongsToRealm(id(SCHEME_USERNAME, "test"), id(SCHEME_XRI, "example.org/test")))
+ .isTrue();
+ assertThat(
+ accountBelongsToRealm(id(SCHEME_HTTP, "example.org/test"), id(SCHEME_USERNAME, "test")))
+ .isTrue();
+ assertThat(
+ accountBelongsToRealm(
+ id(SCHEME_HTTPS, "example.org/test"), id(SCHEME_USERNAME, "test")))
+ .isTrue();
+ assertThat(
+ accountBelongsToRealm(id(SCHEME_XRI, "test"), id(SCHEME_USERNAME, "example.org/test")))
+ .isTrue();
+
+ assertThat(accountBelongsToRealm(SCHEME_EXTERNAL, "test")).isFalse();
+ assertThat(accountBelongsToRealm(SCHEME_GERRIT, "test")).isFalse();
+ assertThat(accountBelongsToRealm(SCHEME_USERNAME, "test")).isFalse();
+ assertThat(accountBelongsToRealm(SCHEME_MAILTO, "foo@bar.com")).isFalse();
+
+ assertThat(accountBelongsToRealm(SCHEME_USERNAME, "gerrit")).isFalse();
+ assertThat(accountBelongsToRealm(SCHEME_USERNAME, "xxgerritxx")).isFalse();
+ assertThat(accountBelongsToRealm(SCHEME_MAILTO, "gerrit.foo@bar.com")).isFalse();
+ assertThat(accountBelongsToRealm(SCHEME_MAILTO, "bar.gerrit@bar.com")).isFalse();
+ }
+}