Add notification when adding keys
When adding SSH/GPG keys, add a notification to the user to inform them as an additional protection for their account should any credentials be compromised. Change-Id: Ia448af8da33dc0be3ba1acbb354ff3628630fe09
This commit is contained in:
parent
7539527136
commit
251b1574d2
|
@ -28,6 +28,12 @@ The `Abandoned.vm` template will determine the contents of the email related
|
|||
to a change being abandoned. It is a `ChangeEmail`: see `ChangeSubject.vm` and
|
||||
`ChangeFooter.vm`.
|
||||
|
||||
=== AddKey.vm
|
||||
|
||||
The `AddKey.vm` template will determine the contents of the email related to
|
||||
SSH and GPG keys being added to a user account. This notification is not sent
|
||||
when the key is administratively added to another user account.
|
||||
|
||||
=== ChangeFooter.vm
|
||||
|
||||
The `ChangeFooter.vm` template will determine the contents of the footer
|
||||
|
|
|
@ -27,6 +27,7 @@ import com.google.common.collect.Lists;
|
|||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import com.google.gerrit.common.errors.EmailException;
|
||||
import com.google.gerrit.extensions.common.GpgKeyInfo;
|
||||
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||
|
@ -41,6 +42,7 @@ import com.google.gerrit.reviewdb.client.AccountExternalId;
|
|||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.GerritPersonIdent;
|
||||
import com.google.gerrit.server.account.AccountResource;
|
||||
import com.google.gerrit.server.mail.AddKeySender;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
|
@ -54,6 +56,8 @@ import org.bouncycastle.openpgp.bc.BcPGPObjectFactory;
|
|||
import org.eclipse.jgit.lib.CommitBuilder;
|
||||
import org.eclipse.jgit.lib.PersonIdent;
|
||||
import org.eclipse.jgit.lib.RefUpdate;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
|
@ -71,20 +75,24 @@ public class PostGpgKeys implements RestModifyView<AccountResource, Input> {
|
|||
public List<String> delete;
|
||||
}
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
private final Provider<PersonIdent> serverIdent;
|
||||
private final Provider<ReviewDb> db;
|
||||
private final Provider<PublicKeyStore> storeProvider;
|
||||
private final PublicKeyChecker checker;
|
||||
private final AddKeySender.Factory addKeyFactory;
|
||||
|
||||
@Inject
|
||||
PostGpgKeys(@GerritPersonIdent Provider<PersonIdent> serverIdent,
|
||||
Provider<ReviewDb> db,
|
||||
Provider<PublicKeyStore> storeProvider,
|
||||
PublicKeyChecker checker) {
|
||||
PublicKeyChecker checker,
|
||||
AddKeySender.Factory addKeyFactory) {
|
||||
this.serverIdent = serverIdent;
|
||||
this.db = db;
|
||||
this.storeProvider = storeProvider;
|
||||
this.checker = checker;
|
||||
this.addKeyFactory = addKeyFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -180,6 +188,7 @@ public class PostGpgKeys implements RestModifyView<AccountResource, Input> {
|
|||
Set<Fingerprint> toRemove) throws BadRequestException,
|
||||
ResourceConflictException, PGPException, IOException {
|
||||
try (PublicKeyStore store = storeProvider.get()) {
|
||||
List<String> addedKeys = new ArrayList<>();
|
||||
for (PGPPublicKeyRing keyRing : keyRings) {
|
||||
PGPPublicKey key = keyRing.getPublicKey();
|
||||
CheckResult result = checker.check(key);
|
||||
|
@ -188,6 +197,7 @@ public class PostGpgKeys implements RestModifyView<AccountResource, Input> {
|
|||
"Problems with public key %s:\n%s",
|
||||
keyToString(key), Joiner.on('\n').join(result.getProblems())));
|
||||
}
|
||||
addedKeys.add(PublicKeyStore.keyToString(key));
|
||||
store.add(keyRing);
|
||||
}
|
||||
for (Fingerprint fp : toRemove) {
|
||||
|
@ -204,6 +214,13 @@ public class PostGpgKeys implements RestModifyView<AccountResource, Input> {
|
|||
case NEW:
|
||||
case FAST_FORWARD:
|
||||
case FORCED:
|
||||
try {
|
||||
addKeyFactory.create(rsrc.getUser(), addedKeys).send();
|
||||
} catch (EmailException e) {
|
||||
log.error("Cannot send GPG key added message to "
|
||||
+ rsrc.getUser().getAccount().getPreferredEmail(), e);
|
||||
}
|
||||
break;
|
||||
case NO_CHANGE:
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -100,6 +100,7 @@ public class SitePathInitializer {
|
|||
chmod(0700, site.tmp_dir);
|
||||
|
||||
extractMailExample("Abandoned.vm");
|
||||
extractMailExample("AddKey.vm");
|
||||
extractMailExample("ChangeFooter.vm");
|
||||
extractMailExample("ChangeSubject.vm");
|
||||
extractMailExample("Comment.vm");
|
||||
|
|
|
@ -18,6 +18,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
|
|||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.io.ByteSource;
|
||||
import com.google.gerrit.common.errors.EmailException;
|
||||
import com.google.gerrit.common.errors.InvalidSshKeyException;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||
|
@ -30,6 +31,7 @@ import com.google.gerrit.server.CurrentUser;
|
|||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.account.AddSshKey.Input;
|
||||
import com.google.gerrit.server.account.GetSshKeys.SshKeyInfo;
|
||||
import com.google.gerrit.server.mail.AddKeySender;
|
||||
import com.google.gerrit.server.ssh.SshKeyCache;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.gwtorm.server.ResultSet;
|
||||
|
@ -37,12 +39,17 @@ import com.google.inject.Inject;
|
|||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
|
||||
@Singleton
|
||||
public class AddSshKey implements RestModifyView<AccountResource, Input> {
|
||||
private static final Logger log = LoggerFactory.getLogger(AddSshKey.class);
|
||||
|
||||
public static class Input {
|
||||
public RawInput raw;
|
||||
}
|
||||
|
@ -50,13 +57,15 @@ public class AddSshKey implements RestModifyView<AccountResource, Input> {
|
|||
private final Provider<CurrentUser> self;
|
||||
private final Provider<ReviewDb> dbProvider;
|
||||
private final SshKeyCache sshKeyCache;
|
||||
private final AddKeySender.Factory addKeyFactory;
|
||||
|
||||
@Inject
|
||||
AddSshKey(Provider<CurrentUser> self, Provider<ReviewDb> dbProvider,
|
||||
SshKeyCache sshKeyCache) {
|
||||
SshKeyCache sshKeyCache, AddKeySender.Factory addKeyFactory) {
|
||||
this.self = self;
|
||||
this.dbProvider = dbProvider;
|
||||
this.sshKeyCache = sshKeyCache;
|
||||
this.addKeyFactory = addKeyFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -96,6 +105,12 @@ public class AddSshKey implements RestModifyView<AccountResource, Input> {
|
|||
sshKeyCache.create(new AccountSshKey.Id(
|
||||
user.getAccountId(), max + 1), sshPublicKey);
|
||||
dbProvider.get().accountSshKeys().insert(Collections.singleton(sshKey));
|
||||
try {
|
||||
addKeyFactory.create(user, sshKey).send();
|
||||
} catch (EmailException e) {
|
||||
log.error("Cannot send SSH key added message to "
|
||||
+ user.getAccount().getPreferredEmail(), e);
|
||||
}
|
||||
sshKeyCache.evict(user.getUserName());
|
||||
return Response.<SshKeyInfo>created(new SshKeyInfo(sshKey));
|
||||
} catch (InvalidSshKeyException e) {
|
||||
|
|
|
@ -95,6 +95,7 @@ import com.google.gerrit.server.git.validators.UploadValidators;
|
|||
import com.google.gerrit.server.group.GroupModule;
|
||||
import com.google.gerrit.server.index.ReindexAfterUpdate;
|
||||
import com.google.gerrit.server.mail.AddReviewerSender;
|
||||
import com.google.gerrit.server.mail.AddKeySender;
|
||||
import com.google.gerrit.server.mail.CreateChangeSender;
|
||||
import com.google.gerrit.server.mail.EmailModule;
|
||||
import com.google.gerrit.server.mail.FromAddressGenerator;
|
||||
|
@ -186,6 +187,7 @@ public class GerritGlobalModule extends FactoryModule {
|
|||
|
||||
factory(AccountInfoCacheFactory.Factory.class);
|
||||
factory(AddReviewerSender.Factory.class);
|
||||
factory(AddKeySender.Factory.class);
|
||||
factory(CapabilityControl.Factory.class);
|
||||
factory(ChangeData.Factory.class);
|
||||
factory(ChangeJson.Factory.class);
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
// Copyright (C) 2015 The Android Open Source Project
|
||||
//
|
||||
// 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.mail;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.gerrit.common.errors.EmailException;
|
||||
import com.google.gerrit.reviewdb.client.AccountSshKey;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
import com.google.inject.assistedinject.AssistedInject;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class AddKeySender extends OutgoingEmail {
|
||||
public interface Factory {
|
||||
public AddKeySender create(IdentifiedUser user, AccountSshKey sshKey);
|
||||
|
||||
public AddKeySender create(IdentifiedUser user, List<String> gpgKey);
|
||||
}
|
||||
|
||||
private final IdentifiedUser callingUser;
|
||||
private final IdentifiedUser user;
|
||||
private final AccountSshKey sshKey;
|
||||
private final List<String> gpgKeys;
|
||||
|
||||
@AssistedInject
|
||||
public AddKeySender(EmailArguments ea,
|
||||
IdentifiedUser callingUser,
|
||||
@Assisted IdentifiedUser user,
|
||||
@Assisted AccountSshKey sshKey) {
|
||||
super(ea, "addkey");
|
||||
this.callingUser = callingUser;
|
||||
this.user = user;
|
||||
this.sshKey = sshKey;
|
||||
this.gpgKeys = null;
|
||||
}
|
||||
|
||||
@AssistedInject
|
||||
public AddKeySender(EmailArguments ea,
|
||||
IdentifiedUser callingUser,
|
||||
@Assisted IdentifiedUser user,
|
||||
@Assisted List<String> gpgKeys) {
|
||||
super(ea, "addkey");
|
||||
this.callingUser = callingUser;
|
||||
this.user = user;
|
||||
this.sshKey = null;
|
||||
this.gpgKeys = gpgKeys;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() throws EmailException {
|
||||
super.init();
|
||||
setHeader("Subject",
|
||||
String.format("[Gerrit Code Review] New %s Keys Added", getKeyType()));
|
||||
add(RecipientType.TO, new Address(getEmail()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldSendMessage() {
|
||||
/*
|
||||
* Don't send an email if no keys are added, or an admin is adding a key to
|
||||
* a user.
|
||||
*/
|
||||
return (sshKey != null || gpgKeys.size() > 0) &&
|
||||
(user.equals(callingUser) ||
|
||||
!callingUser.getCapabilities().canAdministrateServer());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void format() throws EmailException {
|
||||
appendText(velocifyFile("AddKey.vm"));
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return user.getAccount().getPreferredEmail();
|
||||
}
|
||||
|
||||
public String getUserNameEmail() {
|
||||
return getUserNameEmailFor(user.getAccountId());
|
||||
}
|
||||
|
||||
public String getKeyType() {
|
||||
if (sshKey != null) {
|
||||
return "SSH";
|
||||
} else if (gpgKeys != null) {
|
||||
return "GPG";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
public String getSshKey() {
|
||||
return (sshKey != null) ? sshKey.getSshPublicKey() + "\n" : null;
|
||||
}
|
||||
|
||||
public String getGpgKeys() {
|
||||
if (gpgKeys != null) {
|
||||
return Joiner.on("\n").join(gpgKeys);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -268,6 +268,13 @@ public abstract class OutgoingEmail {
|
|||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the human readable name and email for an account;
|
||||
* if neither are available, returns the Anonymous Coward name.
|
||||
*
|
||||
* @param accountId user to fetch.
|
||||
* @return name/email of account, or Anonymous Coward if unset.
|
||||
*/
|
||||
public String getNameEmailFor(Account.Id accountId) {
|
||||
AccountState who = args.accountCache.get(accountId);
|
||||
String name = who.getAccount().getFullName();
|
||||
|
@ -286,6 +293,33 @@ public abstract class OutgoingEmail {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the human readable name and email for an account;
|
||||
* if both are unavailable, returns the username. If no
|
||||
* username is set, this function returns null.
|
||||
*
|
||||
* @param accountId user to fetch.
|
||||
* @return name/email of account, username, or null if unset.
|
||||
*/
|
||||
public String getUserNameEmailFor(Account.Id accountId) {
|
||||
AccountState who = args.accountCache.get(accountId);
|
||||
String name = who.getAccount().getFullName();
|
||||
String email = who.getAccount().getPreferredEmail();
|
||||
|
||||
if (name != null && email != null) {
|
||||
return name + " <" + email + ">";
|
||||
} else if (email != null) {
|
||||
return email;
|
||||
} else if (name != null) {
|
||||
return name;
|
||||
}
|
||||
String username = who.getUserName();
|
||||
if (username != null) {
|
||||
return username;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected boolean shouldSendMessage() {
|
||||
if (body.length() == 0) {
|
||||
// If we have no message body, don't send.
|
||||
|
|
|
@ -58,22 +58,7 @@ public class RegisterNewEmailSender extends OutgoingEmail {
|
|||
}
|
||||
|
||||
public String getUserNameEmail() {
|
||||
String name = user.getAccount().getFullName();
|
||||
String email = user.getAccount().getPreferredEmail();
|
||||
|
||||
if (name != null && email != null) {
|
||||
return name + " <" + email + ">";
|
||||
} else if (email != null) {
|
||||
return email;
|
||||
} else if (name != null) {
|
||||
return name;
|
||||
} else {
|
||||
String username = user.getUserName();
|
||||
if (username != null) {
|
||||
return username;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return getUserNameEmailFor(user.getAccountId());
|
||||
}
|
||||
|
||||
public String getEmailRegistrationToken() {
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
## Copyright (C) 2015 The Android Open Source Project
|
||||
##
|
||||
## 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.
|
||||
##
|
||||
##
|
||||
## Template Type:
|
||||
## -------------
|
||||
## This is a velocity mail template, see: http://velocity.apache.org and the
|
||||
## gerrit-docs:config-mail.txt for more info on modifying gerrit mail templates.
|
||||
##
|
||||
## Template File Names and extensions:
|
||||
## ----------------------------------
|
||||
## Gerrit will use templates ending in ".vm" but will ignore templates ending
|
||||
## in ".vm.example". If a .vm template does not exist, the default internal
|
||||
## gerrit template which is the same as the .vm.example will be used. If you
|
||||
## want to override the default template, copy the .vm.example file to a .vm
|
||||
## file and edit it appropriately.
|
||||
##
|
||||
## This Template:
|
||||
## --------------
|
||||
## The AddKey.vm template will determine the contents of the email
|
||||
## related to adding a new SSH or GPG key to an account.
|
||||
##
|
||||
One or more new ${email.keyType} keys have been added to Gerrit Code Review at ${email.gerritHost}:
|
||||
|
||||
#if($email.sshKey)
|
||||
$email.sshKey
|
||||
#elseif($email.gpgKeys)
|
||||
$email.gpgKeys
|
||||
#end
|
||||
|
||||
If this is not expected, please contact your Gerrit Administrators
|
||||
immediately.
|
||||
|
||||
You can also manage your ${email.keyType} keys by visiting
|
||||
#if($email.sshKey)
|
||||
$email.gerritUrl#/settings/ssh-keys
|
||||
#elseif($email.gpgKeys)
|
||||
$email.gerritUrl#/settings/gpg-keys
|
||||
#end
|
||||
#if($email.userNameEmail)
|
||||
(while signed in as $email.userNameEmail)
|
||||
#else
|
||||
(while signed in as $email.email)
|
||||
#end
|
||||
|
||||
If clicking the link above does not work, copy and paste the URL in a
|
||||
new browser window instead.
|
||||
|
||||
This is a send-only email address. Replies to this message will not
|
||||
be read or answered.
|
Loading…
Reference in New Issue