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
|
to a change being abandoned. It is a `ChangeEmail`: see `ChangeSubject.vm` and
|
||||||
`ChangeFooter.vm`.
|
`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
|
=== ChangeFooter.vm
|
||||||
|
|
||||||
The `ChangeFooter.vm` template will determine the contents of the footer
|
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.Maps;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import com.google.common.io.BaseEncoding;
|
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.common.GpgKeyInfo;
|
||||||
import com.google.gerrit.extensions.restapi.BadRequestException;
|
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||||
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
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.reviewdb.server.ReviewDb;
|
||||||
import com.google.gerrit.server.GerritPersonIdent;
|
import com.google.gerrit.server.GerritPersonIdent;
|
||||||
import com.google.gerrit.server.account.AccountResource;
|
import com.google.gerrit.server.account.AccountResource;
|
||||||
|
import com.google.gerrit.server.mail.AddKeySender;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
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.CommitBuilder;
|
||||||
import org.eclipse.jgit.lib.PersonIdent;
|
import org.eclipse.jgit.lib.PersonIdent;
|
||||||
import org.eclipse.jgit.lib.RefUpdate;
|
import org.eclipse.jgit.lib.RefUpdate;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -71,20 +75,24 @@ public class PostGpgKeys implements RestModifyView<AccountResource, Input> {
|
|||||||
public List<String> delete;
|
public List<String> delete;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||||
private final Provider<PersonIdent> serverIdent;
|
private final Provider<PersonIdent> serverIdent;
|
||||||
private final Provider<ReviewDb> db;
|
private final Provider<ReviewDb> db;
|
||||||
private final Provider<PublicKeyStore> storeProvider;
|
private final Provider<PublicKeyStore> storeProvider;
|
||||||
private final PublicKeyChecker checker;
|
private final PublicKeyChecker checker;
|
||||||
|
private final AddKeySender.Factory addKeyFactory;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
PostGpgKeys(@GerritPersonIdent Provider<PersonIdent> serverIdent,
|
PostGpgKeys(@GerritPersonIdent Provider<PersonIdent> serverIdent,
|
||||||
Provider<ReviewDb> db,
|
Provider<ReviewDb> db,
|
||||||
Provider<PublicKeyStore> storeProvider,
|
Provider<PublicKeyStore> storeProvider,
|
||||||
PublicKeyChecker checker) {
|
PublicKeyChecker checker,
|
||||||
|
AddKeySender.Factory addKeyFactory) {
|
||||||
this.serverIdent = serverIdent;
|
this.serverIdent = serverIdent;
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.storeProvider = storeProvider;
|
this.storeProvider = storeProvider;
|
||||||
this.checker = checker;
|
this.checker = checker;
|
||||||
|
this.addKeyFactory = addKeyFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -180,6 +188,7 @@ public class PostGpgKeys implements RestModifyView<AccountResource, Input> {
|
|||||||
Set<Fingerprint> toRemove) throws BadRequestException,
|
Set<Fingerprint> toRemove) throws BadRequestException,
|
||||||
ResourceConflictException, PGPException, IOException {
|
ResourceConflictException, PGPException, IOException {
|
||||||
try (PublicKeyStore store = storeProvider.get()) {
|
try (PublicKeyStore store = storeProvider.get()) {
|
||||||
|
List<String> addedKeys = new ArrayList<>();
|
||||||
for (PGPPublicKeyRing keyRing : keyRings) {
|
for (PGPPublicKeyRing keyRing : keyRings) {
|
||||||
PGPPublicKey key = keyRing.getPublicKey();
|
PGPPublicKey key = keyRing.getPublicKey();
|
||||||
CheckResult result = checker.check(key);
|
CheckResult result = checker.check(key);
|
||||||
@ -188,6 +197,7 @@ public class PostGpgKeys implements RestModifyView<AccountResource, Input> {
|
|||||||
"Problems with public key %s:\n%s",
|
"Problems with public key %s:\n%s",
|
||||||
keyToString(key), Joiner.on('\n').join(result.getProblems())));
|
keyToString(key), Joiner.on('\n').join(result.getProblems())));
|
||||||
}
|
}
|
||||||
|
addedKeys.add(PublicKeyStore.keyToString(key));
|
||||||
store.add(keyRing);
|
store.add(keyRing);
|
||||||
}
|
}
|
||||||
for (Fingerprint fp : toRemove) {
|
for (Fingerprint fp : toRemove) {
|
||||||
@ -204,6 +214,13 @@ public class PostGpgKeys implements RestModifyView<AccountResource, Input> {
|
|||||||
case NEW:
|
case NEW:
|
||||||
case FAST_FORWARD:
|
case FAST_FORWARD:
|
||||||
case FORCED:
|
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:
|
case NO_CHANGE:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -100,6 +100,7 @@ public class SitePathInitializer {
|
|||||||
chmod(0700, site.tmp_dir);
|
chmod(0700, site.tmp_dir);
|
||||||
|
|
||||||
extractMailExample("Abandoned.vm");
|
extractMailExample("Abandoned.vm");
|
||||||
|
extractMailExample("AddKey.vm");
|
||||||
extractMailExample("ChangeFooter.vm");
|
extractMailExample("ChangeFooter.vm");
|
||||||
extractMailExample("ChangeSubject.vm");
|
extractMailExample("ChangeSubject.vm");
|
||||||
extractMailExample("Comment.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.collect.Iterables;
|
||||||
import com.google.common.io.ByteSource;
|
import com.google.common.io.ByteSource;
|
||||||
|
import com.google.gerrit.common.errors.EmailException;
|
||||||
import com.google.gerrit.common.errors.InvalidSshKeyException;
|
import com.google.gerrit.common.errors.InvalidSshKeyException;
|
||||||
import com.google.gerrit.extensions.restapi.AuthException;
|
import com.google.gerrit.extensions.restapi.AuthException;
|
||||||
import com.google.gerrit.extensions.restapi.BadRequestException;
|
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.IdentifiedUser;
|
||||||
import com.google.gerrit.server.account.AddSshKey.Input;
|
import com.google.gerrit.server.account.AddSshKey.Input;
|
||||||
import com.google.gerrit.server.account.GetSshKeys.SshKeyInfo;
|
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.gerrit.server.ssh.SshKeyCache;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.gwtorm.server.ResultSet;
|
import com.google.gwtorm.server.ResultSet;
|
||||||
@ -37,12 +39,17 @@ import com.google.inject.Inject;
|
|||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class AddSshKey implements RestModifyView<AccountResource, Input> {
|
public class AddSshKey implements RestModifyView<AccountResource, Input> {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(AddSshKey.class);
|
||||||
|
|
||||||
public static class Input {
|
public static class Input {
|
||||||
public RawInput raw;
|
public RawInput raw;
|
||||||
}
|
}
|
||||||
@ -50,13 +57,15 @@ public class AddSshKey implements RestModifyView<AccountResource, Input> {
|
|||||||
private final Provider<CurrentUser> self;
|
private final Provider<CurrentUser> self;
|
||||||
private final Provider<ReviewDb> dbProvider;
|
private final Provider<ReviewDb> dbProvider;
|
||||||
private final SshKeyCache sshKeyCache;
|
private final SshKeyCache sshKeyCache;
|
||||||
|
private final AddKeySender.Factory addKeyFactory;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
AddSshKey(Provider<CurrentUser> self, Provider<ReviewDb> dbProvider,
|
AddSshKey(Provider<CurrentUser> self, Provider<ReviewDb> dbProvider,
|
||||||
SshKeyCache sshKeyCache) {
|
SshKeyCache sshKeyCache, AddKeySender.Factory addKeyFactory) {
|
||||||
this.self = self;
|
this.self = self;
|
||||||
this.dbProvider = dbProvider;
|
this.dbProvider = dbProvider;
|
||||||
this.sshKeyCache = sshKeyCache;
|
this.sshKeyCache = sshKeyCache;
|
||||||
|
this.addKeyFactory = addKeyFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -96,6 +105,12 @@ public class AddSshKey implements RestModifyView<AccountResource, Input> {
|
|||||||
sshKeyCache.create(new AccountSshKey.Id(
|
sshKeyCache.create(new AccountSshKey.Id(
|
||||||
user.getAccountId(), max + 1), sshPublicKey);
|
user.getAccountId(), max + 1), sshPublicKey);
|
||||||
dbProvider.get().accountSshKeys().insert(Collections.singleton(sshKey));
|
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());
|
sshKeyCache.evict(user.getUserName());
|
||||||
return Response.<SshKeyInfo>created(new SshKeyInfo(sshKey));
|
return Response.<SshKeyInfo>created(new SshKeyInfo(sshKey));
|
||||||
} catch (InvalidSshKeyException e) {
|
} 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.group.GroupModule;
|
||||||
import com.google.gerrit.server.index.ReindexAfterUpdate;
|
import com.google.gerrit.server.index.ReindexAfterUpdate;
|
||||||
import com.google.gerrit.server.mail.AddReviewerSender;
|
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.CreateChangeSender;
|
||||||
import com.google.gerrit.server.mail.EmailModule;
|
import com.google.gerrit.server.mail.EmailModule;
|
||||||
import com.google.gerrit.server.mail.FromAddressGenerator;
|
import com.google.gerrit.server.mail.FromAddressGenerator;
|
||||||
@ -186,6 +187,7 @@ public class GerritGlobalModule extends FactoryModule {
|
|||||||
|
|
||||||
factory(AccountInfoCacheFactory.Factory.class);
|
factory(AccountInfoCacheFactory.Factory.class);
|
||||||
factory(AddReviewerSender.Factory.class);
|
factory(AddReviewerSender.Factory.class);
|
||||||
|
factory(AddKeySender.Factory.class);
|
||||||
factory(CapabilityControl.Factory.class);
|
factory(CapabilityControl.Factory.class);
|
||||||
factory(ChangeData.Factory.class);
|
factory(ChangeData.Factory.class);
|
||||||
factory(ChangeJson.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;
|
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) {
|
public String getNameEmailFor(Account.Id accountId) {
|
||||||
AccountState who = args.accountCache.get(accountId);
|
AccountState who = args.accountCache.get(accountId);
|
||||||
String name = who.getAccount().getFullName();
|
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() {
|
protected boolean shouldSendMessage() {
|
||||||
if (body.length() == 0) {
|
if (body.length() == 0) {
|
||||||
// If we have no message body, don't send.
|
// If we have no message body, don't send.
|
||||||
|
@ -58,22 +58,7 @@ public class RegisterNewEmailSender extends OutgoingEmail {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getUserNameEmail() {
|
public String getUserNameEmail() {
|
||||||
String name = user.getAccount().getFullName();
|
return getUserNameEmailFor(user.getAccountId());
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getEmailRegistrationToken() {
|
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
Block a user