Remove contact store functionality
Class loading of PGP functionality has never worked out of the box, from which we can conclude that this feature is unused in the wild. PGP functionality has never been located in bcprov-*.jar, at least as long as the original library download configuration has existed.3bccd773
points to bcprov-jdk16-144.jar, which does not contain PGP classes: $ curl -sOL http://www.bouncycastle.org/download/bcprov-jdk16-144.jar && jar tf bcprov-jdk16-144.jar | grep -i pgp org/bouncycastle/crypto/modes/OpenPGPCFBBlockCipher.class org/bouncycastle/crypto/modes/PGPCFBBlockCipher.class Even before that commit, in44671f5c
, we were checking for the presence of PGPPublicKey.class in the havePGP() helper method. This functionality at one point was used by Google to implement CLA checking, but that used a different build system and so did not see the breakage caused by incorrect library download configuration. These days, Google does not even use the same contact store mechanism for googlesource.com; CLAs are managed using a different system. Also delete UI associated with storing contact information. Although it was possible to configure a CLA to prompt the user for contact information, looking at the logic in AccountSecurityImpl, this info was dropped on the floor unless a ContactStore was configured. As we know, this was never the case, so claiming to store encrypted contact information in the UI was basically a lie. Similarly, the contactFiledOn field in Account was only set in the same ContactStore-enabled codepath, so we can kill that as well. Change-Id: I497cd374566c7d56262dafeeb96e4612fee54e8f
This commit is contained in:
@@ -29,7 +29,6 @@ Contributor agreements are defined as contributor-agreement sections in
|
||||
====
|
||||
[contributor-agreement "Individual"]
|
||||
description = If you are going to be contributing code on your own, this is the one you want. You can sign this one online.
|
||||
requireContactInformation = true
|
||||
agreementUrl = static/cla_individual.html
|
||||
autoVerify = group CLA Accepted - Individual
|
||||
accepted = group CLA Accepted - Individual
|
||||
@@ -52,11 +51,6 @@ Commit the configuration change, and push it back:
|
||||
Short text describing the contributor agreement. This text will appear
|
||||
when the user selects an agreement.
|
||||
|
||||
[[contributor-agreement.name.requireContactInformation]]contributor-agreement.<name>.requireContactInformation::
|
||||
+
|
||||
True if the user must provide contact information when signing a
|
||||
contributor agreement. Default is false.
|
||||
|
||||
[[contributor-agreement.name.agreementUrl]]contributor-agreement.<name>.agreementUrl::
|
||||
+
|
||||
An absolute URL or a relative path to an HTML file containing the text
|
||||
|
@@ -1,213 +0,0 @@
|
||||
= Gerrit Code Review - Contact Information
|
||||
|
||||
To help ensure contributor privacy, but still support gathering of
|
||||
contributor agreements as necessary, Gerrit encrypts all offline
|
||||
contact information gathered from users. This data is shipped to
|
||||
another server, typically at a different location, to make it more
|
||||
difficult for an attacker to obtain.
|
||||
|
||||
This feature is optional. If the crypto APIs aren't installed
|
||||
and the `contactstore.url` setting in `gerrit.config` is not set,
|
||||
Gerrit will not collect contact information from users.
|
||||
|
||||
|
||||
== Setup
|
||||
|
||||
Ensure Bouncy Castle Crypto API is available in the web application's
|
||||
CLASSPATH (e.g. in `'JETTY_HOME'/lib/plus` for Jetty). Gerrit needs
|
||||
both `bcprov-jdk\*-*.jar` and `bcpg-jdk\*-*.jar` to be provided
|
||||
for the contact encryption to work.
|
||||
|
||||
* link:http://www.bouncycastle.org/latest_releases.html[Bouncy Castle Crypto API]
|
||||
|
||||
Ensure a proper JCE policy file is installed. By default most
|
||||
JRE installations forbid the use of a strong key, resulting in
|
||||
SecurityException messages when trying to encrypt the contact data.
|
||||
You need to obtain a strong JCE policy file and install it by hand.
|
||||
Look for the 'Unlimited Strength Jurisdiction Policy' download.
|
||||
|
||||
* link:http://java.sun.com/javase/downloads/index.jsp[Java SE Downloads]
|
||||
|
||||
Create a public/private key pair for contact data handling.
|
||||
Generate the keys on a protected system, where the resulting
|
||||
private key is unlikely to fall into the wrong hands.
|
||||
|
||||
====
|
||||
gpg --gen-key
|
||||
====
|
||||
|
||||
Select to use a `DSA and Elgamal` key type, as the public key will
|
||||
be used for data encryption.
|
||||
|
||||
The information chosen for name, email and comment fields can be
|
||||
anything reasonable which would identify the contact store of this
|
||||
Gerrit instance. It is probably a good idea to not use a real
|
||||
person's name here, but instead some sort of organizational role.
|
||||
The actual values chosen don't matter later, and are only to help
|
||||
document the purpose of the key.
|
||||
|
||||
Choose a fairly long expiration period, such as 20 years. For most
|
||||
Gerrit instances, contact data will be written once, and rarely,
|
||||
if ever, read back.
|
||||
|
||||
Export the public key for Gerrit to use during encryption. The
|
||||
public key must be stored in a file called `contact_information.pub`
|
||||
and reside inside of the `site_config` directory. Armoring it
|
||||
during export makes it easier to transport between systems, as
|
||||
you can easily copy-and-paste the text. Gerrit can read both the
|
||||
armored and unarmored formats.
|
||||
|
||||
====
|
||||
gpg --export --armor KEYEMAIL >$site_path/etc/contact_information.pub
|
||||
====
|
||||
|
||||
Consider storing the private key with some sort of key escrow
|
||||
service within your organization. Without the private key it
|
||||
is impossible to recover contact records.
|
||||
|
||||
Install a contact store implementation somewhere to receive
|
||||
the contact records. To be really paranoid, Gerrit always
|
||||
ships the data to another HTTP server, preferably over HTTPS.
|
||||
Existing open-source server implementations can be found in the
|
||||
gerrit-contactstore project.
|
||||
|
||||
* link:https://code.google.com/p/gerrit/source/checkout?repo=contactstore[gerrit-contactstore]
|
||||
|
||||
Configure `'$site_path'/etc/gerrit.config` with the contact store's
|
||||
URL (in `contactstore.url`), and if needed, APPSEC value (in
|
||||
`contactstore.appsec`):
|
||||
|
||||
====
|
||||
git config --file $site_path/etc/gerrit.config appsec.url https://...
|
||||
git config --file $site_path/etc/gerrit.config appsec.appsec sekret
|
||||
====
|
||||
|
||||
|
||||
== Contact Store Protocol
|
||||
|
||||
To implement a new contact store, the following details are useful.
|
||||
|
||||
Gerrit connects to the contact store by sending a standard
|
||||
`application/x-www-form-urlencoded` within an HTTP POST request
|
||||
sent to the store URL (the exact URL that is in contactstore.url)
|
||||
with the following form fields in the body:
|
||||
|
||||
* APPSEC
|
||||
+
|
||||
A shared secret "password" that should be known only to Gerrit
|
||||
and the contact store. The contact store should test this value to
|
||||
deter spamming of the contact store by outside parties. Gerrit reads
|
||||
this from contactstore.appsec.
|
||||
|
||||
* account_id
|
||||
+
|
||||
Unique account_id value from the Gerrit database for the account
|
||||
the contact information belongs to. Base 10 integer.
|
||||
|
||||
* email
|
||||
+
|
||||
Preferred email address of the account. May facilitate lookups in
|
||||
the contact store at a future date. May be omitted or the empty
|
||||
string if the user hasn't chosen a preferred email.
|
||||
|
||||
* filed
|
||||
+
|
||||
Seconds since the UNIX epoch of when the contact information
|
||||
was filed. May be omitted or the empty string if Gerrit
|
||||
doesn't think the supplied contact information is valid enough.
|
||||
|
||||
* data
|
||||
+
|
||||
Encrypted account data as an armored ASCII blob. This is usually
|
||||
several KB of text data as a single string, with embedded newlines
|
||||
to break the lines at about 70-75 characters per line. Data can
|
||||
be decoded using GnuPG with the correct private key.
|
||||
|
||||
Upon successful store, the contact store application should respond
|
||||
with HTTP status code `200` and a body consisting only of `OK`
|
||||
(or `OK\n`). Any other response code or body is considered to be
|
||||
a failure by Gerrit.
|
||||
|
||||
Using `https://` for the store URL is *highly* encouraged, as it
|
||||
prevents man-in-the-middle attacks from reading the shared secret
|
||||
APPSEC token, or messing with the data field.
|
||||
|
||||
=== Data Format
|
||||
|
||||
Once decrypted the `data` field looks something like the following:
|
||||
|
||||
----
|
||||
Account-Id: 1001240
|
||||
Date: 2009-02-23 20:32:32.852 UTC
|
||||
Full-Name: John Doe
|
||||
Preferred-Email: jdoe@example.com
|
||||
Identity: jd15@some-isp.com
|
||||
Identity: jdoe@example.com <http://jdoe.blogger.com/>
|
||||
Address:
|
||||
123 Any Street
|
||||
Any Town, Somewhere
|
||||
Country: USA
|
||||
Phone-Number: +1 (555) 555-1212
|
||||
Fax-Number: 555.1200
|
||||
----
|
||||
|
||||
The fields are as follows:
|
||||
|
||||
* `Account-Id`
|
||||
+
|
||||
Value of the `account_id` field in the metadata database. This is
|
||||
a unique key for this account, and links all data records to it.
|
||||
|
||||
* `Date`
|
||||
+
|
||||
Date and time of when this contact record was submitted by the user.
|
||||
Written in an ISO formatted date/time string (`YYYY-MM-DD hh:mm:ss`),
|
||||
in the UTC timezone.
|
||||
|
||||
* `Full-Name`
|
||||
+
|
||||
The `full_name` field of the account record when the user submitted
|
||||
the contact information. This should be the user's given name and
|
||||
family name.
|
||||
|
||||
* `Preferred-Email`
|
||||
+
|
||||
The `preferred_email` field of the account record when the user
|
||||
submitted the contact information. This should be one of the emails
|
||||
listed in the `Identity` field.
|
||||
|
||||
* `Identity`
|
||||
+
|
||||
This field occurs once for each `account_external_id` record
|
||||
in the database for this account. The email address is listed,
|
||||
and if the user is using OpenID authentication, the OpenID claimed
|
||||
identity follows in brackets (`<...>`). Identity lines without an
|
||||
OpenID identity are usually created by sending an email containing
|
||||
a unique hyperlink that the user must visit to setup the identity.
|
||||
|
||||
* `Address`
|
||||
+
|
||||
Free form text, as entered by the user. This should describe some
|
||||
location that physical documents could be sent to, but it is not
|
||||
verified, so users can enter pretty much anything here. Each line
|
||||
is prefixed with a single TAB character, but is otherwise exactly
|
||||
as entered.
|
||||
|
||||
* `Country`
|
||||
+
|
||||
Free form text, as entered by the user. This should be some sort
|
||||
of country name or ISO country abbreviation, but it is not verified,
|
||||
so it can be pretty much anything.
|
||||
|
||||
* `Phone-Number`, `Fax-Number`
|
||||
+
|
||||
Free form text, as entered by the user. The format here can be
|
||||
anything, and as the example shows, may not even be consistent in
|
||||
the same record.
|
||||
|
||||
GERRIT
|
||||
------
|
||||
Part of link:index.html[Gerrit Code Review]
|
||||
|
||||
SEARCHBOX
|
||||
---------
|
@@ -1128,22 +1128,6 @@ to anonymous users via the
|
||||
link:rest-api-projects.html#get-config[REST API].
|
||||
|
||||
|
||||
[[contactstore]]
|
||||
=== Section contactstore
|
||||
|
||||
[[contactstore.url]]contactstore.url::
|
||||
+
|
||||
URL of the web based contact store Gerrit will send any offline
|
||||
contact information to when it collects the data from users as part
|
||||
of a contributor agreement.
|
||||
+
|
||||
See link:config-contact.html[Contact Information].
|
||||
|
||||
[[contactstore.appsec]]contactstore.appsec::
|
||||
+
|
||||
Shared secret of the web based contact store.
|
||||
|
||||
|
||||
[[container]]
|
||||
=== Section container
|
||||
|
||||
|
@@ -53,7 +53,6 @@
|
||||
. link:cmd-index.html[Command Line Tools]
|
||||
. link:config-plugins.html#replication[Replication]
|
||||
. link:config-plugins.html[Plugins]
|
||||
. link:config-contact.html[User Contact Information]
|
||||
. link:config-reverseproxy.html[Reverse Proxy]
|
||||
. link:config-auto-site-initialization.html[Automatic Site Initialization on Startup]
|
||||
. link:pgm-index.html[Server Side Administrative Tools]
|
||||
|
@@ -1452,10 +1452,6 @@ AccountInfo]. In addition `AccountDetailInfo` has the following fields:
|
||||
|`registered_on` ||
|
||||
The link:rest-api.html#timestamp[timestamp] of when the account was
|
||||
registered.
|
||||
|`contact_filed_on` |optional|
|
||||
The link:rest-api.html#timestamp[timestamp] of when contact information
|
||||
for this account was filed. Not set if no contact information was
|
||||
filed.
|
||||
|=================================
|
||||
|
||||
[[account-info]]
|
||||
|
@@ -1095,18 +1095,6 @@ link:config-gerrit.html#change.submitWholeTopic[A configuration if
|
||||
the whole topic is submitted].
|
||||
|=============================
|
||||
|
||||
[[contact-store-info]]
|
||||
=== ContactStoreInfo
|
||||
The `ContactStoreInfo` entity contains information about the contact
|
||||
store.
|
||||
|
||||
[options="header",cols="1,6"]
|
||||
|=======================
|
||||
|Field Name |Description
|
||||
|`url` |
|
||||
The link:config-gerrit.html#contactstore.url[URL of the contact store].
|
||||
|=======================
|
||||
|
||||
[[download-info]]
|
||||
=== DownloadInfo
|
||||
The `DownloadInfo` entity contains information about supported download
|
||||
@@ -1363,9 +1351,6 @@ link:#auth-info[AuthInfo] entity.
|
||||
Information about the configuration from the
|
||||
link:config-gerrit.html#change[change] section as
|
||||
link:#change-config-info[ChangeConfigInfo] entity.
|
||||
|`contact_store` |optional|
|
||||
Information about the contact store configuration as
|
||||
link:#contact-store-info[ContactStoreInfo] entity.
|
||||
|`download` ||
|
||||
Information about the configured download options as
|
||||
link:#download-info[DownloadInfo] entity.
|
||||
|
@@ -32,6 +32,5 @@ public class GetAccountDetailIT extends AbstractDaemonTest {
|
||||
assertAccountInfo(admin, info);
|
||||
Account account = accountCache.get(admin.getId()).getAccount();
|
||||
assertThat(info.registeredOn).isEqualTo(account.getRegisteredOn());
|
||||
assertThat(info.contactFiledOn).isEqualTo(account.getContactFiledOn());
|
||||
}
|
||||
}
|
||||
|
@@ -94,9 +94,6 @@ public class ServerInfoIT extends AbstractDaemonTest {
|
||||
assertThat(i.change.replyLabel).isEqualTo("Vote\u2026");
|
||||
assertThat(i.change.updateDelay).isEqualTo(50);
|
||||
|
||||
// contactstore
|
||||
assertThat(i.contactStore).isNull();
|
||||
|
||||
// download
|
||||
assertThat(i.download.archives).containsExactly("tar", "tbz2", "tgz", "txz");
|
||||
assertThat(i.download.schemes).isEmpty();
|
||||
@@ -147,9 +144,6 @@ public class ServerInfoIT extends AbstractDaemonTest {
|
||||
assertThat(i.change.replyLabel).isEqualTo("Reply\u2026");
|
||||
assertThat(i.change.updateDelay).isEqualTo(30);
|
||||
|
||||
// contactstore
|
||||
assertThat(i.contactStore).isNull();
|
||||
|
||||
// download
|
||||
assertThat(i.download.archives).containsExactly("tar", "tbz2", "tgz", "txz");
|
||||
assertThat(i.download.schemes).isEmpty();
|
||||
|
@@ -18,7 +18,6 @@ import com.google.gerrit.common.audit.Audit;
|
||||
import com.google.gerrit.common.auth.SignInRequired;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.AccountExternalId;
|
||||
import com.google.gerrit.reviewdb.client.ContactInformation;
|
||||
import com.google.gwtjsonrpc.common.AsyncCallback;
|
||||
import com.google.gwtjsonrpc.common.RemoteJsonService;
|
||||
import com.google.gwtjsonrpc.common.RpcImpl;
|
||||
@@ -41,7 +40,7 @@ public interface AccountSecurity extends RemoteJsonService {
|
||||
@Audit
|
||||
@SignInRequired
|
||||
void updateContact(String fullName, String emailAddr,
|
||||
ContactInformation info, AsyncCallback<Account> callback);
|
||||
AsyncCallback<Account> callback);
|
||||
|
||||
@Audit
|
||||
@SignInRequired
|
||||
|
@@ -25,7 +25,6 @@ public class ContributorAgreement implements Comparable<ContributorAgreement> {
|
||||
protected String name;
|
||||
protected String description;
|
||||
protected List<PermissionRule> accepted;
|
||||
protected boolean requireContactInformation;
|
||||
protected GroupReference autoVerify;
|
||||
protected String agreementUrl;
|
||||
|
||||
@@ -63,14 +62,6 @@ public class ContributorAgreement implements Comparable<ContributorAgreement> {
|
||||
this.accepted = accepted;
|
||||
}
|
||||
|
||||
public boolean isRequireContactInformation() {
|
||||
return requireContactInformation;
|
||||
}
|
||||
|
||||
public void setRequireContactInformation(boolean requireContactInformation) {
|
||||
this.requireContactInformation = requireContactInformation;
|
||||
}
|
||||
|
||||
public GroupReference getAutoVerify() {
|
||||
return autoVerify;
|
||||
}
|
||||
@@ -101,7 +92,6 @@ public class ContributorAgreement implements Comparable<ContributorAgreement> {
|
||||
ContributorAgreement ca = new ContributorAgreement(name);
|
||||
ca.description = description;
|
||||
ca.accepted = Collections.emptyList();
|
||||
ca.requireContactInformation = requireContactInformation;
|
||||
if (autoVerify != null) {
|
||||
ca.autoVerify = new GroupReference();
|
||||
}
|
||||
|
@@ -1,30 +0,0 @@
|
||||
// Copyright (C) 2009 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.common.errors;
|
||||
|
||||
/** Error indicating the server cannot store contact information. */
|
||||
public class ContactInformationStoreException extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final String MESSAGE = "Cannot store contact information";
|
||||
|
||||
public ContactInformationStoreException() {
|
||||
super(MESSAGE);
|
||||
}
|
||||
|
||||
public ContactInformationStoreException(final Throwable why) {
|
||||
super(MESSAGE, why);
|
||||
}
|
||||
}
|
@@ -44,22 +44,6 @@ public class AccountInfo extends JavaScriptObject {
|
||||
private final native Timestamp _getRegisteredOn() /*-{ return this._cts; }-*/;
|
||||
private final native void _setRegisteredOn(Timestamp ts) /*-{ this._cts = ts; }-*/;
|
||||
|
||||
public final Timestamp contactFiledOn() {
|
||||
if (contactFiledOnRaw() != null) {
|
||||
Timestamp ts = _getContactFiledOn();
|
||||
if (ts == null) {
|
||||
ts = JavaSqlTimestamp_JsonSerializer.parseTimestamp(contactFiledOnRaw());
|
||||
_setContactFiledOn(ts);
|
||||
}
|
||||
return ts;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private final native String contactFiledOnRaw() /*-{ return this.contact_filed_on; }-*/;
|
||||
private final native Timestamp _getContactFiledOn() /*-{ return this._cts; }-*/;
|
||||
private final native void _setContactFiledOn(Timestamp ts) /*-{ this._cts = ts; }-*/;
|
||||
|
||||
/**
|
||||
* @return true if the server supplied avatar information about this account.
|
||||
* The information may be an empty list, indicating no avatars are
|
||||
|
@@ -25,7 +25,6 @@ import java.util.Map;
|
||||
public class ServerInfo extends JavaScriptObject {
|
||||
public final native AuthInfo auth() /*-{ return this.auth; }-*/;
|
||||
public final native ChangeConfigInfo change() /*-{ return this.change; }-*/;
|
||||
public final native ContactStoreInfo contactStore() /*-{ return this.contact_store; }-*/;
|
||||
public final native DownloadInfo download() /*-{ return this.download; }-*/;
|
||||
public final native GerritInfo gerrit() /*-{ return this.gerrit; }-*/;
|
||||
public final native GitwebInfo gitweb() /*-{ return this.gitweb; }-*/;
|
||||
@@ -47,10 +46,6 @@ public class ServerInfo extends JavaScriptObject {
|
||||
private final native NativeMap<NativeString> _urlAliases() /*-{ return this.url_aliases; }-*/;
|
||||
|
||||
|
||||
public final boolean hasContactStore() {
|
||||
return contactStore() != null;
|
||||
}
|
||||
|
||||
public final boolean hasSshd() {
|
||||
return sshd() != null;
|
||||
}
|
||||
@@ -71,13 +66,6 @@ public class ServerInfo extends JavaScriptObject {
|
||||
}
|
||||
}
|
||||
|
||||
public static class ContactStoreInfo extends JavaScriptObject {
|
||||
public final native String url() /*-{ return this.url; }-*/;
|
||||
|
||||
protected ContactStoreInfo() {
|
||||
}
|
||||
}
|
||||
|
||||
public static class PluginConfigInfo extends JavaScriptObject {
|
||||
public final native boolean hasAvatars() /*-{ return this.has_avatars || false; }-*/;
|
||||
|
||||
|
@@ -17,8 +17,6 @@ package com.google.gerrit.client;
|
||||
import com.google.gwt.resources.client.CssResource;
|
||||
|
||||
public interface GerritCss extends CssResource {
|
||||
String accountContactOnFile();
|
||||
String accountContactPrivacyDetails();
|
||||
String accountDashboard();
|
||||
String accountInfoBlock();
|
||||
String accountLinkPanel();
|
||||
|
@@ -124,11 +124,6 @@ public interface AccountConstants extends Constants {
|
||||
|
||||
String contactFieldFullName();
|
||||
String contactFieldEmail();
|
||||
String contactPrivacyDetailsHtml();
|
||||
String contactFieldAddress();
|
||||
String contactFieldCountry();
|
||||
String contactFieldPhone();
|
||||
String contactFieldFax();
|
||||
String buttonOpenRegisterNewEmail();
|
||||
String buttonSendRegisterNewEmail();
|
||||
String buttonCancel();
|
||||
|
@@ -128,18 +128,6 @@ watchedProjectColumnAbandonedChanges = Abandoned Changes
|
||||
|
||||
contactFieldFullName = Full Name
|
||||
contactFieldEmail = Preferred Email
|
||||
contactPrivacyDetailsHtml = \
|
||||
<b>The following offline contact information is stored encrypted.</b><br />\
|
||||
<br />\
|
||||
Contact information will only be made available to administrators if it is \
|
||||
necessary to reach you through non-email based communication. Received data \
|
||||
is stored encrypted with a strong public/private key pair algorithm, and \
|
||||
this site does not have the private key. Once saved, you will be unable to \
|
||||
retrieve previously stored contact details.
|
||||
contactFieldAddress = Mailing Address
|
||||
contactFieldCountry = Country
|
||||
contactFieldPhone = Phone Number
|
||||
contactFieldFax = Fax Number
|
||||
buttonOpenRegisterNewEmail = Register New Email ...
|
||||
buttonSendRegisterNewEmail = Register
|
||||
buttonCancel = Cancel
|
||||
|
@@ -16,12 +16,9 @@ package com.google.gerrit.client.account;
|
||||
|
||||
import com.google.gwt.i18n.client.Messages;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public interface AccountMessages extends Messages {
|
||||
String lines(short cnt);
|
||||
String rowsPerPage(short cnt);
|
||||
String changeScreenServerDefault(String d);
|
||||
String enterIAGREE(String iagree);
|
||||
String contactOnFile(Date lastDate);
|
||||
}
|
||||
|
@@ -1,7 +1,4 @@
|
||||
lines = {0} lines
|
||||
rowsPerPage = {0} rows per page
|
||||
|
||||
changeScreenServerDefault = Server Default ({0})
|
||||
|
||||
enterIAGREE = (enter {0} in the box to the left)
|
||||
contactOnFile = Contact information last updated on {0,date,medium} at {0,time,short}.
|
||||
|
@@ -1,130 +0,0 @@
|
||||
// Copyright (C) 2008 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.client.account;
|
||||
|
||||
import com.google.gerrit.client.Gerrit;
|
||||
import com.google.gerrit.client.info.AccountInfo;
|
||||
import com.google.gerrit.client.ui.OnEditEnabler;
|
||||
import com.google.gerrit.reviewdb.client.ContactInformation;
|
||||
import com.google.gwt.user.client.ui.Grid;
|
||||
import com.google.gwt.user.client.ui.HTML;
|
||||
import com.google.gwt.user.client.ui.Label;
|
||||
import com.google.gwtexpui.globalkey.client.NpTextArea;
|
||||
import com.google.gwtexpui.globalkey.client.NpTextBox;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Date;
|
||||
|
||||
class ContactPanelFull extends ContactPanelShort {
|
||||
private Label hasContact;
|
||||
private NpTextArea addressTxt;
|
||||
private NpTextBox countryTxt;
|
||||
private NpTextBox phoneTxt;
|
||||
private NpTextBox faxTxt;
|
||||
|
||||
@Override
|
||||
protected void onInitUI() {
|
||||
super.onInitUI();
|
||||
|
||||
addressTxt = new NpTextArea();
|
||||
addressTxt.setVisibleLines(4);
|
||||
addressTxt.setCharacterWidth(60);
|
||||
|
||||
countryTxt = new NpTextBox();
|
||||
countryTxt.setVisibleLength(40);
|
||||
countryTxt.setMaxLength(40);
|
||||
|
||||
phoneTxt = new NpTextBox();
|
||||
phoneTxt.setVisibleLength(30);
|
||||
phoneTxt.setMaxLength(30);
|
||||
|
||||
faxTxt = new NpTextBox();
|
||||
faxTxt.setVisibleLength(30);
|
||||
faxTxt.setMaxLength(30);
|
||||
|
||||
final Grid infoSecure = new Grid(4, 2);
|
||||
infoSecure.setStyleName(Gerrit.RESOURCES.css().infoBlock());
|
||||
infoSecure.addStyleName(Gerrit.RESOURCES.css().accountInfoBlock());
|
||||
|
||||
final HTML privhtml = new HTML(Util.C.contactPrivacyDetailsHtml());
|
||||
privhtml.setStyleName(Gerrit.RESOURCES.css().accountContactPrivacyDetails());
|
||||
|
||||
hasContact = new Label();
|
||||
hasContact.setStyleName(Gerrit.RESOURCES.css().accountContactOnFile());
|
||||
hasContact.setVisible(false);
|
||||
|
||||
if (Gerrit.info().hasContactStore()) {
|
||||
body.add(privhtml);
|
||||
body.add(hasContact);
|
||||
body.add(infoSecure);
|
||||
}
|
||||
|
||||
row(infoSecure, 0, Util.C.contactFieldAddress(), addressTxt);
|
||||
row(infoSecure, 1, Util.C.contactFieldCountry(), countryTxt);
|
||||
row(infoSecure, 2, Util.C.contactFieldPhone(), phoneTxt);
|
||||
row(infoSecure, 3, Util.C.contactFieldFax(), faxTxt);
|
||||
|
||||
infoSecure.getCellFormatter().addStyleName(0, 0, Gerrit.RESOURCES.css().topmost());
|
||||
infoSecure.getCellFormatter().addStyleName(0, 1, Gerrit.RESOURCES.css().topmost());
|
||||
infoSecure.getCellFormatter().addStyleName(3, 0, Gerrit.RESOURCES.css().bottomheader());
|
||||
|
||||
final OnEditEnabler sbl = new OnEditEnabler(save);
|
||||
sbl.listenTo(addressTxt);
|
||||
sbl.listenTo(countryTxt);
|
||||
sbl.listenTo(phoneTxt);
|
||||
sbl.listenTo(faxTxt);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void display(AccountInfo account) {
|
||||
super.display(account);
|
||||
displayHasContact(account);
|
||||
addressTxt.setText("");
|
||||
countryTxt.setText("");
|
||||
phoneTxt.setText("");
|
||||
faxTxt.setText("");
|
||||
}
|
||||
|
||||
private void displayHasContact(AccountInfo account) {
|
||||
if (account.contactFiledOn() != null) {
|
||||
Timestamp dt = account.contactFiledOn();
|
||||
hasContact.setText(Util.M.contactOnFile(new Date(dt.getTime())));
|
||||
hasContact.setVisible(true);
|
||||
} else {
|
||||
hasContact.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void onSaveSuccess(AccountInfo account) {
|
||||
super.onSaveSuccess(account);
|
||||
displayHasContact(account);
|
||||
}
|
||||
|
||||
@Override
|
||||
ContactInformation toContactInformation() {
|
||||
final ContactInformation info;
|
||||
if (Gerrit.info().hasContactStore()) {
|
||||
info = new ContactInformation();
|
||||
info.setAddress(addressTxt.getText());
|
||||
info.setCountry(countryTxt.getText());
|
||||
info.setPhoneNumber(phoneTxt.getText());
|
||||
info.setFaxNumber(faxTxt.getText());
|
||||
} else {
|
||||
info = null;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
}
|
@@ -25,7 +25,6 @@ import com.google.gerrit.common.PageLinks;
|
||||
import com.google.gerrit.common.errors.EmailException;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.Account.FieldName;
|
||||
import com.google.gerrit.reviewdb.client.ContactInformation;
|
||||
import com.google.gwt.core.client.JsArray;
|
||||
import com.google.gwt.event.dom.client.ChangeEvent;
|
||||
import com.google.gwt.event.dom.client.ChangeHandler;
|
||||
@@ -356,11 +355,10 @@ class ContactPanelShort extends Composite {
|
||||
newEmail = currentEmail;
|
||||
}
|
||||
|
||||
final ContactInformation info = toContactInformation();
|
||||
save.setEnabled(false);
|
||||
registerNewEmail.setEnabled(false);
|
||||
|
||||
Util.ACCOUNT_SEC.updateContact(newName, newEmail, info,
|
||||
Util.ACCOUNT_SEC.updateContact(newName, newEmail,
|
||||
new GerritCallback<Account>() {
|
||||
@Override
|
||||
public void onSuccess(Account result) {
|
||||
@@ -388,10 +386,6 @@ class ContactPanelShort extends Composite {
|
||||
display(me);
|
||||
}
|
||||
|
||||
ContactInformation toContactInformation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
private int emailListIndexOf(String value) {
|
||||
for (int i = 0; i < emailPick.getItemCount(); i++) {
|
||||
if (value.equalsIgnoreCase(emailPick.getValue(i))) {
|
||||
|
@@ -15,12 +15,12 @@
|
||||
package com.google.gerrit.client.account;
|
||||
|
||||
public class MyContactInformationScreen extends SettingsScreen {
|
||||
private ContactPanelFull panel;
|
||||
private ContactPanelShort panel;
|
||||
|
||||
@Override
|
||||
protected void onInitUI() {
|
||||
super.onInitUI();
|
||||
panel = new ContactPanelFull() {
|
||||
panel = new ContactPanelShort() {
|
||||
@Override
|
||||
void display() {
|
||||
MyContactInformationScreen.this.display();
|
||||
|
@@ -23,7 +23,6 @@ import com.google.gerrit.client.ui.SmallHeading;
|
||||
import com.google.gerrit.common.PageLinks;
|
||||
import com.google.gerrit.common.data.AgreementInfo;
|
||||
import com.google.gerrit.common.data.ContributorAgreement;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.event.dom.client.ClickEvent;
|
||||
import com.google.gwt.event.dom.client.ClickHandler;
|
||||
@@ -42,7 +41,6 @@ import com.google.gwt.user.client.ui.Panel;
|
||||
import com.google.gwt.user.client.ui.RadioButton;
|
||||
import com.google.gwt.user.client.ui.VerticalPanel;
|
||||
import com.google.gwtexpui.globalkey.client.NpTextBox;
|
||||
import com.google.gwtjsonrpc.common.AsyncCallback;
|
||||
import com.google.gwtjsonrpc.common.VoidResult;
|
||||
|
||||
import java.util.HashSet;
|
||||
@@ -60,9 +58,6 @@ public class NewAgreementScreen extends AccountScreen {
|
||||
private Panel agreementGroup;
|
||||
private HTML agreementHtml;
|
||||
|
||||
private Panel contactGroup;
|
||||
private ContactPanelFull contactPanel;
|
||||
|
||||
private Panel finalGroup;
|
||||
private NpTextBox yesIAgreeBox;
|
||||
private Button submit;
|
||||
@@ -117,11 +112,6 @@ public class NewAgreementScreen extends AccountScreen {
|
||||
agreementGroup.add(agreementHtml);
|
||||
formBody.add(agreementGroup);
|
||||
|
||||
contactGroup = new FlowPanel();
|
||||
contactGroup
|
||||
.add(new SmallHeading(Util.C.newAgreementReviewContactHeading()));
|
||||
formBody.add(contactGroup);
|
||||
|
||||
finalGroup = new VerticalPanel();
|
||||
finalGroup.add(new SmallHeading(Util.C.newAgreementCompleteHeading()));
|
||||
final FlowPanel fp = new FlowPanel();
|
||||
@@ -157,7 +147,6 @@ public class NewAgreementScreen extends AccountScreen {
|
||||
private void renderSelf() {
|
||||
current = null;
|
||||
agreementGroup.setVisible(false);
|
||||
contactGroup.setVisible(false);
|
||||
finalGroup.setVisible(false);
|
||||
radios.clear();
|
||||
|
||||
@@ -206,21 +195,7 @@ public class NewAgreementScreen extends AccountScreen {
|
||||
yesIAgreeBox.setFocus(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (contactGroup.isVisible()) {
|
||||
contactPanel.doSave(new AsyncCallback<Account>() {
|
||||
@Override
|
||||
public void onSuccess(Account result) {
|
||||
doEnterAgreement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
doEnterAgreement();
|
||||
}
|
||||
doEnterAgreement();
|
||||
}
|
||||
|
||||
private void doEnterAgreement() {
|
||||
@@ -275,13 +250,6 @@ public class NewAgreementScreen extends AccountScreen {
|
||||
agreementGroup.setVisible(false);
|
||||
}
|
||||
|
||||
if (contactPanel == null && cla.isRequireContactInformation()) {
|
||||
contactPanel = new ContactPanelFull();
|
||||
contactGroup.add(contactPanel);
|
||||
contactPanel.hideSaveButton();
|
||||
}
|
||||
contactGroup.setVisible(
|
||||
cla.isRequireContactInformation() && cla.getAutoVerify() != null);
|
||||
finalGroup.setVisible(cla.getAutoVerify() != null);
|
||||
yesIAgreeBox.setText("");
|
||||
submit.setEnabled(false);
|
||||
|
@@ -1031,18 +1031,6 @@ a:hover.downloadLink {
|
||||
.accountInfoBlock .gwt-Button {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.accountContactPrivacyDetails {
|
||||
margin-left: 10px;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
width: 40em;
|
||||
}
|
||||
.accountContactOnFile {
|
||||
margin-left: 10px;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.addWatchPanel {
|
||||
margin-top: 10px;
|
||||
|
@@ -17,10 +17,8 @@ package com.google.gerrit.httpd.rpc.account;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.gerrit.audit.AuditService;
|
||||
import com.google.gerrit.common.ChangeHooks;
|
||||
import com.google.gerrit.common.TimeUtil;
|
||||
import com.google.gerrit.common.data.AccountSecurity;
|
||||
import com.google.gerrit.common.data.ContributorAgreement;
|
||||
import com.google.gerrit.common.errors.ContactInformationStoreException;
|
||||
import com.google.gerrit.common.errors.NoSuchEntityException;
|
||||
import com.google.gerrit.common.errors.PermissionDeniedException;
|
||||
import com.google.gerrit.httpd.rpc.BaseServiceImplementation;
|
||||
@@ -28,7 +26,6 @@ import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.AccountExternalId;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroupMember;
|
||||
import com.google.gerrit.reviewdb.client.ContactInformation;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
@@ -36,7 +33,6 @@ import com.google.gerrit.server.account.AccountByEmailCache;
|
||||
import com.google.gerrit.server.account.AccountCache;
|
||||
import com.google.gerrit.server.account.GroupCache;
|
||||
import com.google.gerrit.server.account.Realm;
|
||||
import com.google.gerrit.server.contact.ContactStore;
|
||||
import com.google.gerrit.server.project.ProjectCache;
|
||||
import com.google.gwtjsonrpc.common.AsyncCallback;
|
||||
import com.google.gwtjsonrpc.common.VoidResult;
|
||||
@@ -50,13 +46,11 @@ import java.util.Set;
|
||||
|
||||
class AccountSecurityImpl extends BaseServiceImplementation implements
|
||||
AccountSecurity {
|
||||
private final ContactStore contactStore;
|
||||
private final Realm realm;
|
||||
private final ProjectCache projectCache;
|
||||
private final Provider<IdentifiedUser> user;
|
||||
private final AccountByEmailCache byEmailCache;
|
||||
private final AccountCache accountCache;
|
||||
private final boolean useContactInfo;
|
||||
|
||||
private final DeleteExternalIds.Factory deleteExternalIdsFactory;
|
||||
private final ExternalIdDetailFactory.Factory externalIdDetailFactory;
|
||||
@@ -67,7 +61,7 @@ class AccountSecurityImpl extends BaseServiceImplementation implements
|
||||
|
||||
@Inject
|
||||
AccountSecurityImpl(final Provider<ReviewDb> schema,
|
||||
final Provider<CurrentUser> currentUser, final ContactStore cs,
|
||||
final Provider<CurrentUser> currentUser,
|
||||
final Realm r, final Provider<IdentifiedUser> u,
|
||||
final ProjectCache pc,
|
||||
final AccountByEmailCache abec, final AccountCache uac,
|
||||
@@ -76,16 +70,12 @@ class AccountSecurityImpl extends BaseServiceImplementation implements
|
||||
final ChangeHooks hooks, final GroupCache groupCache,
|
||||
final AuditService auditService) {
|
||||
super(schema, currentUser);
|
||||
contactStore = cs;
|
||||
realm = r;
|
||||
user = u;
|
||||
projectCache = pc;
|
||||
byEmailCache = abec;
|
||||
accountCache = uac;
|
||||
this.auditService = auditService;
|
||||
|
||||
useContactInfo = contactStore != null && contactStore.isEnabled();
|
||||
|
||||
this.deleteExternalIdsFactory = deleteExternalIdsFactory;
|
||||
this.externalIdDetailFactory = externalIdDetailFactory;
|
||||
this.hooks = hooks;
|
||||
@@ -105,7 +95,7 @@ class AccountSecurityImpl extends BaseServiceImplementation implements
|
||||
|
||||
@Override
|
||||
public void updateContact(final String name, final String emailAddr,
|
||||
final ContactInformation info, final AsyncCallback<Account> callback) {
|
||||
final AsyncCallback<Account> callback) {
|
||||
run(callback, new Action<Account>() {
|
||||
@Override
|
||||
public Account run(ReviewDb db) throws OrmException, Failure {
|
||||
@@ -120,19 +110,6 @@ class AccountSecurityImpl extends BaseServiceImplementation implements
|
||||
throw new Failure(new PermissionDeniedException("Email address must be verified"));
|
||||
}
|
||||
me.setPreferredEmail(Strings.emptyToNull(emailAddr));
|
||||
if (useContactInfo) {
|
||||
if (ContactInformation.hasAddress(info)
|
||||
|| (me.isContactFiled() && ContactInformation.hasData(info))) {
|
||||
me.setContactFiled(TimeUtil.nowTs());
|
||||
}
|
||||
if (ContactInformation.hasData(info)) {
|
||||
try {
|
||||
contactStore.store(me, info);
|
||||
} catch (ContactInformationStoreException e) {
|
||||
throw new Failure(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
db.accounts().update(Collections.singleton(me));
|
||||
if (!eq(oldEmail, me.getPreferredEmail())) {
|
||||
byEmailCache.evict(oldEmail);
|
||||
|
@@ -53,8 +53,6 @@ import com.google.gerrit.server.config.DownloadConfig;
|
||||
import com.google.gerrit.server.config.GerritGlobalModule;
|
||||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.gerrit.server.config.RestCacheAdminModule;
|
||||
import com.google.gerrit.server.contact.ContactStoreModule;
|
||||
import com.google.gerrit.server.contact.HttpContactStoreConnection;
|
||||
import com.google.gerrit.server.git.ChangeCacheImplModule;
|
||||
import com.google.gerrit.server.git.GarbageCollectionModule;
|
||||
import com.google.gerrit.server.git.ReceiveCommitsExecutorModule;
|
||||
@@ -427,11 +425,9 @@ public class Daemon extends SiteProgram {
|
||||
modules.add(RequestContextFilter.module());
|
||||
modules.add(AllRequestFilter.module());
|
||||
modules.add(H2CacheBasedWebSession.module());
|
||||
modules.add(HttpContactStoreConnection.module());
|
||||
modules.add(sysInjector.getInstance(GitOverHttpModule.class));
|
||||
modules.add(sysInjector.getInstance(WebModule.class));
|
||||
modules.add(new HttpPluginModule());
|
||||
modules.add(new ContactStoreModule());
|
||||
if (sshd) {
|
||||
modules.add(sshInjector.getInstance(WebSshGlueModule.class));
|
||||
} else {
|
||||
|
@@ -182,9 +182,7 @@ public final class Account {
|
||||
@Column(id = 4, notNull = false)
|
||||
protected String preferredEmail;
|
||||
|
||||
/** When did the user last give us contact information? Null if never. */
|
||||
@Column(id = 5, notNull = false)
|
||||
protected Timestamp contactFiledOn;
|
||||
// DELETED: id = 5 (contactFiledOn)
|
||||
|
||||
/** This user's preferences */
|
||||
@Column(id = 6, name = Column.NONE)
|
||||
@@ -257,18 +255,6 @@ public final class Account {
|
||||
generalPreferences = p;
|
||||
}
|
||||
|
||||
public boolean isContactFiled() {
|
||||
return contactFiledOn != null;
|
||||
}
|
||||
|
||||
public Timestamp getContactFiledOn() {
|
||||
return contactFiledOn;
|
||||
}
|
||||
|
||||
public void setContactFiled(Timestamp ts) {
|
||||
contactFiledOn = ts;
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return ! inactive;
|
||||
}
|
||||
|
@@ -1,85 +0,0 @@
|
||||
// Copyright (C) 2008 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.reviewdb.client;
|
||||
|
||||
import com.google.gwtorm.client.Column;
|
||||
|
||||
/** Non-Internet contact details, such as a postal address and telephone. */
|
||||
public final class ContactInformation {
|
||||
@Column(id = 1, length = Integer.MAX_VALUE, notNull = false)
|
||||
protected String address;
|
||||
|
||||
@Column(id = 2, notNull = false, length = 40)
|
||||
protected String country;
|
||||
|
||||
@Column(id = 3, notNull = false, length = 30)
|
||||
protected String phoneNbr;
|
||||
|
||||
@Column(id = 4, notNull = false, length = 30)
|
||||
protected String faxNbr;
|
||||
|
||||
public ContactInformation() {
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(final String a) {
|
||||
address = a;
|
||||
}
|
||||
|
||||
public String getCountry() {
|
||||
return country;
|
||||
}
|
||||
|
||||
public void setCountry(final String c) {
|
||||
country = c;
|
||||
}
|
||||
|
||||
public String getPhoneNumber() {
|
||||
return phoneNbr;
|
||||
}
|
||||
|
||||
public void setPhoneNumber(final String p) {
|
||||
phoneNbr = p;
|
||||
}
|
||||
|
||||
public String getFaxNumber() {
|
||||
return faxNbr;
|
||||
}
|
||||
|
||||
public void setFaxNumber(final String f) {
|
||||
faxNbr = f;
|
||||
}
|
||||
|
||||
public static boolean hasData(final ContactInformation contactInformation) {
|
||||
if (contactInformation == null) {
|
||||
return false;
|
||||
}
|
||||
return hasData(contactInformation.address)
|
||||
|| hasData(contactInformation.country)
|
||||
|| hasData(contactInformation.phoneNbr)
|
||||
|| hasData(contactInformation.faxNbr);
|
||||
}
|
||||
|
||||
public static boolean hasAddress(final ContactInformation contactInformation) {
|
||||
return contactInformation != null && hasData(contactInformation.address);
|
||||
}
|
||||
|
||||
private static boolean hasData(final String s) {
|
||||
return s != null && s.trim().length() > 0;
|
||||
}
|
||||
}
|
@@ -43,7 +43,6 @@ public class GetDetail implements RestReadView<AccountResource> {
|
||||
Account a = rsrc.getUser().getAccount();
|
||||
AccountDetailInfo info = new AccountDetailInfo(a.getId().get());
|
||||
info.registeredOn = a.getRegisteredOn();
|
||||
info.contactFiledOn = a.getContactFiledOn();
|
||||
try {
|
||||
directory.fillAccountInfo(Collections.singleton(info),
|
||||
EnumSet.allOf(FillOptions.class));
|
||||
@@ -56,7 +55,6 @@ public class GetDetail implements RestReadView<AccountResource> {
|
||||
|
||||
public static class AccountDetailInfo extends AccountInfo {
|
||||
public Timestamp registeredOn;
|
||||
public Timestamp contactFiledOn;
|
||||
|
||||
public AccountDetailInfo(Integer id) {
|
||||
super(id);
|
||||
|
@@ -97,7 +97,6 @@ public class GetServerInfo implements RestReadView<ConfigResource> {
|
||||
ServerInfo info = new ServerInfo();
|
||||
info.auth = getAuthInfo(authConfig, realm);
|
||||
info.change = getChangeInfo(config);
|
||||
info.contactStore = getContactStoreInfo();
|
||||
info.download =
|
||||
getDownloadInfo(downloadSchemes, downloadCommands, cloneCommands,
|
||||
archiveFormats);
|
||||
@@ -170,17 +169,6 @@ public class GetServerInfo implements RestReadView<ConfigResource> {
|
||||
return info;
|
||||
}
|
||||
|
||||
private ContactStoreInfo getContactStoreInfo() {
|
||||
String url = config.getString("contactstore", null, "url");
|
||||
if (url == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ContactStoreInfo contactStore = new ContactStoreInfo();
|
||||
contactStore.url = url;
|
||||
return contactStore;
|
||||
}
|
||||
|
||||
private DownloadInfo getDownloadInfo(
|
||||
DynamicMap<DownloadScheme> downloadSchemes,
|
||||
DynamicMap<DownloadCommand> downloadCommands,
|
||||
@@ -323,7 +311,6 @@ public class GetServerInfo implements RestReadView<ConfigResource> {
|
||||
public static class ServerInfo {
|
||||
public AuthInfo auth;
|
||||
public ChangeConfigInfo change;
|
||||
public ContactStoreInfo contactStore;
|
||||
public DownloadInfo download;
|
||||
public GerritInfo gerrit;
|
||||
public GitwebInfo gitweb;
|
||||
@@ -358,10 +345,6 @@ public class GetServerInfo implements RestReadView<ConfigResource> {
|
||||
public Boolean submitWholeTopic;
|
||||
}
|
||||
|
||||
public static class ContactStoreInfo {
|
||||
public String url;
|
||||
}
|
||||
|
||||
public static class DownloadInfo {
|
||||
public Map<String, DownloadSchemeInfo> schemes;
|
||||
public List<String> archives;
|
||||
|
@@ -50,7 +50,6 @@ public final class SitePaths {
|
||||
|
||||
public final Path gerrit_config;
|
||||
public final Path secure_config;
|
||||
public final Path contact_information_pub;
|
||||
|
||||
public final Path ssl_keystore;
|
||||
public final Path ssh_key;
|
||||
@@ -89,7 +88,6 @@ public final class SitePaths {
|
||||
|
||||
gerrit_config = etc_dir.resolve("gerrit.config");
|
||||
secure_config = etc_dir.resolve("secure.config");
|
||||
contact_information_pub = etc_dir.resolve("contact_information.pub");
|
||||
|
||||
ssl_keystore = etc_dir.resolve("keystore");
|
||||
ssh_key = etc_dir.resolve("ssh_host_key");
|
||||
|
@@ -1,42 +0,0 @@
|
||||
// Copyright (C) 2011 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.contact;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
/** Single connection to a {@link ContactStore}. */
|
||||
public interface ContactStoreConnection {
|
||||
public static interface Factory {
|
||||
/**
|
||||
* Open a new connection to a {@link ContactStore}.
|
||||
*
|
||||
* @param url contact store URL.
|
||||
* @return a new connection to the store.
|
||||
*
|
||||
* @throws IOException the URL couldn't be opened.
|
||||
*/
|
||||
ContactStoreConnection open(URL url) throws IOException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a blob of contact data in the store.
|
||||
*
|
||||
* @param body protocol-specific body data.
|
||||
*
|
||||
* @throws IOException an error occurred storing the contact data.
|
||||
*/
|
||||
public void store(byte[] body) throws IOException;
|
||||
}
|
@@ -1,72 +0,0 @@
|
||||
// Copyright (C) 2009 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.contact;
|
||||
|
||||
import com.google.gerrit.common.Nullable;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.gerrit.server.config.SitePaths;
|
||||
import com.google.gerrit.server.util.BouncyCastleUtil;
|
||||
import com.google.gwtorm.server.SchemaFactory;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Provides;
|
||||
import com.google.inject.ProvisionException;
|
||||
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.eclipse.jgit.util.StringUtils;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/** Creates the {@link ContactStore} based on the configuration. */
|
||||
public class ContactStoreModule extends AbstractModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Provides
|
||||
public ContactStore provideContactStore(@GerritServerConfig final Config config,
|
||||
final SitePaths site, final SchemaFactory<ReviewDb> schema,
|
||||
final ContactStoreConnection.Factory connFactory) {
|
||||
String url = config.getString("contactstore", null, "url");
|
||||
if (StringUtils.isEmptyOrNull(url)) {
|
||||
return new NoContactStore();
|
||||
}
|
||||
|
||||
if (!BouncyCastleUtil.havePGP()) {
|
||||
throw new ProvisionException("BouncyCastle PGP not installed; "
|
||||
+ " needed to encrypt contact information");
|
||||
}
|
||||
|
||||
URL storeUrl;
|
||||
try {
|
||||
storeUrl = new URL(url);
|
||||
} catch (MalformedURLException e) {
|
||||
throw new ProvisionException("Invalid contactstore.url: " + url, e);
|
||||
}
|
||||
|
||||
String storeAPPSEC = config.getString("contactstore", null, "appsec");
|
||||
Path pubkey = site.contact_information_pub;
|
||||
if (!Files.exists(pubkey)) {
|
||||
throw new ProvisionException("PGP public key file \""
|
||||
+ pubkey.toAbsolutePath() + "\" not found");
|
||||
}
|
||||
return new EncryptedContactStore(storeUrl, storeAPPSEC, pubkey, schema,
|
||||
connFactory);
|
||||
}
|
||||
}
|
@@ -1,277 +0,0 @@
|
||||
// Copyright (C) 2009 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.contact;
|
||||
|
||||
import com.google.gerrit.common.TimeUtil;
|
||||
import com.google.gerrit.common.errors.ContactInformationStoreException;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.AccountExternalId;
|
||||
import com.google.gerrit.reviewdb.client.ContactInformation;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.UrlEncoded;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.gwtorm.server.SchemaFactory;
|
||||
import com.google.inject.ProvisionException;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import org.bouncycastle.bcpg.ArmoredOutputStream;
|
||||
import org.bouncycastle.openpgp.PGPCompressedData;
|
||||
import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
|
||||
import org.bouncycastle.openpgp.PGPEncryptedData;
|
||||
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPLiteralData;
|
||||
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
|
||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
||||
import org.bouncycastle.openpgp.PGPUtil;
|
||||
import org.bouncycastle.openpgp.bc.BcPGPPublicKeyRingCollection;
|
||||
import org.bouncycastle.openpgp.operator.bc.BcPGPDataEncryptorBuilder;
|
||||
import org.bouncycastle.openpgp.operator.bc.BcPublicKeyKeyEncryptionMethodGenerator;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.sql.Timestamp;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/** Encrypts {@link ContactInformation} instances and saves them. */
|
||||
@Singleton
|
||||
class EncryptedContactStore implements ContactStore {
|
||||
private static final Logger log =
|
||||
LoggerFactory.getLogger(EncryptedContactStore.class);
|
||||
private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
|
||||
|
||||
private final SchemaFactory<ReviewDb> schema;
|
||||
private final PGPPublicKey dest;
|
||||
private final SecureRandom prng;
|
||||
private final URL storeUrl;
|
||||
private final String storeAPPSEC;
|
||||
private final ContactStoreConnection.Factory connFactory;
|
||||
|
||||
EncryptedContactStore(final URL storeUrl, final String storeAPPSEC,
|
||||
final Path pubKey, final SchemaFactory<ReviewDb> schema,
|
||||
final ContactStoreConnection.Factory connFactory) {
|
||||
this.storeUrl = storeUrl;
|
||||
this.storeAPPSEC = storeAPPSEC;
|
||||
this.schema = schema;
|
||||
this.dest = selectKey(readPubRing(pubKey));
|
||||
this.connFactory = connFactory;
|
||||
|
||||
final String prngName = "SHA1PRNG";
|
||||
try {
|
||||
prng = SecureRandom.getInstance(prngName);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new ProvisionException("Cannot create " + prngName, e);
|
||||
}
|
||||
|
||||
// Run a test encryption to verify the proper algorithms exist in
|
||||
// our JVM and we are able to invoke them. This helps to identify
|
||||
// a system configuration problem early at startup, rather than a
|
||||
// lot later during runtime.
|
||||
//
|
||||
try {
|
||||
encrypt("test", new Date(0), "test".getBytes("UTF-8"));
|
||||
} catch (PGPException | IOException e) {
|
||||
throw new ProvisionException("PGP encryption not available", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static PGPPublicKeyRingCollection readPubRing(Path pub) {
|
||||
try (InputStream fin = Files.newInputStream(pub);
|
||||
InputStream in = PGPUtil.getDecoderStream(fin)) {
|
||||
return new BcPGPPublicKeyRingCollection(in);
|
||||
} catch (IOException | PGPException e) {
|
||||
throw new ProvisionException("Cannot read " + pub, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static PGPPublicKey selectKey(final PGPPublicKeyRingCollection rings) {
|
||||
for (final Iterator<?> ri = rings.getKeyRings(); ri.hasNext();) {
|
||||
final PGPPublicKeyRing currRing = (PGPPublicKeyRing) ri.next();
|
||||
for (final Iterator<?> ki = currRing.getPublicKeys(); ki.hasNext();) {
|
||||
final PGPPublicKey k = (PGPPublicKey) ki.next();
|
||||
if (k.isEncryptionKey()) {
|
||||
return k;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(final Account account, final ContactInformation info)
|
||||
throws ContactInformationStoreException {
|
||||
try {
|
||||
final byte[] plainText = format(account, info).getBytes("UTF-8");
|
||||
final String fileName = "account-" + account.getId();
|
||||
final Date fileDate = account.getContactFiledOn();
|
||||
final byte[] encText = encrypt(fileName, fileDate, plainText);
|
||||
final String encStr = new String(encText, "UTF-8");
|
||||
|
||||
final Timestamp filedOn = account.getContactFiledOn();
|
||||
final UrlEncoded u = new UrlEncoded();
|
||||
if (storeAPPSEC != null) {
|
||||
u.put("APPSEC", storeAPPSEC);
|
||||
}
|
||||
if (account.getPreferredEmail() != null) {
|
||||
u.put("email", account.getPreferredEmail());
|
||||
}
|
||||
if (filedOn != null) {
|
||||
u.put("filed", String.valueOf(filedOn.getTime() / 1000L));
|
||||
}
|
||||
u.put("account_id", String.valueOf(account.getId().get()));
|
||||
u.put("data", encStr);
|
||||
connFactory.open(storeUrl).store(u.toString().getBytes("UTF-8"));
|
||||
} catch (IOException | PGPException e) {
|
||||
log.error("Cannot store encrypted contact information", e);
|
||||
throw new ContactInformationStoreException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private final PGPEncryptedDataGenerator cpk() {
|
||||
final BcPGPDataEncryptorBuilder builder =
|
||||
new BcPGPDataEncryptorBuilder(PGPEncryptedData.CAST5)
|
||||
.setSecureRandom(prng);
|
||||
PGPEncryptedDataGenerator cpk =
|
||||
new PGPEncryptedDataGenerator(builder, true);
|
||||
final BcPublicKeyKeyEncryptionMethodGenerator methodGenerator =
|
||||
new BcPublicKeyKeyEncryptionMethodGenerator(dest);
|
||||
cpk.addMethod(methodGenerator);
|
||||
return cpk;
|
||||
}
|
||||
|
||||
private byte[] encrypt(final String name, final Date date,
|
||||
final byte[] rawText) throws PGPException,
|
||||
IOException {
|
||||
final byte[] zText = compress(name, date, rawText);
|
||||
|
||||
final ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
||||
try (ArmoredOutputStream aout = new ArmoredOutputStream(buf);
|
||||
OutputStream cout = cpk().open(aout, zText.length)) {
|
||||
cout.write(zText);
|
||||
}
|
||||
|
||||
return buf.toByteArray();
|
||||
}
|
||||
|
||||
private static byte[] compress(final String fileName, Date fileDate,
|
||||
final byte[] plainText) throws IOException {
|
||||
final ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
||||
final PGPCompressedDataGenerator comdg;
|
||||
final int len = plainText.length;
|
||||
if (fileDate == null) {
|
||||
fileDate = PGPLiteralData.NOW;
|
||||
}
|
||||
|
||||
comdg = new PGPCompressedDataGenerator(PGPCompressedData.ZIP);
|
||||
try (OutputStream out =
|
||||
new PGPLiteralDataGenerator().open(comdg.open(buf),
|
||||
PGPLiteralData.BINARY, fileName, len, fileDate)) {
|
||||
out.write(plainText);
|
||||
} finally {
|
||||
comdg.close(); // PGPCompressedDataGenerator doesn't implement Closable
|
||||
}
|
||||
return buf.toByteArray();
|
||||
}
|
||||
|
||||
private String format(final Account account, final ContactInformation info)
|
||||
throws ContactInformationStoreException {
|
||||
Timestamp on = account.getContactFiledOn();
|
||||
if (on == null) {
|
||||
on = TimeUtil.nowTs();
|
||||
}
|
||||
|
||||
final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
|
||||
df.setTimeZone(UTC);
|
||||
|
||||
final StringBuilder b = new StringBuilder();
|
||||
field(b, "Account-Id", account.getId().toString());
|
||||
field(b, "Date", df.format(on) + " " + UTC.getID());
|
||||
field(b, "Full-Name", account.getFullName());
|
||||
field(b, "Preferred-Email", account.getPreferredEmail());
|
||||
|
||||
try (ReviewDb db = schema.open()) {
|
||||
for (final AccountExternalId e : db.accountExternalIds().byAccount(
|
||||
account.getId())) {
|
||||
final StringBuilder oistr = new StringBuilder();
|
||||
if (e.getEmailAddress() != null && e.getEmailAddress().length() > 0) {
|
||||
if (oistr.length() > 0) {
|
||||
oistr.append(' ');
|
||||
}
|
||||
oistr.append(e.getEmailAddress());
|
||||
}
|
||||
if (e.isScheme(AccountExternalId.SCHEME_MAILTO)) {
|
||||
if (oistr.length() > 0) {
|
||||
oistr.append(' ');
|
||||
}
|
||||
oistr.append('<');
|
||||
oistr.append(e.getExternalId());
|
||||
oistr.append('>');
|
||||
}
|
||||
field(b, "Identity", oistr.toString());
|
||||
}
|
||||
} catch (OrmException e) {
|
||||
throw new ContactInformationStoreException(e);
|
||||
}
|
||||
|
||||
field(b, "Address", info.getAddress());
|
||||
field(b, "Country", info.getCountry());
|
||||
field(b, "Phone-Number", info.getPhoneNumber());
|
||||
field(b, "Fax-Number", info.getFaxNumber());
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
private static void field(final StringBuilder b, final String name,
|
||||
String value) {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
value = value.trim();
|
||||
if (value.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
b.append(name);
|
||||
b.append(':');
|
||||
if (value.indexOf('\n') == -1) {
|
||||
b.append(' ');
|
||||
b.append(value);
|
||||
} else {
|
||||
value = value.replaceAll("\r\n", "\n");
|
||||
value = value.replaceAll("\n", "\n\t");
|
||||
b.append("\n\t");
|
||||
b.append(value);
|
||||
}
|
||||
b.append('\n');
|
||||
}
|
||||
}
|
@@ -1,65 +0,0 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
|
||||
package com.google.gerrit.server.contact;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
import com.google.inject.assistedinject.FactoryModuleBuilder;
|
||||
|
||||
import org.eclipse.jgit.util.IO;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
|
||||
/** {@link ContactStoreConnection} with an underlying {@literal @HttpURLConnection}. */
|
||||
public class HttpContactStoreConnection implements ContactStoreConnection {
|
||||
public static Module module() {
|
||||
return new AbstractModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
install(new FactoryModuleBuilder()
|
||||
.implement(ContactStoreConnection.class, HttpContactStoreConnection.class)
|
||||
.build(ContactStoreConnection.Factory.class));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private final HttpURLConnection conn;
|
||||
|
||||
@Inject
|
||||
HttpContactStoreConnection(@Assisted final URL url) throws IOException {
|
||||
final URLConnection urlConn = url.openConnection();
|
||||
if (!(urlConn instanceof HttpURLConnection)) {
|
||||
throw new IllegalArgumentException("Non-HTTP URL not supported: " + urlConn);
|
||||
}
|
||||
conn = (HttpURLConnection) urlConn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(final byte[] body) throws IOException {
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setRequestProperty("Content-Type",
|
||||
"application/x-www-form-urlencoded; charset=UTF-8");
|
||||
conn.setDoOutput(true);
|
||||
conn.setFixedLengthStreamingMode(body.length);
|
||||
try (OutputStream out = conn.getOutputStream()) {
|
||||
out.write(body);
|
||||
}
|
||||
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
|
||||
throw new IOException("Connection failed: " + conn.getResponseCode());
|
||||
}
|
||||
final byte[] dst = new byte[2];
|
||||
try (InputStream in = conn.getInputStream()) {
|
||||
IO.readFully(in, dst, 0, 2);
|
||||
}
|
||||
if (dst[0] != 'O' || dst[1] != 'K') {
|
||||
throw new IOException("Store failed: " + dst[0] + dst[1]);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,33 +0,0 @@
|
||||
// Copyright (C) 2009 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.contact;
|
||||
|
||||
import com.google.gerrit.common.errors.ContactInformationStoreException;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.ContactInformation;
|
||||
|
||||
public class NoContactStore implements ContactStore {
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(Account account, ContactInformation info)
|
||||
throws ContactInformationStoreException {
|
||||
throw new ContactInformationStoreException(new IllegalStateException(
|
||||
"ContactStore not configured"));
|
||||
}
|
||||
}
|
@@ -97,7 +97,6 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
|
||||
|
||||
private static final String CONTRIBUTOR_AGREEMENT = "contributor-agreement";
|
||||
private static final String KEY_ACCEPTED = "accepted";
|
||||
private static final String KEY_REQUIRE_CONTACT_INFORMATION = "requireContactInformation";
|
||||
private static final String KEY_AUTO_VERIFY = "autoVerify";
|
||||
private static final String KEY_AGREEMENT_URL = "agreementUrl";
|
||||
|
||||
@@ -455,8 +454,6 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
|
||||
for (String name : rc.getSubsections(CONTRIBUTOR_AGREEMENT)) {
|
||||
ContributorAgreement ca = getContributorAgreement(name, true);
|
||||
ca.setDescription(rc.getString(CONTRIBUTOR_AGREEMENT, name, KEY_DESCRIPTION));
|
||||
ca.setRequireContactInformation(
|
||||
rc.getBoolean(CONTRIBUTOR_AGREEMENT, name, KEY_REQUIRE_CONTACT_INFORMATION, false));
|
||||
ca.setAgreementUrl(rc.getString(CONTRIBUTOR_AGREEMENT, name, KEY_AGREEMENT_URL));
|
||||
ca.setAccepted(loadPermissionRules(
|
||||
rc, CONTRIBUTOR_AGREEMENT, name, KEY_ACCEPTED, groupsByName, false));
|
||||
@@ -883,7 +880,6 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
|
||||
Config rc, Set<AccountGroup.UUID> keepGroups) {
|
||||
for (ContributorAgreement ca : sort(contributorAgreements.values())) {
|
||||
set(rc, CONTRIBUTOR_AGREEMENT, ca.getName(), KEY_DESCRIPTION, ca.getDescription());
|
||||
set(rc, CONTRIBUTOR_AGREEMENT, ca.getName(), KEY_REQUIRE_CONTACT_INFORMATION, ca.isRequireContactInformation());
|
||||
set(rc, CONTRIBUTOR_AGREEMENT, ca.getName(), KEY_AGREEMENT_URL, ca.getAgreementUrl());
|
||||
|
||||
if (ca.getAutoVerify() != null) {
|
||||
|
@@ -352,19 +352,10 @@ public class ProjectControl {
|
||||
}
|
||||
final IdentifiedUser iUser = (IdentifiedUser) user;
|
||||
|
||||
boolean hasContactInfo = !missing(iUser.getAccount().getFullName())
|
||||
&& !missing(iUser.getAccount().getPreferredEmail())
|
||||
&& iUser.getAccount().isContactFiled();
|
||||
|
||||
List<AccountGroup.UUID> okGroupIds = Lists.newArrayList();
|
||||
List<AccountGroup.UUID> missingInfoGroupIds = Lists.newArrayList();
|
||||
for (ContributorAgreement ca : contributorAgreements) {
|
||||
List<AccountGroup.UUID> groupIds;
|
||||
if (hasContactInfo || !ca.isRequireContactInformation()) {
|
||||
groupIds = okGroupIds;
|
||||
} else {
|
||||
groupIds = missingInfoGroupIds;
|
||||
}
|
||||
groupIds = okGroupIds;
|
||||
|
||||
for (PermissionRule rule : ca.getAccepted()) {
|
||||
if ((rule.getAction() == Action.ALLOW) && (rule.getGroup() != null)
|
||||
@@ -378,28 +369,6 @@ public class ProjectControl {
|
||||
return Capable.OK;
|
||||
}
|
||||
|
||||
if (iUser.getEffectiveGroups().containsAnyOf(missingInfoGroupIds)) {
|
||||
final StringBuilder msg = new StringBuilder();
|
||||
for (ContributorAgreement ca : contributorAgreements) {
|
||||
if (ca.isRequireContactInformation()) {
|
||||
msg.append(ca.getName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
msg.append(" contributor agreement requires");
|
||||
msg.append(" current contact information.\n");
|
||||
if (canonicalWebUrl != null) {
|
||||
msg.append("\nPlease review your contact information");
|
||||
msg.append(":\n\n ");
|
||||
msg.append(canonicalWebUrl);
|
||||
msg.append("#");
|
||||
msg.append(PageLinks.SETTINGS_CONTACT);
|
||||
msg.append("\n");
|
||||
}
|
||||
msg.append("\n");
|
||||
return new Capable(msg.toString());
|
||||
}
|
||||
|
||||
final StringBuilder msg = new StringBuilder();
|
||||
msg.append(" A Contributor Agreement must be completed before uploading");
|
||||
if (canonicalWebUrl != null) {
|
||||
@@ -415,10 +384,6 @@ public class ProjectControl {
|
||||
return new Capable(msg.toString());
|
||||
}
|
||||
|
||||
private static boolean missing(final String value) {
|
||||
return value == null || value.trim().equals("");
|
||||
}
|
||||
|
||||
private boolean canPerformOnAnyRef(String permissionName) {
|
||||
for (SectionMatcher matcher : access()) {
|
||||
AccessSection section = matcher.section;
|
||||
|
@@ -32,7 +32,7 @@ import java.util.List;
|
||||
/** A version of the database schema. */
|
||||
public abstract class SchemaVersion {
|
||||
/** The current schema version. */
|
||||
public static final Class<Schema_110> C = Schema_110.class;
|
||||
public static final Class<Schema_111> C = Schema_111.class;
|
||||
|
||||
public static int getBinaryVersion() {
|
||||
return guessVersion(C);
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2009 The Android Open Source Project
|
||||
// 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.
|
||||
@@ -12,15 +12,14 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.server.contact;
|
||||
package com.google.gerrit.server.schema;
|
||||
|
||||
import com.google.gerrit.common.errors.ContactInformationStoreException;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.ContactInformation;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
|
||||
public interface ContactStore {
|
||||
boolean isEnabled();
|
||||
|
||||
void store(Account account, ContactInformation info)
|
||||
throws ContactInformationStoreException;
|
||||
public class Schema_111 extends SchemaVersion {
|
||||
@Inject
|
||||
Schema_111(Provider<Schema_110> prior) {
|
||||
super(prior);
|
||||
}
|
||||
}
|
@@ -82,7 +82,6 @@ public class ProjectConfigTest extends LocalDiskRepositoryTestCase {
|
||||
+ " description = A simple description\n" //
|
||||
+ " accepted = group Developers\n" //
|
||||
+ " accepted = group Staff\n" //
|
||||
+ " requireContactInformation = true\n" //
|
||||
+ " autoVerify = group Developers\n" //
|
||||
+ " agreementUrl = http://www.example.com/agree\n")) //
|
||||
));
|
||||
@@ -97,7 +96,6 @@ public class ProjectConfigTest extends LocalDiskRepositoryTestCase {
|
||||
assertThat(ca.getAccepted().get(0).getGroup()).isEqualTo(developers);
|
||||
assertThat(ca.getAccepted().get(1).getGroup().getName()).isEqualTo("Staff");
|
||||
assertThat(ca.getAutoVerify().getName()).isEqualTo("Developers");
|
||||
assertThat(ca.isRequireContactInformation()).isTrue();
|
||||
|
||||
AccessSection section = cfg.getAccessSection("refs/heads/*");
|
||||
assertThat(section).isNotNull();
|
||||
@@ -184,7 +182,6 @@ public class ProjectConfigTest extends LocalDiskRepositoryTestCase {
|
||||
+ "[contributor-agreement \"Individual\"]\n" //
|
||||
+ " description = A simple description\n" //
|
||||
+ " accepted = group Developers\n" //
|
||||
+ " requireContactInformation = true\n" //
|
||||
+ " autoVerify = group Developers\n" //
|
||||
+ " agreementUrl = http://www.example.com/agree\n")) //
|
||||
));
|
||||
@@ -197,7 +194,6 @@ public class ProjectConfigTest extends LocalDiskRepositoryTestCase {
|
||||
Permission submit = section.getPermission(Permission.SUBMIT);
|
||||
submit.add(new PermissionRule(cfg.resolve(staff)));
|
||||
ContributorAgreement ca = cfg.getContributorAgreement("Individual");
|
||||
ca.setRequireContactInformation(false);
|
||||
ca.setAccepted(Collections.singletonList(new PermissionRule(cfg.resolve(staff))));
|
||||
ca.setAutoVerify(null);
|
||||
ca.setDescription("A new description");
|
||||
|
@@ -38,8 +38,6 @@ import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.gerrit.server.config.GerritServerConfigModule;
|
||||
import com.google.gerrit.server.config.RestCacheAdminModule;
|
||||
import com.google.gerrit.server.config.SitePath;
|
||||
import com.google.gerrit.server.contact.ContactStoreModule;
|
||||
import com.google.gerrit.server.contact.HttpContactStoreConnection;
|
||||
import com.google.gerrit.server.git.ChangeCacheImplModule;
|
||||
import com.google.gerrit.server.git.GarbageCollectionModule;
|
||||
import com.google.gerrit.server.git.LocalDiskRepositoryManager;
|
||||
@@ -344,9 +342,7 @@ public class WebAppInitializer extends GuiceServletContextListener
|
||||
modules.add(new NoSshModule());
|
||||
}
|
||||
modules.add(H2CacheBasedWebSession.module());
|
||||
modules.add(HttpContactStoreConnection.module());
|
||||
modules.add(new HttpPluginModule());
|
||||
modules.add(new ContactStoreModule());
|
||||
|
||||
AuthConfig authConfig = cfgInjector.getInstance(AuthConfig.class);
|
||||
if (authConfig.getAuthType() == AuthType.OPENID) {
|
||||
|
Reference in New Issue
Block a user