Merge branch 'dev-spearce'
* dev-spearce: (33 commits) Use transactions to handle comments when possible Avoid opening extra ReviewDb connection in PatchSetInfoFactory Minor ORM cleanups to support other backends Add command to output a Protobuf message file for the DB Support gwtorm 1.2 Fix reference of Database<T> to SchemaFactory<T> Support Velocity 1.5 Move replication queue binding out of GerritGlobalModule Remove static initialization of Velocity Move WorkQueue out of GerritGlobalModule Support auth.type = CUSTOM_EXTENSION Extract Git /p/ module configuration Make WebSession an abstract interface Move GitRepositoryManager setup out of SchemaModule Move SmtpEmailSender to its own module Make Address, EmailHeader visible to other EmailSenders Disable SSH Keys in the web UI if SSHD is disabled Refactor how we tie the SSH objects into the HTTP injector daemon: Allow httpd without sshd Allow sshd.listenAddress = off to disable the daemon ... Conflicts: gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitWebConfig.java gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaModule.java Change-Id: If957ce2eeb9b1de4ed2b134b0db129c336900442
This commit is contained in:
@@ -1040,6 +1040,16 @@ By default unset, as the git daemon must be configured externally
|
||||
by the system administrator, and might not even be running on the
|
||||
same host as Gerrit.
|
||||
|
||||
[[gerrit.gitHttpUrl]]gerrit.gitHttpUrl::
|
||||
+
|
||||
Optional base URL for repositories available over the HTTP
|
||||
protocol. For example, set this to `http://mirror.example.com/base/`
|
||||
to have Gerrit display URLs from this server, rather than itself.
|
||||
+
|
||||
By default unset, as the HTTP daemon must be configured externally
|
||||
by the system administrator, and might not even be running on the
|
||||
same host as Gerrit.
|
||||
|
||||
[[gerrit.replicateOnStartup]]gerrit.replicateOnStartup::
|
||||
+
|
||||
If true, replicates to all remotes on startup to ensure they are
|
||||
@@ -1073,9 +1083,10 @@ For example, "?p=$project.git;h=$commit".
|
||||
[[gitweb.type]]gitweb.type::
|
||||
+
|
||||
Optional type of affiliated gitweb service. This allows using
|
||||
alternatives to gitweb, such as cgit.
|
||||
alternatives to gitweb, such as cgit. If set to disabled there
|
||||
is no gitweb hyperlinking support.
|
||||
+
|
||||
Valid values are `gitweb`, `cgit` or `custom`.
|
||||
Valid values are `gitweb`, `cgit`, `disabled` or `custom`.
|
||||
|
||||
[[gitweb.type]]gitweb.revision::
|
||||
+
|
||||
@@ -1656,6 +1667,17 @@ A name of a group which exists in the database. Zero, one or many
|
||||
groups are allowed. Each on its own line. Groups which don't exist
|
||||
in the database are ignored.
|
||||
|
||||
[[rules]]Section rules
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
[[rules.enable]]rules.enable::
|
||||
+
|
||||
If true, Gerrit will load and excute 'rules.pl' files in each
|
||||
project's refs/meta/config branch, if present. When set to false,
|
||||
only the default internal rules will be used.
|
||||
+
|
||||
Default is true, to execute project specific rules.
|
||||
|
||||
[[sendemail]]Section sendemail
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -1767,6 +1789,25 @@ should expire.
|
||||
+
|
||||
By default, unset, so no Expiry-Date header is generated.
|
||||
|
||||
|
||||
[[site]]Section site
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
[[site.checkUserAgent]]site.checkUserAgent::
|
||||
+
|
||||
If true the server checks the User-Agent HTTP header and sends the
|
||||
correct JavaScript to the client as part of the initial page load.
|
||||
This usually reduces a round-trip for the client, allowing the UI to
|
||||
start more quickly. If false, a tiny JavaScript loader is sent to the
|
||||
client instead to determine the correct code to use. Default is true.
|
||||
|
||||
[[site.refreshHeaderFooter]]site.refreshHeaderFooter::
|
||||
+
|
||||
If true the server checks the site header, footer and CSS files for
|
||||
updated versions. If false, a server restart is required to change
|
||||
any of these resources. Default is true, allowing automatic reloads.
|
||||
|
||||
|
||||
[[sshd]] Section sshd
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -1786,6 +1827,8 @@ default of 29418.
|
||||
If multiple values are supplied, the daemon will listen on all
|
||||
of them.
|
||||
+
|
||||
To disable the internal SSHD, set listenAddress to `off`.
|
||||
+
|
||||
By default, *:29418.
|
||||
|
||||
[[sshd.advertisedAddress]]sshd.advertisedAddress::
|
||||
|
@@ -52,6 +52,18 @@ limitations under the License.
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
@@ -26,6 +26,7 @@ import java.util.Set;
|
||||
|
||||
public class GerritConfig implements Cloneable {
|
||||
protected String registerUrl;
|
||||
protected String httpPasswordUrl;
|
||||
protected List<OpenIdProviderPattern> allowedOpenIDs;
|
||||
|
||||
protected GitwebLink gitweb;
|
||||
@@ -35,6 +36,7 @@ public class GerritConfig implements Cloneable {
|
||||
protected AuthType authType;
|
||||
protected Set<DownloadScheme> downloadSchemes;
|
||||
protected String gitDaemonUrl;
|
||||
protected String gitHttpUrl;
|
||||
protected String sshdAddress;
|
||||
protected Project.NameKey wildProject;
|
||||
protected ApprovalTypes approvalTypes;
|
||||
@@ -52,6 +54,14 @@ public class GerritConfig implements Cloneable {
|
||||
registerUrl = u;
|
||||
}
|
||||
|
||||
public String getHttpPasswordUrl() {
|
||||
return httpPasswordUrl;
|
||||
}
|
||||
|
||||
public void setHttpPasswordUrl(String url) {
|
||||
httpPasswordUrl = url;
|
||||
}
|
||||
|
||||
public List<OpenIdProviderPattern> getAllowedOpenIDs() {
|
||||
return allowedOpenIDs;
|
||||
}
|
||||
@@ -111,6 +121,17 @@ public class GerritConfig implements Cloneable {
|
||||
gitDaemonUrl = url;
|
||||
}
|
||||
|
||||
public String getGitHttpUrl() {
|
||||
return gitHttpUrl;
|
||||
}
|
||||
|
||||
public void setGitHttpUrl(String url) {
|
||||
if (url != null && !url.endsWith("/")) {
|
||||
url += "/";
|
||||
}
|
||||
gitHttpUrl = url;
|
||||
}
|
||||
|
||||
public String getSshdAddress() {
|
||||
return sshdAddress;
|
||||
}
|
||||
|
@@ -44,6 +44,9 @@ public class GitWebType {
|
||||
// The custom name is not defined, let's keep the old style of using GitWeb
|
||||
type.setLinkName("gitweb");
|
||||
|
||||
} else if (name.equalsIgnoreCase("disabled")) {
|
||||
type = null;
|
||||
|
||||
} else {
|
||||
type = null;
|
||||
}
|
||||
|
@@ -21,6 +21,7 @@ import com.google.gerrit.reviewdb.AccountDiffPreference;
|
||||
public class HostPageData {
|
||||
public Account account;
|
||||
public AccountDiffPreference accountDiffPref;
|
||||
public String xsrfToken;
|
||||
public GerritConfig config;
|
||||
public Theme theme;
|
||||
|
||||
|
@@ -73,13 +73,13 @@ public class Gerrit implements EntryPoint {
|
||||
public static final GerritResources RESOURCES =
|
||||
GWT.create(GerritResources.class);
|
||||
public static final SystemInfoService SYSTEM_SVC;
|
||||
private static final String SESSION_COOKIE = "GerritAccount";
|
||||
|
||||
private static String myHost;
|
||||
private static GerritConfig myConfig;
|
||||
private static HostPageData.Theme myTheme;
|
||||
private static Account myAccount;
|
||||
private static AccountDiffPreference myAccountDiffPref;
|
||||
private static String xsrfToken;
|
||||
|
||||
private static MorphingTabPanel menuLeft;
|
||||
private static LinkMenuBar menuRight;
|
||||
@@ -241,12 +241,16 @@ public class Gerrit implements EntryPoint {
|
||||
}
|
||||
|
||||
/** Sign the user into the application. */
|
||||
public static void doSignIn(final String token) {
|
||||
public static void doSignIn(String token) {
|
||||
switch (myConfig.getAuthType()) {
|
||||
case HTTP:
|
||||
case HTTP_LDAP:
|
||||
case CLIENT_SSL_CERT_LDAP:
|
||||
Location.assign(Location.getPath() + "login/" + token);
|
||||
case CUSTOM_EXTENSION:
|
||||
if (!token.startsWith("/")) {
|
||||
token = "/" + token;
|
||||
}
|
||||
Location.assign(Location.getPath() + "login" + token);
|
||||
break;
|
||||
|
||||
case DEVELOPMENT_BECOME_ANY_ACCOUNT:
|
||||
@@ -265,10 +269,15 @@ public class Gerrit implements EntryPoint {
|
||||
}
|
||||
|
||||
static void deleteSessionCookie() {
|
||||
Cookies.removeCookie(SESSION_COOKIE);
|
||||
myAccount = null;
|
||||
myAccountDiffPref = null;
|
||||
xsrfToken = null;
|
||||
refreshMenuBar();
|
||||
|
||||
// If the cookie was HttpOnly, this request to delete it will
|
||||
// most likely not be successful. We can try anyway though.
|
||||
//
|
||||
Cookies.removeCookie("GerritAccount");
|
||||
}
|
||||
|
||||
public void onModuleLoad() {
|
||||
@@ -305,9 +314,11 @@ public class Gerrit implements EntryPoint {
|
||||
myTheme = result.theme;
|
||||
if (result.account != null) {
|
||||
myAccount = result.account;
|
||||
xsrfToken = result.xsrfToken;
|
||||
}
|
||||
if (result.accountDiffPref != null) {
|
||||
myAccountDiffPref = result.accountDiffPref;
|
||||
applyUserPreferences();
|
||||
}
|
||||
onModuleLoad2();
|
||||
}
|
||||
@@ -436,7 +447,7 @@ public class Gerrit implements EntryPoint {
|
||||
JsonUtil.setDefaultXsrfManager(new XsrfManager() {
|
||||
@Override
|
||||
public String getToken(JsonDefTarget proxy) {
|
||||
return Cookies.getCookie(SESSION_COOKIE);
|
||||
return xsrfToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -553,6 +564,7 @@ public class Gerrit implements EntryPoint {
|
||||
|
||||
case LDAP:
|
||||
case LDAP_BIND:
|
||||
case CUSTOM_EXTENSION:
|
||||
if (cfg.getRegisterUrl() != null) {
|
||||
menuRight.add(anchor(C.menuRegister(), cfg.getRegisterUrl()));
|
||||
}
|
||||
|
@@ -56,6 +56,7 @@ public interface AccountConstants extends Constants {
|
||||
String buttonChangeUserName();
|
||||
String buttonClearPassword();
|
||||
String buttonGeneratePassword();
|
||||
String linkObtainPassword();
|
||||
String invalidUserName();
|
||||
String invalidUserEmail();
|
||||
|
||||
|
@@ -37,6 +37,7 @@ buttonSetUserName = Select Username
|
||||
buttonChangeUserName = Change Username
|
||||
buttonClearPassword = Clear Password
|
||||
buttonGeneratePassword = Generate Password
|
||||
linkObtainPassword = Obtain Password
|
||||
invalidUserName = Username must contain only letters, numbers, _, - or .
|
||||
invalidUserEmail = Email format is wrong.
|
||||
sshKeyInvalid = Invalid Key
|
||||
|
@@ -23,11 +23,12 @@ import com.google.gerrit.reviewdb.AccountExternalId;
|
||||
import com.google.gwt.event.dom.client.ClickEvent;
|
||||
import com.google.gwt.event.dom.client.ClickHandler;
|
||||
import com.google.gwt.i18n.client.LocaleInfo;
|
||||
import com.google.gwt.user.client.ui.Anchor;
|
||||
import com.google.gwt.user.client.ui.Button;
|
||||
import com.google.gwt.user.client.ui.FlowPanel;
|
||||
import com.google.gwt.user.client.ui.Grid;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.google.gwtexpui.clippy.client.CopyableLabel;
|
||||
|
||||
import java.util.List;
|
||||
@@ -42,6 +43,16 @@ public class MyPasswordScreen extends SettingsScreen {
|
||||
protected void onInitUI() {
|
||||
super.onInitUI();
|
||||
|
||||
String url = Gerrit.getConfig().getHttpPasswordUrl();
|
||||
if (url != null) {
|
||||
Anchor link = new Anchor();
|
||||
link.setText(Util.C.linkObtainPassword());
|
||||
link.setHref(url);
|
||||
link.setTarget("_blank");
|
||||
add(link);
|
||||
return;
|
||||
}
|
||||
|
||||
password = new CopyableLabel("");
|
||||
password.addStyleName(Gerrit.RESOURCES.css().accountPassword());
|
||||
|
||||
@@ -84,6 +95,11 @@ public class MyPasswordScreen extends SettingsScreen {
|
||||
protected void onLoad() {
|
||||
super.onLoad();
|
||||
|
||||
if (password == null) {
|
||||
display();
|
||||
return;
|
||||
}
|
||||
|
||||
enableUI(false);
|
||||
Util.ACCOUNT_SEC
|
||||
.myExternalIds(new ScreenLoadCallback<List<AccountExternalId>>(this) {
|
||||
|
@@ -99,18 +99,20 @@ public class RegisterScreen extends AccountScreen {
|
||||
formBody.add(fp);
|
||||
}
|
||||
|
||||
final FlowPanel sshKeyGroup = new FlowPanel();
|
||||
sshKeyGroup.setStyleName(Gerrit.RESOURCES.css().registerScreenSection());
|
||||
sshKeyGroup.add(new SmallHeading(Util.C.welcomeSshKeyHeading()));
|
||||
final HTML whySshKey = new HTML(Util.C.welcomeSshKeyText());
|
||||
whySshKey.setStyleName(Gerrit.RESOURCES.css().registerScreenExplain());
|
||||
sshKeyGroup.add(whySshKey);
|
||||
sshKeyGroup.add(new SshPanel() {
|
||||
{
|
||||
setKeyTableVisible(false);
|
||||
}
|
||||
});
|
||||
formBody.add(sshKeyGroup);
|
||||
if (Gerrit.getConfig().getSshdAddress() != null) {
|
||||
final FlowPanel sshKeyGroup = new FlowPanel();
|
||||
sshKeyGroup.setStyleName(Gerrit.RESOURCES.css().registerScreenSection());
|
||||
sshKeyGroup.add(new SmallHeading(Util.C.welcomeSshKeyHeading()));
|
||||
final HTML whySshKey = new HTML(Util.C.welcomeSshKeyText());
|
||||
whySshKey.setStyleName(Gerrit.RESOURCES.css().registerScreenExplain());
|
||||
sshKeyGroup.add(whySshKey);
|
||||
sshKeyGroup.add(new SshPanel() {
|
||||
{
|
||||
setKeyTableVisible(false);
|
||||
}
|
||||
});
|
||||
formBody.add(sshKeyGroup);
|
||||
}
|
||||
|
||||
final FlowPanel choices = new FlowPanel();
|
||||
choices.setStyleName(Gerrit.RESOURCES.css().registerScreenNextLinks());
|
||||
|
@@ -26,7 +26,9 @@ public abstract class SettingsScreen extends MenuScreen {
|
||||
link(Util.C.tabPreferences(), PageLinks.SETTINGS_PREFERENCES);
|
||||
link(Util.C.tabWatchedProjects(), PageLinks.SETTINGS_PROJECTS);
|
||||
link(Util.C.tabContactInformation(), PageLinks.SETTINGS_CONTACT);
|
||||
link(Util.C.tabSshKeys(), PageLinks.SETTINGS_SSHKEYS);
|
||||
if (Gerrit.getConfig().getSshdAddress() != null) {
|
||||
link(Util.C.tabSshKeys(), PageLinks.SETTINGS_SSHKEYS);
|
||||
}
|
||||
link(Util.C.tabHttpAccess(), PageLinks.SETTINGS_HTTP_PASSWORD);
|
||||
link(Util.C.tabWebIdentities(), PageLinks.SETTINGS_WEBIDENT);
|
||||
link(Util.C.tabMyGroups(), PageLinks.SETTINGS_MYGROUPS);
|
||||
|
@@ -205,8 +205,12 @@ class PatchSetComplexDisclosurePanel extends ComplexDisclosurePanel implements O
|
||||
&& (allowedSchemes.contains(DownloadScheme.ANON_HTTP) ||
|
||||
allowedSchemes.contains(DownloadScheme.DEFAULT_DOWNLOADS))) {
|
||||
StringBuilder r = new StringBuilder();
|
||||
r.append(GWT.getHostPageBaseURL());
|
||||
r.append("p/");
|
||||
if (Gerrit.getConfig().getGitHttpUrl() != null) {
|
||||
r.append(Gerrit.getConfig().getGitHttpUrl());
|
||||
} else {
|
||||
r.append(GWT.getHostPageBaseURL());
|
||||
r.append("p/");
|
||||
}
|
||||
r.append(projectName);
|
||||
r.append(" ");
|
||||
r.append(patchSet.getRefName());
|
||||
@@ -242,24 +246,29 @@ class PatchSetComplexDisclosurePanel extends ComplexDisclosurePanel implements O
|
||||
&& Gerrit.getUserAccount().getUserName().length() > 0
|
||||
&& (allowedSchemes.contains(DownloadScheme.HTTP) ||
|
||||
allowedSchemes.contains(DownloadScheme.DEFAULT_DOWNLOADS))) {
|
||||
String base = GWT.getHostPageBaseURL();
|
||||
int p = base.indexOf("://");
|
||||
int s = base.indexOf('/', p + 3);
|
||||
if (s < 0) {
|
||||
s = base.length();
|
||||
}
|
||||
String host = base.substring(p + 3, s);
|
||||
if (host.contains("@")) {
|
||||
host = host.substring(host.indexOf('@') + 1);
|
||||
}
|
||||
|
||||
final StringBuilder r = new StringBuilder();
|
||||
r.append(base.substring(0, p + 3));
|
||||
r.append(Gerrit.getUserAccount().getUserName());
|
||||
r.append('@');
|
||||
r.append(host);
|
||||
r.append(base.substring(s));
|
||||
r.append("p/");
|
||||
if (Gerrit.getConfig().getGitHttpUrl() != null
|
||||
&& changeDetail.isAllowsAnonymous()) {
|
||||
r.append(Gerrit.getConfig().getGitHttpUrl());
|
||||
} else {
|
||||
String base = GWT.getHostPageBaseURL();
|
||||
int p = base.indexOf("://");
|
||||
int s = base.indexOf('/', p + 3);
|
||||
if (s < 0) {
|
||||
s = base.length();
|
||||
}
|
||||
String host = base.substring(p + 3, s);
|
||||
if (host.contains("@")) {
|
||||
host = host.substring(host.indexOf('@') + 1);
|
||||
}
|
||||
|
||||
r.append(base.substring(0, p + 3));
|
||||
r.append(Gerrit.getUserAccount().getUserName());
|
||||
r.append('@');
|
||||
r.append(host);
|
||||
r.append(base.substring(s));
|
||||
r.append("p/");
|
||||
}
|
||||
r.append(projectName);
|
||||
r.append(" ");
|
||||
r.append(patchSet.getRefName());
|
||||
|
@@ -93,4 +93,20 @@ limitations under the License.
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
@@ -0,0 +1,218 @@
|
||||
// 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.httpd;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.HOURS;
|
||||
|
||||
import com.google.gerrit.httpd.WebSessionManager.Key;
|
||||
import com.google.gerrit.httpd.WebSessionManager.Val;
|
||||
import com.google.gerrit.reviewdb.Account;
|
||||
import com.google.gerrit.reviewdb.AccountExternalId;
|
||||
import com.google.gerrit.server.AccessPath;
|
||||
import com.google.gerrit.server.AnonymousUser;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.account.AuthResult;
|
||||
import com.google.gerrit.server.cache.Cache;
|
||||
import com.google.gerrit.server.cache.CacheModule;
|
||||
import com.google.gerrit.server.cache.EvictionPolicy;
|
||||
import com.google.gerrit.server.config.AuthConfig;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.servlet.RequestScoped;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
@RequestScoped
|
||||
public final class CacheBasedWebSession implements WebSession {
|
||||
private static final String ACCOUNT_COOKIE = "GerritAccount";
|
||||
|
||||
public static Module module() {
|
||||
return new CacheModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
final String cacheName = WebSessionManager.CACHE_NAME;
|
||||
final TypeLiteral<Cache<Key, Val>> type =
|
||||
new TypeLiteral<Cache<Key, Val>>() {};
|
||||
disk(type, cacheName) //
|
||||
.memoryLimit(1024) // reasonable default for many sites
|
||||
.maxAge(12, HOURS) // expire sessions if they are inactive
|
||||
.evictionPolicy(EvictionPolicy.LRU) // keep most recently used
|
||||
;
|
||||
bind(WebSessionManager.class);
|
||||
bind(WebSession.class)
|
||||
.to(CacheBasedWebSession.class)
|
||||
.in(RequestScoped.class);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private final HttpServletRequest request;
|
||||
private final HttpServletResponse response;
|
||||
private final WebSessionManager manager;
|
||||
private final AuthConfig authConfig;
|
||||
private final Provider<AnonymousUser> anonymousProvider;
|
||||
private final IdentifiedUser.RequestFactory identified;
|
||||
private AccessPath accessPath = AccessPath.WEB_UI;
|
||||
private Cookie outCookie;
|
||||
|
||||
private Key key;
|
||||
private Val val;
|
||||
|
||||
@Inject
|
||||
CacheBasedWebSession(final HttpServletRequest request,
|
||||
final HttpServletResponse response, final WebSessionManager manager,
|
||||
final AuthConfig authConfig,
|
||||
final Provider<AnonymousUser> anonymousProvider,
|
||||
final IdentifiedUser.RequestFactory identified) {
|
||||
this.request = request;
|
||||
this.response = response;
|
||||
this.manager = manager;
|
||||
this.authConfig = authConfig;
|
||||
this.anonymousProvider = anonymousProvider;
|
||||
this.identified = identified;
|
||||
|
||||
final String cookie = readCookie();
|
||||
if (cookie != null) {
|
||||
key = new Key(cookie);
|
||||
val = manager.get(key);
|
||||
} else {
|
||||
key = null;
|
||||
val = null;
|
||||
}
|
||||
|
||||
if (isSignedIn() && val.needsCookieRefresh()) {
|
||||
// Cookie is more than half old. Send the cookie again to the
|
||||
// client with an updated expiration date. We don't dare to
|
||||
// change the key token here because there may be other RPCs
|
||||
// queued up in the browser whose xsrfKey would not get updated
|
||||
// with the new token, causing them to fail.
|
||||
//
|
||||
val = manager.createVal(key, val);
|
||||
saveCookie();
|
||||
}
|
||||
}
|
||||
|
||||
private String readCookie() {
|
||||
final Cookie[] all = request.getCookies();
|
||||
if (all != null) {
|
||||
for (final Cookie c : all) {
|
||||
if (ACCOUNT_COOKIE.equals(c.getName())) {
|
||||
final String v = c.getValue();
|
||||
return v != null && !"".equals(v) ? v : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isSignedIn() {
|
||||
return val != null;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return isSignedIn() ? val.getXsrfToken() : null;
|
||||
}
|
||||
|
||||
public boolean isTokenValid(final String inputToken) {
|
||||
return isSignedIn() //
|
||||
&& val.getXsrfToken() != null //
|
||||
&& val.getXsrfToken().equals(inputToken);
|
||||
}
|
||||
|
||||
public AccountExternalId.Key getLastLoginExternalId() {
|
||||
return val != null ? val.getExternalId() : null;
|
||||
}
|
||||
|
||||
public CurrentUser getCurrentUser() {
|
||||
if (isSignedIn()) {
|
||||
return identified.create(accessPath, val.getAccountId());
|
||||
}
|
||||
return anonymousProvider.get();
|
||||
}
|
||||
|
||||
public void login(final AuthResult res, final boolean rememberMe) {
|
||||
final Account.Id id = res.getAccountId();
|
||||
final AccountExternalId.Key identity = res.getExternalId();
|
||||
|
||||
if (val != null) {
|
||||
manager.destroy(key);
|
||||
}
|
||||
|
||||
key = manager.createKey(id);
|
||||
val = manager.createVal(key, id, rememberMe, identity, null);
|
||||
saveCookie();
|
||||
}
|
||||
|
||||
/** Change the access path from the default of {@link AccessPath#WEB_UI}. */
|
||||
public void setAccessPath(AccessPath path) {
|
||||
accessPath = path;
|
||||
}
|
||||
|
||||
/** Set the user account for this current request only. */
|
||||
public void setUserAccountId(Account.Id id) {
|
||||
key = new Key("id:" + id);
|
||||
val = new Val(id, 0, false, null, "");
|
||||
}
|
||||
|
||||
public void logout() {
|
||||
if (val != null) {
|
||||
manager.destroy(key);
|
||||
key = null;
|
||||
val = null;
|
||||
saveCookie();
|
||||
}
|
||||
}
|
||||
|
||||
private void saveCookie() {
|
||||
final String token;
|
||||
final int ageSeconds;
|
||||
|
||||
if (key == null) {
|
||||
token = "";
|
||||
ageSeconds = 0 /* erase at client */;
|
||||
} else {
|
||||
token = key.getToken();
|
||||
ageSeconds = manager.getCookieAge(val);
|
||||
}
|
||||
|
||||
String path = authConfig.getCookiePath();
|
||||
if (path == null || path.isEmpty()) {
|
||||
path = request.getContextPath();
|
||||
if (path.isEmpty()) {
|
||||
path = "/";
|
||||
}
|
||||
}
|
||||
|
||||
if (outCookie != null) {
|
||||
throw new IllegalStateException("Cookie " + ACCOUNT_COOKIE + " was set");
|
||||
}
|
||||
|
||||
outCookie = new Cookie(ACCOUNT_COOKIE, token);
|
||||
outCookie.setSecure(isSecure(request));
|
||||
outCookie.setPath(path);
|
||||
outCookie.setMaxAge(ageSeconds);
|
||||
outCookie.setSecure(authConfig.getCookieSecure());
|
||||
response.addCookie(outCookie);
|
||||
}
|
||||
|
||||
private static boolean isSecure(final HttpServletRequest req) {
|
||||
return req.isSecure() || "https".equals(req.getScheme());
|
||||
}
|
||||
}
|
@@ -94,10 +94,16 @@ class GerritConfigProvider implements Provider<GerritConfig> {
|
||||
case LDAP_BIND:
|
||||
config.setRegisterUrl(cfg.getString("auth", null, "registerurl"));
|
||||
break;
|
||||
|
||||
case CUSTOM_EXTENSION:
|
||||
config.setRegisterUrl(cfg.getString("auth", null, "registerurl"));
|
||||
config.setHttpPasswordUrl(cfg.getString("auth", null, "httpPasswordUrl"));
|
||||
break;
|
||||
}
|
||||
config.setUseContributorAgreements(cfg.getBoolean("auth",
|
||||
"contributoragreements", false));
|
||||
config.setGitDaemonUrl(cfg.getString("gerrit", null, "canonicalgiturl"));
|
||||
config.setGitHttpUrl(cfg.getString("gerrit", null, "gitHttpUrl"));
|
||||
config.setUseContactInfo(contactStore != null && contactStore.isEnabled());
|
||||
config.setDownloadSchemes(schemeConfig.getDownloadScheme());
|
||||
config.setAuthType(authConfig.getAuthType());
|
||||
|
@@ -0,0 +1,42 @@
|
||||
// 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.httpd;
|
||||
|
||||
import com.google.gerrit.server.config.AuthConfig;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.servlet.ServletModule;
|
||||
|
||||
/**
|
||||
* Configures Git access over {@code "/p/PROJECT_NAME"} URLs.
|
||||
*/
|
||||
public class GitOverHttpModule extends ServletModule {
|
||||
private final AuthConfig authConfig;
|
||||
|
||||
@Inject
|
||||
GitOverHttpModule(AuthConfig authConfig) {
|
||||
this.authConfig = authConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureServlets() {
|
||||
filter("/p/*").through(ProjectAccessPathFilter.class);
|
||||
if (authConfig.isTrustContainerAuth()) {
|
||||
filter("/p/*").through(ContainerAuthFilter.class);
|
||||
} else {
|
||||
filter("/p/*").through(ProjectDigestFilter.class);
|
||||
}
|
||||
serve("/p/*").with(ProjectServlet.class);
|
||||
}
|
||||
}
|
@@ -41,6 +41,15 @@ public class GitWebConfig {
|
||||
final String cfgCgi = cfg.getString("gitweb", null, "cgi");
|
||||
|
||||
type = GitWebType.fromName(cfg.getString("gitweb", null, "type"));
|
||||
if (type == null) {
|
||||
url = null;
|
||||
gitweb_cgi = null;
|
||||
gitweb_css = null;
|
||||
gitweb_js = null;
|
||||
git_logo_png = null;
|
||||
return;
|
||||
}
|
||||
|
||||
type.setLinkName(cfg.getString("gitweb", null, "linkname"));
|
||||
type.setBranch(cfg.getString("gitweb", null, "branch"));
|
||||
type.setProject(cfg.getString("gitweb", null, "project"));
|
||||
@@ -58,7 +67,7 @@ public class GitWebConfig {
|
||||
}
|
||||
|
||||
if ((cfgUrl != null && cfgUrl.isEmpty())
|
||||
|| (cfgCgi != null && cfgCgi.isEmpty()) || type == null) {
|
||||
|| (cfgCgi != null && cfgCgi.isEmpty())) {
|
||||
// Either setting was explicitly set to the empty string disabling
|
||||
// gitweb for this server. Disable the configuration.
|
||||
//
|
||||
|
@@ -30,7 +30,7 @@ import javax.servlet.ServletResponse;
|
||||
|
||||
/** Set the WebSession to {@link AccessPath#GIT}. */
|
||||
@Singleton
|
||||
class ProjectAccessPathFilter implements Filter {
|
||||
public class ProjectAccessPathFilter implements Filter {
|
||||
private final Provider<WebSession> session;
|
||||
|
||||
@Inject
|
||||
|
@@ -24,7 +24,6 @@ import com.google.gerrit.httpd.raw.SshInfoServlet;
|
||||
import com.google.gerrit.httpd.raw.StaticServlet;
|
||||
import com.google.gerrit.httpd.raw.ToolServlet;
|
||||
import com.google.gerrit.reviewdb.Change;
|
||||
import com.google.gerrit.server.config.AuthConfig;
|
||||
import com.google.gwtexpui.server.CacheControlFilter;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.Provider;
|
||||
@@ -38,12 +37,6 @@ import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
class UrlModule extends ServletModule {
|
||||
private final AuthConfig authConfig;
|
||||
|
||||
public UrlModule(AuthConfig authConfig) {
|
||||
this.authConfig = authConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureServlets() {
|
||||
filter("/*").through(Key.get(CacheControlFilter.class));
|
||||
@@ -60,14 +53,6 @@ class UrlModule extends ServletModule {
|
||||
serve("/static/*").with(StaticServlet.class);
|
||||
serve("/tools/*").with(ToolServlet.class);
|
||||
|
||||
filter("/p/*").through(ProjectAccessPathFilter.class);
|
||||
if (authConfig.isTrustContainerAuth()) {
|
||||
filter("/p/*").through(ContainerAuthFilter.class);
|
||||
} else {
|
||||
filter("/p/*").through(ProjectDigestFilter.class);
|
||||
}
|
||||
serve("/p/*").with(ProjectServlet.class);
|
||||
|
||||
serve("/Main.class").with(notFound());
|
||||
serve("/com/google/gerrit/launcher/*").with(notFound());
|
||||
serve("/servlet/*").with(notFound());
|
||||
|
@@ -24,7 +24,6 @@ import com.google.gerrit.httpd.auth.ldap.LdapAuthModule;
|
||||
import com.google.gerrit.httpd.auth.openid.OpenIdModule;
|
||||
import com.google.gerrit.httpd.gitweb.GitWebModule;
|
||||
import com.google.gerrit.httpd.rpc.UiRpcModule;
|
||||
import com.google.gerrit.reviewdb.AuthType;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.RemotePeer;
|
||||
@@ -38,12 +37,9 @@ import com.google.gerrit.server.config.FactoryModule;
|
||||
import com.google.gerrit.server.config.GerritRequestModule;
|
||||
import com.google.gerrit.server.contact.ContactStore;
|
||||
import com.google.gerrit.server.contact.ContactStoreProvider;
|
||||
import com.google.gerrit.server.ssh.SshInfo;
|
||||
import com.google.gerrit.server.ssh.SshKeyCache;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.ProvisionException;
|
||||
import com.google.inject.servlet.RequestScoped;
|
||||
import com.google.inject.servlet.ServletModule;
|
||||
@@ -53,20 +49,14 @@ import java.net.SocketAddress;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class WebModule extends FactoryModule {
|
||||
private final Provider<SshInfo> sshInfoProvider;
|
||||
private final Provider<SshKeyCache> sshKeyCacheProvider;
|
||||
private final AuthConfig authConfig;
|
||||
private final boolean wantSSL;
|
||||
private final GitWebConfig gitWebConfig;
|
||||
|
||||
@Inject
|
||||
WebModule(final Provider<SshInfo> sshInfoProvider,
|
||||
final Provider<SshKeyCache> sshKeyCacheProvider,
|
||||
final AuthConfig authConfig,
|
||||
WebModule(final AuthConfig authConfig,
|
||||
@CanonicalWebUrl @Nullable final String canonicalUrl,
|
||||
final Injector creatingInjector) {
|
||||
this.sshInfoProvider = sshInfoProvider;
|
||||
this.sshKeyCacheProvider = sshKeyCacheProvider;
|
||||
this.authConfig = authConfig;
|
||||
this.wantSSL = canonicalUrl != null && canonicalUrl.startsWith("https:");
|
||||
|
||||
@@ -120,18 +110,17 @@ public class WebModule extends FactoryModule {
|
||||
});
|
||||
break;
|
||||
|
||||
case CUSTOM_EXTENSION:
|
||||
break;
|
||||
default:
|
||||
throw new ProvisionException("Unsupported loginType: " + authConfig.getAuthType());
|
||||
}
|
||||
|
||||
install(new UrlModule(authConfig));
|
||||
install(new UrlModule());
|
||||
install(new UiRpcModule());
|
||||
install(new GerritRequestModule());
|
||||
install(new ProjectServlet.Module());
|
||||
|
||||
bind(SshInfo.class).toProvider(sshInfoProvider);
|
||||
bind(SshKeyCache.class).toProvider(sshKeyCacheProvider);
|
||||
|
||||
bind(GitWebConfig.class).toInstance(gitWebConfig);
|
||||
if (gitWebConfig.getGitwebCGI() != null) {
|
||||
install(new GitWebModule());
|
||||
@@ -151,8 +140,6 @@ public class WebModule extends FactoryModule {
|
||||
bind(SocketAddress.class).annotatedWith(RemotePeer.class).toProvider(
|
||||
HttpRemotePeerProvider.class).in(RequestScoped.class);
|
||||
|
||||
install(WebSession.module());
|
||||
|
||||
bind(CurrentUser.class).toProvider(HttpCurrentUserProvider.class).in(
|
||||
RequestScoped.class);
|
||||
bind(IdentifiedUser.class).toProvider(HttpIdentifiedUserProvider.class).in(
|
||||
|
@@ -14,194 +14,30 @@
|
||||
|
||||
package com.google.gerrit.httpd;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.HOURS;
|
||||
|
||||
import com.google.gerrit.httpd.WebSessionManager.Key;
|
||||
import com.google.gerrit.httpd.WebSessionManager.Val;
|
||||
import com.google.gerrit.reviewdb.Account;
|
||||
import com.google.gerrit.reviewdb.AccountExternalId;
|
||||
import com.google.gerrit.server.AccessPath;
|
||||
import com.google.gerrit.server.AnonymousUser;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.account.AuthResult;
|
||||
import com.google.gerrit.server.cache.Cache;
|
||||
import com.google.gerrit.server.cache.CacheModule;
|
||||
import com.google.gerrit.server.cache.EvictionPolicy;
|
||||
import com.google.gerrit.server.config.AuthConfig;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.servlet.RequestScoped;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
public interface WebSession {
|
||||
public boolean isSignedIn();
|
||||
|
||||
@RequestScoped
|
||||
public final class WebSession {
|
||||
private static final String ACCOUNT_COOKIE = "GerritAccount";
|
||||
public String getToken();
|
||||
|
||||
static Module module() {
|
||||
return new CacheModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
final String cacheName = WebSessionManager.CACHE_NAME;
|
||||
final TypeLiteral<Cache<Key, Val>> type =
|
||||
new TypeLiteral<Cache<Key, Val>>() {};
|
||||
disk(type, cacheName) //
|
||||
.memoryLimit(1024) // reasonable default for many sites
|
||||
.maxAge(12, HOURS) // expire sessions if they are inactive
|
||||
.evictionPolicy(EvictionPolicy.LRU) // keep most recently used
|
||||
;
|
||||
bind(WebSessionManager.class);
|
||||
bind(WebSession.class).in(RequestScoped.class);
|
||||
}
|
||||
};
|
||||
}
|
||||
public boolean isTokenValid(String inputToken);
|
||||
|
||||
private final HttpServletRequest request;
|
||||
private final HttpServletResponse response;
|
||||
private final WebSessionManager manager;
|
||||
private final AuthConfig authConfig;
|
||||
private final Provider<AnonymousUser> anonymousProvider;
|
||||
private final IdentifiedUser.RequestFactory identified;
|
||||
private AccessPath accessPath = AccessPath.WEB_UI;
|
||||
private Cookie outCookie;
|
||||
public AccountExternalId.Key getLastLoginExternalId();
|
||||
|
||||
private Key key;
|
||||
private Val val;
|
||||
public CurrentUser getCurrentUser();
|
||||
|
||||
@Inject
|
||||
WebSession(final HttpServletRequest request,
|
||||
final HttpServletResponse response, final WebSessionManager manager,
|
||||
final AuthConfig authConfig,
|
||||
final Provider<AnonymousUser> anonymousProvider,
|
||||
final IdentifiedUser.RequestFactory identified) {
|
||||
this.request = request;
|
||||
this.response = response;
|
||||
this.manager = manager;
|
||||
this.authConfig = authConfig;
|
||||
this.anonymousProvider = anonymousProvider;
|
||||
this.identified = identified;
|
||||
|
||||
final String cookie = readCookie();
|
||||
if (cookie != null) {
|
||||
key = new Key(cookie);
|
||||
val = manager.get(key);
|
||||
} else {
|
||||
key = null;
|
||||
val = null;
|
||||
}
|
||||
|
||||
if (isSignedIn() && val.needsCookieRefresh()) {
|
||||
// Cookie is more than half old. Send the cookie again to the
|
||||
// client with an updated expiration date. We don't dare to
|
||||
// change the key token here because there may be other RPCs
|
||||
// queued up in the browser whose xsrfKey would not get updated
|
||||
// with the new token, causing them to fail.
|
||||
//
|
||||
val = manager.createVal(key, val);
|
||||
saveCookie();
|
||||
}
|
||||
}
|
||||
|
||||
private String readCookie() {
|
||||
final Cookie[] all = request.getCookies();
|
||||
if (all != null) {
|
||||
for (final Cookie c : all) {
|
||||
if (ACCOUNT_COOKIE.equals(c.getName())) {
|
||||
final String v = c.getValue();
|
||||
return v != null && !"".equals(v) ? v : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isSignedIn() {
|
||||
return val != null;
|
||||
}
|
||||
|
||||
String getToken() {
|
||||
return isSignedIn() ? key.getToken() : null;
|
||||
}
|
||||
|
||||
public boolean isTokenValid(final String inputToken) {
|
||||
return isSignedIn() && key.getToken().equals(inputToken);
|
||||
}
|
||||
|
||||
public AccountExternalId.Key getLastLoginExternalId() {
|
||||
return val != null ? val.getExternalId() : null;
|
||||
}
|
||||
|
||||
CurrentUser getCurrentUser() {
|
||||
if (isSignedIn()) {
|
||||
return identified.create(accessPath, val.getAccountId());
|
||||
}
|
||||
return anonymousProvider.get();
|
||||
}
|
||||
|
||||
public void login(final AuthResult res, final boolean rememberMe) {
|
||||
final Account.Id id = res.getAccountId();
|
||||
final AccountExternalId.Key identity = res.getExternalId();
|
||||
|
||||
logout();
|
||||
|
||||
key = manager.createKey(id);
|
||||
val = manager.createVal(key, id, rememberMe, identity);
|
||||
saveCookie();
|
||||
}
|
||||
public void login(AuthResult res, boolean rememberMe);
|
||||
|
||||
/** Change the access path from the default of {@link AccessPath#WEB_UI}. */
|
||||
void setAccessPath(AccessPath path) {
|
||||
accessPath = path;
|
||||
}
|
||||
public void setAccessPath(AccessPath path);
|
||||
|
||||
/** Set the user account for this current request only. */
|
||||
void setUserAccountId(Account.Id id) {
|
||||
key = new Key("id:" + id);
|
||||
val = new Val(id, 0, false, null);
|
||||
}
|
||||
public void setUserAccountId(Account.Id id);
|
||||
|
||||
public void logout() {
|
||||
if (val != null) {
|
||||
manager.destroy(key);
|
||||
key = null;
|
||||
val = null;
|
||||
saveCookie();
|
||||
}
|
||||
}
|
||||
|
||||
private void saveCookie() {
|
||||
final String token;
|
||||
final int ageSeconds;
|
||||
|
||||
if (key == null) {
|
||||
token = "";
|
||||
ageSeconds = 0 /* erase at client */;
|
||||
} else {
|
||||
token = key.getToken();
|
||||
ageSeconds = manager.getCookieAge(val);
|
||||
}
|
||||
|
||||
if (outCookie == null) {
|
||||
String path = authConfig.getCookiePath();
|
||||
if (path == null || path.isEmpty()) {
|
||||
path = request.getContextPath();
|
||||
if (path.isEmpty()) {
|
||||
path = "/";
|
||||
}
|
||||
}
|
||||
outCookie = new Cookie(ACCOUNT_COOKIE, token);
|
||||
outCookie.setPath(path);
|
||||
outCookie.setMaxAge(ageSeconds);
|
||||
outCookie.setSecure(authConfig.getCookieSecure());
|
||||
response.addCookie(outCookie);
|
||||
} else {
|
||||
outCookie.setValue(token);
|
||||
outCookie.setMaxAge(ageSeconds);
|
||||
}
|
||||
}
|
||||
public void logout();
|
||||
}
|
||||
|
@@ -78,12 +78,13 @@ class WebSessionManager {
|
||||
final Account.Id who = val.getAccountId();
|
||||
final boolean remember = val.isPersistentCookie();
|
||||
final AccountExternalId.Key lastLogin = val.getExternalId();
|
||||
final String xsrfToken = val.getXsrfToken();
|
||||
|
||||
return createVal(key, who, remember, lastLogin);
|
||||
return createVal(key, who, remember, lastLogin, xsrfToken);
|
||||
}
|
||||
|
||||
Val createVal(final Key key, final Account.Id who, final boolean remember,
|
||||
final AccountExternalId.Key lastLogin) {
|
||||
final AccountExternalId.Key lastLogin, String xsrfToken) {
|
||||
// Refresh the cookie every hour or when it is half-expired.
|
||||
// This reduces the odds that the user session will be kicked
|
||||
// early but also avoids us needing to refresh the cookie on
|
||||
@@ -94,7 +95,17 @@ class WebSessionManager {
|
||||
final long refresh = Math.min(halfAgeRefresh, minRefresh);
|
||||
final long refreshCookieAt = now() + refresh;
|
||||
|
||||
final Val val = new Val(who, refreshCookieAt, remember, lastLogin);
|
||||
if (xsrfToken == null) {
|
||||
// If we don't yet have a token for this session, establish one.
|
||||
//
|
||||
final int nonceLen = 20;
|
||||
final ByteArrayOutputStream buf;
|
||||
final byte[] rnd = new byte[nonceLen];
|
||||
prng.nextBytes(rnd);
|
||||
xsrfToken = CookieBase64.encode(rnd);
|
||||
}
|
||||
|
||||
Val val = new Val(who, refreshCookieAt, remember, lastLogin, xsrfToken);
|
||||
self.put(key, val);
|
||||
return val;
|
||||
}
|
||||
@@ -162,13 +173,16 @@ class WebSessionManager {
|
||||
private transient long refreshCookieAt;
|
||||
private transient boolean persistentCookie;
|
||||
private transient AccountExternalId.Key externalId;
|
||||
private transient String xsrfToken;
|
||||
|
||||
Val(final Account.Id accountId, final long refreshCookieAt,
|
||||
final boolean persistentCookie, final AccountExternalId.Key externalId) {
|
||||
final boolean persistentCookie, final AccountExternalId.Key externalId,
|
||||
final String xsrfToken) {
|
||||
this.accountId = accountId;
|
||||
this.refreshCookieAt = refreshCookieAt;
|
||||
this.persistentCookie = persistentCookie;
|
||||
this.externalId = externalId;
|
||||
this.xsrfToken = xsrfToken;
|
||||
}
|
||||
|
||||
Account.Id getAccountId() {
|
||||
@@ -187,6 +201,10 @@ class WebSessionManager {
|
||||
return persistentCookie;
|
||||
}
|
||||
|
||||
String getXsrfToken() {
|
||||
return xsrfToken;
|
||||
}
|
||||
|
||||
private void writeObject(final ObjectOutputStream out) throws IOException {
|
||||
writeVarInt32(out, 1);
|
||||
writeVarInt32(out, accountId.get());
|
||||
@@ -202,6 +220,9 @@ class WebSessionManager {
|
||||
writeString(out, externalId.get());
|
||||
}
|
||||
|
||||
writeVarInt32(out, 5);
|
||||
writeString(out, xsrfToken);
|
||||
|
||||
writeVarInt32(out, 0);
|
||||
}
|
||||
|
||||
@@ -223,6 +244,9 @@ class WebSessionManager {
|
||||
case 4:
|
||||
externalId = new AccountExternalId.Key(readString(in));
|
||||
continue;
|
||||
case 5:
|
||||
xsrfToken = readString(in);
|
||||
continue;
|
||||
default:
|
||||
throw new IOException("Unknown tag found in object: " + tag);
|
||||
}
|
||||
|
@@ -0,0 +1,46 @@
|
||||
// Copyright (C) 2010 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.httpd;
|
||||
|
||||
import com.google.gerrit.server.ssh.SshInfo;
|
||||
import com.google.gerrit.server.ssh.SshKeyCache;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
|
||||
/**
|
||||
* Pulls objects from the SSH injector over the HTTP injector.
|
||||
* <p>
|
||||
* This mess is only necessary because we build up two different injectors, in
|
||||
* order to have different request scopes. But some HTTP RPCs can cause changes
|
||||
* to the SSH side of the house, and thus needs access to it.
|
||||
*/
|
||||
public class WebSshGlueModule extends AbstractModule {
|
||||
private final Provider<SshInfo> sshInfoProvider;
|
||||
private final Provider<SshKeyCache> sshKeyCacheProvider;
|
||||
|
||||
@Inject
|
||||
WebSshGlueModule(Provider<SshInfo> sshInfoProvider,
|
||||
Provider<SshKeyCache> sshKeyCacheProvider) {
|
||||
this.sshInfoProvider = sshInfoProvider;
|
||||
this.sshKeyCacheProvider = sshKeyCacheProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(SshInfo.class).toProvider(sshInfoProvider);
|
||||
bind(SshKeyCache.class).toProvider(sshKeyCacheProvider);
|
||||
}
|
||||
}
|
@@ -80,7 +80,7 @@ class HttpLoginServlet extends HttpServlet {
|
||||
protected void doGet(final HttpServletRequest req,
|
||||
final HttpServletResponse rsp) throws ServletException, IOException {
|
||||
final String token = getToken(req);
|
||||
if ("logout".equals(token) || "signout".equals(token)) {
|
||||
if ("/logout".equals(token) || "/signout".equals(token)) {
|
||||
req.getRequestDispatcher("/logout").forward(req, rsp);
|
||||
return;
|
||||
}
|
||||
@@ -166,6 +166,8 @@ class HttpLoginServlet extends HttpServlet {
|
||||
String token = req.getPathInfo();
|
||||
if (token == null || token.isEmpty()) {
|
||||
token = PageLinks.MINE;
|
||||
} else if (!token.startsWith("/")) {
|
||||
token = "/" + token;
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
@@ -77,6 +77,8 @@ class LoginRedirectServlet extends HttpServlet {
|
||||
String token = req.getPathInfo();
|
||||
if (token == null || token.isEmpty()) {
|
||||
token = PageLinks.MINE;
|
||||
} else if (!token.startsWith("/")) {
|
||||
token = "/" + token;
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
@@ -17,8 +17,10 @@ package com.google.gerrit.httpd.raw;
|
||||
import com.google.gerrit.common.data.GerritConfig;
|
||||
import com.google.gerrit.common.data.HostPageData;
|
||||
import com.google.gerrit.httpd.HtmlDomUtil;
|
||||
import com.google.gerrit.httpd.WebSession;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.gerrit.server.config.SitePaths;
|
||||
import com.google.gwt.user.server.rpc.RPCServletUtils;
|
||||
import com.google.gwtexpui.linker.server.Permutation;
|
||||
@@ -28,6 +30,7 @@ import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.slf4j.Logger;
|
||||
@@ -61,6 +64,7 @@ public class HostPageServlet extends HttpServlet {
|
||||
private static final String HPD_ID = "gerrit_hostpagedata";
|
||||
|
||||
private final Provider<CurrentUser> currentUser;
|
||||
private final Provider<WebSession> session;
|
||||
private final GerritConfig config;
|
||||
private final HostPageData.Theme signedOutTheme;
|
||||
private final HostPageData.Theme signedInTheme;
|
||||
@@ -68,17 +72,22 @@ public class HostPageServlet extends HttpServlet {
|
||||
private final Document template;
|
||||
private final String noCacheName;
|
||||
private final PermutationSelector selector;
|
||||
private final boolean refreshHeaderFooter;
|
||||
private volatile Page page;
|
||||
|
||||
@Inject
|
||||
HostPageServlet(final Provider<CurrentUser> cu, final SitePaths sp,
|
||||
final ThemeFactory themeFactory, final GerritConfig gc,
|
||||
final ServletContext servletContext) throws IOException, ServletException {
|
||||
HostPageServlet(final Provider<CurrentUser> cu, final Provider<WebSession> w,
|
||||
final SitePaths sp, final ThemeFactory themeFactory,
|
||||
final GerritConfig gc, final ServletContext servletContext,
|
||||
@GerritServerConfig final Config cfg)
|
||||
throws IOException, ServletException {
|
||||
currentUser = cu;
|
||||
session = w;
|
||||
config = gc;
|
||||
signedOutTheme = themeFactory.getSignedOutTheme();
|
||||
signedInTheme = themeFactory.getSignedInTheme();
|
||||
site = sp;
|
||||
refreshHeaderFooter = cfg.getBoolean("site", "refreshHeaderFooter", true);
|
||||
|
||||
final String pageName = "HostPage.html";
|
||||
template = HtmlDomUtil.parseFile(getClass(), pageName);
|
||||
@@ -95,7 +104,7 @@ public class HostPageServlet extends HttpServlet {
|
||||
|
||||
final String src = "gerrit/gerrit.nocache.js";
|
||||
selector = new PermutationSelector("gerrit");
|
||||
if (IS_DEV) {
|
||||
if (IS_DEV || !cfg.getBoolean("site", "checkUserAgent", true)) {
|
||||
noCacheName = src;
|
||||
} else {
|
||||
final Element devmode = HtmlDomUtil.find(template, "gerrit_gwtdevmode");
|
||||
@@ -136,7 +145,7 @@ public class HostPageServlet extends HttpServlet {
|
||||
|
||||
private Page get() {
|
||||
Page p = page;
|
||||
if (p.isStale()) {
|
||||
if (refreshHeaderFooter && p.isStale()) {
|
||||
final Page newPage;
|
||||
try {
|
||||
newPage = new Page();
|
||||
@@ -163,6 +172,10 @@ public class HostPageServlet extends HttpServlet {
|
||||
json(((IdentifiedUser) user).getAccount(), w);
|
||||
w.write(";");
|
||||
|
||||
w.write(HPD_ID + ".xsrfToken=");
|
||||
json(session.get().getToken(), w);
|
||||
w.write(";");
|
||||
|
||||
w.write(HPD_ID + ".accountDiffPref=");
|
||||
json(((IdentifiedUser) user).getAccountDiffPreference(), w);
|
||||
w.write(";");
|
||||
|
@@ -128,7 +128,7 @@ class PatchSetDetailFactory extends Handler<PatchSetDetail> {
|
||||
byKey.put(p.getKey(), p);
|
||||
}
|
||||
|
||||
for (final PatchLineComment c : db.patchComments().published(psIdNew)) {
|
||||
for (final PatchLineComment c : db.patchComments().publishedByPatchSet(psIdNew)) {
|
||||
final Patch p = byKey.get(c.getKey().getParentKey());
|
||||
if (p != null) {
|
||||
p.setCommentCount(p.getCommentCount() + 1);
|
||||
@@ -138,7 +138,7 @@ class PatchSetDetailFactory extends Handler<PatchSetDetail> {
|
||||
detail = new PatchSetDetail();
|
||||
detail.setPatchSet(patchSet);
|
||||
|
||||
detail.setInfo(infoFactory.get(psIdNew));
|
||||
detail.setInfo(infoFactory.get(db, psIdNew));
|
||||
detail.setPatches(patches);
|
||||
|
||||
final CurrentUser user = control.getCurrentUser();
|
||||
@@ -148,7 +148,7 @@ class PatchSetDetailFactory extends Handler<PatchSetDetail> {
|
||||
// quickly locate where they have pending drafts, and review them.
|
||||
//
|
||||
final Account.Id me = ((IdentifiedUser) user).getAccountId();
|
||||
for (final PatchLineComment c : db.patchComments().draft(psIdNew, me)) {
|
||||
for (final PatchLineComment c : db.patchComments().draftByPatchSetAuthor(psIdNew, me)) {
|
||||
final Patch p = byKey.get(c.getKey().getParentKey());
|
||||
if (p != null) {
|
||||
p.setDraftCount(p.getDraftCount() + 1);
|
||||
|
@@ -83,8 +83,8 @@ final class PatchSetPublishDetailFactory extends Handler<PatchSetPublishDetail>
|
||||
final Change.Id changeId = patchSetId.getParentKey();
|
||||
final ChangeControl control = changeControlFactory.validateFor(changeId);
|
||||
change = control.getChange();
|
||||
patchSetInfo = infoFactory.get(patchSetId);
|
||||
drafts = db.patchComments().draft(patchSetId, user.getAccountId()).toList();
|
||||
patchSetInfo = infoFactory.get(db, patchSetId);
|
||||
drafts = db.patchComments().draftByPatchSetAuthor(patchSetId, user.getAccountId()).toList();
|
||||
|
||||
aic.want(change.getOwner());
|
||||
|
||||
|
@@ -110,18 +110,25 @@ class PatchDetailServiceImpl extends BaseServiceImplementation implements
|
||||
final AsyncCallback<VoidResult> callback) {
|
||||
run(callback, new Action<VoidResult>() {
|
||||
public VoidResult run(ReviewDb db) throws OrmException, Failure {
|
||||
final PatchLineComment comment = db.patchComments().get(commentKey);
|
||||
if (comment == null) {
|
||||
throw new Failure(new NoSuchEntityException());
|
||||
Change.Id id = commentKey.getParentKey().getParentKey().getParentKey();
|
||||
db.changes().beginTransaction(id);
|
||||
try {
|
||||
final PatchLineComment comment = db.patchComments().get(commentKey);
|
||||
if (comment == null) {
|
||||
throw new Failure(new NoSuchEntityException());
|
||||
}
|
||||
if (!getAccountId().equals(comment.getAuthor())) {
|
||||
throw new Failure(new NoSuchEntityException());
|
||||
}
|
||||
if (comment.getStatus() != PatchLineComment.Status.DRAFT) {
|
||||
throw new Failure(new IllegalStateException("Comment published"));
|
||||
}
|
||||
db.patchComments().delete(Collections.singleton(comment));
|
||||
db.commit();
|
||||
return VoidResult.INSTANCE;
|
||||
} finally {
|
||||
db.rollback();
|
||||
}
|
||||
if (!getAccountId().equals(comment.getAuthor())) {
|
||||
throw new Failure(new NoSuchEntityException());
|
||||
}
|
||||
if (comment.getStatus() != PatchLineComment.Status.DRAFT) {
|
||||
throw new Failure(new IllegalStateException("Comment published"));
|
||||
}
|
||||
db.patchComments().delete(Collections.singleton(comment));
|
||||
return VoidResult.INSTANCE;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -142,14 +149,20 @@ class PatchDetailServiceImpl extends BaseServiceImplementation implements
|
||||
Account.Id account = getAccountId();
|
||||
AccountPatchReview.Key key =
|
||||
new AccountPatchReview.Key(patchKey, account);
|
||||
AccountPatchReview apr = db.accountPatchReviews().get(key);
|
||||
if (apr == null && reviewed) {
|
||||
db.accountPatchReviews().insert(
|
||||
Collections.singleton(new AccountPatchReview(patchKey, account)));
|
||||
} else if (apr != null && !reviewed) {
|
||||
db.accountPatchReviews().delete(Collections.singleton(apr));
|
||||
db.accounts().beginTransaction(account);
|
||||
try {
|
||||
AccountPatchReview apr = db.accountPatchReviews().get(key);
|
||||
if (apr == null && reviewed) {
|
||||
db.accountPatchReviews().insert(
|
||||
Collections.singleton(new AccountPatchReview(patchKey, account)));
|
||||
} else if (apr != null && !reviewed) {
|
||||
db.accountPatchReviews().delete(Collections.singleton(apr));
|
||||
}
|
||||
db.commit();
|
||||
return VoidResult.INSTANCE;
|
||||
} finally {
|
||||
db.rollback();
|
||||
}
|
||||
return VoidResult.INSTANCE;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@@ -287,7 +287,7 @@ class PatchScriptFactory extends Handler<PatchScript> {
|
||||
|
||||
private void loadPublished(final Map<Patch.Key, Patch> byKey,
|
||||
final AccountInfoCacheFactory aic, final String file) throws OrmException {
|
||||
for (PatchLineComment c : db.patchComments().published(changeId, file)) {
|
||||
for (PatchLineComment c : db.patchComments().publishedByChangeFile(changeId, file)) {
|
||||
if (comments.include(c)) {
|
||||
aic.want(c.getAuthor());
|
||||
}
|
||||
@@ -303,7 +303,7 @@ class PatchScriptFactory extends Handler<PatchScript> {
|
||||
private void loadDrafts(final Map<Patch.Key, Patch> byKey,
|
||||
final AccountInfoCacheFactory aic, final Account.Id me, final String file)
|
||||
throws OrmException {
|
||||
for (PatchLineComment c : db.patchComments().draft(changeId, file, me)) {
|
||||
for (PatchLineComment c : db.patchComments().draftByChangeFileAuthor(changeId, file, me)) {
|
||||
if (comments.include(c)) {
|
||||
aic.want(me);
|
||||
}
|
||||
|
@@ -49,7 +49,6 @@ class SaveDraft extends Handler<PatchLineComment> {
|
||||
this.changeControlFactory = changeControlFactory;
|
||||
this.db = db;
|
||||
this.currentUser = currentUser;
|
||||
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
@@ -62,42 +61,50 @@ class SaveDraft extends Handler<PatchLineComment> {
|
||||
final Patch.Key patchKey = comment.getKey().getParentKey();
|
||||
final PatchSet.Id patchSetId = patchKey.getParentKey();
|
||||
final Change.Id changeId = patchKey.getParentKey().getParentKey();
|
||||
changeControlFactory.validateFor(changeId);
|
||||
if (db.patchSets().get(patchSetId) == null) {
|
||||
throw new NoSuchChangeException(changeId);
|
||||
}
|
||||
|
||||
final Account.Id me = currentUser.getAccountId();
|
||||
if (comment.getKey().get() == null) {
|
||||
if (comment.getLine() < 1) {
|
||||
throw new IllegalStateException("Comment line must be >= 1, not "
|
||||
+ comment.getLine());
|
||||
}
|
||||
|
||||
if (comment.getParentUuid() != null) {
|
||||
final PatchLineComment parent =
|
||||
db.patchComments().get(
|
||||
new PatchLineComment.Key(patchKey, comment.getParentUuid()));
|
||||
if (parent == null || parent.getSide() != comment.getSide()) {
|
||||
throw new IllegalStateException("Parent comment must be on same side");
|
||||
}
|
||||
}
|
||||
|
||||
final PatchLineComment nc =
|
||||
new PatchLineComment(new PatchLineComment.Key(patchKey, ChangeUtil
|
||||
.messageUUID(db)), comment.getLine(), me, comment.getParentUuid());
|
||||
nc.setSide(comment.getSide());
|
||||
nc.setMessage(comment.getMessage());
|
||||
db.patchComments().insert(Collections.singleton(nc));
|
||||
return nc;
|
||||
|
||||
} else {
|
||||
if (!me.equals(comment.getAuthor())) {
|
||||
db.changes().beginTransaction(changeId);
|
||||
try {
|
||||
changeControlFactory.validateFor(changeId);
|
||||
if (db.patchSets().get(patchSetId) == null) {
|
||||
throw new NoSuchChangeException(changeId);
|
||||
}
|
||||
comment.updated();
|
||||
db.patchComments().update(Collections.singleton(comment));
|
||||
return comment;
|
||||
|
||||
final Account.Id me = currentUser.getAccountId();
|
||||
if (comment.getKey().get() == null) {
|
||||
if (comment.getLine() < 1) {
|
||||
throw new IllegalStateException("Comment line must be >= 1, not "
|
||||
+ comment.getLine());
|
||||
}
|
||||
|
||||
if (comment.getParentUuid() != null) {
|
||||
final PatchLineComment parent =
|
||||
db.patchComments().get(
|
||||
new PatchLineComment.Key(patchKey, comment.getParentUuid()));
|
||||
if (parent == null || parent.getSide() != comment.getSide()) {
|
||||
throw new IllegalStateException("Parent comment must be on same side");
|
||||
}
|
||||
}
|
||||
|
||||
final PatchLineComment nc =
|
||||
new PatchLineComment(new PatchLineComment.Key(patchKey, ChangeUtil
|
||||
.messageUUID(db)), comment.getLine(), me, comment.getParentUuid());
|
||||
nc.setSide(comment.getSide());
|
||||
nc.setMessage(comment.getMessage());
|
||||
db.patchComments().insert(Collections.singleton(nc));
|
||||
db.commit();
|
||||
return nc;
|
||||
|
||||
} else {
|
||||
if (!me.equals(comment.getAuthor())) {
|
||||
throw new NoSuchChangeException(changeId);
|
||||
}
|
||||
comment.updated();
|
||||
db.patchComments().update(Collections.singleton(comment));
|
||||
db.commit();
|
||||
return comment;
|
||||
}
|
||||
} finally {
|
||||
db.rollback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -8,14 +8,17 @@
|
||||
var token;
|
||||
if (p >= 0) {
|
||||
token = href.substring(p + 1);
|
||||
if (token.length != 0 && token.charAt(0) == '/') {
|
||||
token = token.substring(1);
|
||||
}
|
||||
href = href.substring(0, p);
|
||||
} else {
|
||||
token = 'mine';
|
||||
token = '';
|
||||
}
|
||||
window.location.replace(href + 'login/' + token);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<p>Redirecting to <a href="login/mine">Gerrit Code Review</a>.</p>
|
||||
<p>Redirecting to <a href="login/">Gerrit Code Review</a>.</p>
|
||||
</body>
|
||||
</html>
|
||||
|
@@ -16,8 +16,11 @@ package com.google.gerrit.pgm;
|
||||
|
||||
import static com.google.gerrit.server.schema.DataSourceProvider.Context.MULTI_USER;
|
||||
|
||||
import com.google.gerrit.httpd.CacheBasedWebSession;
|
||||
import com.google.gerrit.httpd.GitOverHttpModule;
|
||||
import com.google.gerrit.httpd.HttpCanonicalWebUrlProvider;
|
||||
import com.google.gerrit.httpd.WebModule;
|
||||
import com.google.gerrit.httpd.WebSshGlueModule;
|
||||
import com.google.gerrit.lifecycle.LifecycleManager;
|
||||
import com.google.gerrit.pgm.http.jetty.JettyEnv;
|
||||
import com.google.gerrit.pgm.http.jetty.JettyModule;
|
||||
@@ -31,7 +34,11 @@ import com.google.gerrit.server.config.CanonicalWebUrlModule;
|
||||
import com.google.gerrit.server.config.CanonicalWebUrlProvider;
|
||||
import com.google.gerrit.server.config.GerritGlobalModule;
|
||||
import com.google.gerrit.server.config.MasterNodeStartup;
|
||||
import com.google.gerrit.server.git.PushReplication;
|
||||
import com.google.gerrit.server.git.WorkQueue;
|
||||
import com.google.gerrit.server.mail.SmtpEmailSender;
|
||||
import com.google.gerrit.server.schema.SchemaVersionCheck;
|
||||
import com.google.gerrit.server.ssh.NoSshModule;
|
||||
import com.google.gerrit.sshd.SshModule;
|
||||
import com.google.gerrit.sshd.commands.MasterCommandModule;
|
||||
import com.google.gerrit.sshd.commands.SlaveCommandModule;
|
||||
@@ -114,10 +121,6 @@ public class Daemon extends SiteProgram {
|
||||
if (slave && httpd) {
|
||||
throw die("Cannot combine --slave and --enable-httpd");
|
||||
}
|
||||
if (httpd && !sshd) {
|
||||
// TODO Support HTTP without SSH.
|
||||
throw die("--enable-httpd currently requires --enable-sshd");
|
||||
}
|
||||
|
||||
if (consoleLog) {
|
||||
} else {
|
||||
@@ -188,7 +191,10 @@ public class Daemon extends SiteProgram {
|
||||
final List<Module> modules = new ArrayList<Module>();
|
||||
modules.add(SchemaVersionCheck.module());
|
||||
modules.add(new LogFileCompressor.Module());
|
||||
modules.add(new WorkQueue.Module());
|
||||
modules.add(cfgInjector.getInstance(GerritGlobalModule.class));
|
||||
modules.add(new SmtpEmailSender.Module());
|
||||
modules.add(new PushReplication.Module());
|
||||
if (httpd) {
|
||||
modules.add(new CanonicalWebUrlModule() {
|
||||
@Override
|
||||
@@ -217,11 +223,15 @@ public class Daemon extends SiteProgram {
|
||||
|
||||
private Injector createSshInjector() {
|
||||
final List<Module> modules = new ArrayList<Module>();
|
||||
modules.add(new SshModule());
|
||||
if (slave) {
|
||||
modules.add(new SlaveCommandModule());
|
||||
if (sshd) {
|
||||
modules.add(new SshModule());
|
||||
if (slave) {
|
||||
modules.add(new SlaveCommandModule());
|
||||
} else {
|
||||
modules.add(new MasterCommandModule());
|
||||
}
|
||||
} else {
|
||||
modules.add(new MasterCommandModule());
|
||||
modules.add(new NoSshModule());
|
||||
}
|
||||
return sysInjector.createChildInjector(modules);
|
||||
}
|
||||
@@ -240,7 +250,12 @@ public class Daemon extends SiteProgram {
|
||||
private Injector createWebInjector() {
|
||||
final List<Module> modules = new ArrayList<Module>();
|
||||
modules.add(sshInjector.getInstance(WebModule.class));
|
||||
modules.add(sshInjector.getInstance(ProjectQoSFilter.Module.class));
|
||||
modules.add(sysInjector.getInstance(GitOverHttpModule.class));
|
||||
modules.add(sshInjector.getInstance(WebSshGlueModule.class));
|
||||
modules.add(CacheBasedWebSession.module());
|
||||
if (sshd) {
|
||||
modules.add(sshInjector.getInstance(ProjectQoSFilter.Module.class));
|
||||
}
|
||||
return sysInjector.createChildInjector(modules);
|
||||
}
|
||||
|
||||
|
75
gerrit-pgm/src/main/java/com/google/gerrit/pgm/ProtoGen.java
Normal file
75
gerrit-pgm/src/main/java/com/google/gerrit/pgm/ProtoGen.java
Normal file
@@ -0,0 +1,75 @@
|
||||
// Copyright (C) 2010 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.pgm;
|
||||
|
||||
import com.google.gerrit.pgm.util.AbstractProgram;
|
||||
import com.google.gerrit.reviewdb.ReviewDb;
|
||||
import com.google.gwtorm.schema.java.JavaSchemaModel;
|
||||
|
||||
import org.eclipse.jgit.storage.file.LockFile;
|
||||
import org.eclipse.jgit.util.FS;
|
||||
import org.eclipse.jgit.util.IO;
|
||||
import org.kohsuke.args4j.Option;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class ProtoGen extends AbstractProgram {
|
||||
@Option(name = "--output", aliases = {"-o"}, required = true, metaVar = "FILE", usage = "File to write .proto into")
|
||||
private File file;
|
||||
|
||||
@Override
|
||||
public int run() throws Exception {
|
||||
LockFile lock = new LockFile(file.getAbsoluteFile(), FS.DETECTED);
|
||||
if (!lock.lock()) {
|
||||
throw die("Cannot lock " + file);
|
||||
}
|
||||
try {
|
||||
JavaSchemaModel jsm = new JavaSchemaModel(ReviewDb.class);
|
||||
PrintWriter out = new PrintWriter(new BufferedWriter(
|
||||
new OutputStreamWriter(lock.getOutputStream(), "UTF-8")));
|
||||
try {
|
||||
String header;
|
||||
InputStream in = getClass().getResourceAsStream("ProtoGenHeader.txt");
|
||||
try {
|
||||
ByteBuffer buf = IO.readWholeStream(in, 1024);
|
||||
int ptr = buf.arrayOffset() + buf.position();
|
||||
int len = buf.remaining();
|
||||
header = new String(buf.array(), ptr, len, "UTF-8");
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
|
||||
String version = com.google.gerrit.common.Version.getVersion();
|
||||
out.write(header.replace("@@VERSION@@", version));
|
||||
jsm.generateProto(out);
|
||||
out.flush();
|
||||
} finally {
|
||||
out.close();
|
||||
}
|
||||
if (!lock.commit()) {
|
||||
throw die("Could not write to " + file);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
System.out.println("Created " + file.getPath());
|
||||
return 0;
|
||||
}
|
||||
}
|
@@ -29,6 +29,7 @@ import com.google.inject.Injector;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.inject.servlet.GuiceFilter;
|
||||
import com.google.inject.servlet.GuiceHelper;
|
||||
import com.google.inject.servlet.GuiceServletContextListener;
|
||||
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
@@ -67,6 +68,10 @@ import java.util.Set;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
@Singleton
|
||||
public class JettyServer {
|
||||
static class Lifecycle implements LifecycleListener {
|
||||
@@ -117,7 +122,23 @@ public class JettyServer {
|
||||
|
||||
Handler app = makeContext(env, cfg);
|
||||
if (cfg.getBoolean("httpd", "requestlog", !reverseProxy)) {
|
||||
RequestLogHandler handler = new RequestLogHandler();
|
||||
RequestLogHandler handler = new RequestLogHandler() {
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest,
|
||||
final HttpServletRequest req, final HttpServletResponse rsp)
|
||||
throws IOException, ServletException {
|
||||
// Force the user to construct, so it's available to our HttpLog
|
||||
// later on when the request gets logged out to the access file.
|
||||
//
|
||||
GuiceHelper.runInContext(req, rsp, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
userProvider.get();
|
||||
}
|
||||
});
|
||||
super.handle(target, baseRequest, req, rsp);
|
||||
}
|
||||
};
|
||||
handler.setRequestLog(new HttpLog(site, userProvider));
|
||||
handler.setHandler(app);
|
||||
app = handler;
|
||||
|
@@ -54,13 +54,20 @@ class InitSshd implements InitStep {
|
||||
String hostname = "*";
|
||||
int port = 29418;
|
||||
String listenAddress = sshd.get("listenAddress");
|
||||
if (listenAddress != null && !listenAddress.isEmpty()) {
|
||||
if (isOff(listenAddress)) {
|
||||
hostname = "off";
|
||||
} else if (listenAddress != null && !listenAddress.isEmpty()) {
|
||||
final InetSocketAddress addr = SocketUtil.parse(listenAddress, port);
|
||||
hostname = SocketUtil.hostname(addr);
|
||||
port = addr.getPort();
|
||||
}
|
||||
|
||||
hostname = ui.readString(hostname, "Listen on address");
|
||||
if (isOff(hostname)) {
|
||||
sshd.set("listenAddress", "off");
|
||||
return;
|
||||
}
|
||||
|
||||
port = ui.readInt(port, "Listen on port");
|
||||
sshd.set("listenAddress", SocketUtil.format(hostname, port));
|
||||
|
||||
@@ -73,6 +80,12 @@ class InitSshd implements InitStep {
|
||||
generateSshHostKeys();
|
||||
}
|
||||
|
||||
private static boolean isOff(String listenHostname) {
|
||||
return "off".equalsIgnoreCase(listenHostname)
|
||||
|| "none".equalsIgnoreCase(listenHostname)
|
||||
|| "no".equalsIgnoreCase(listenHostname);
|
||||
}
|
||||
|
||||
private void generateSshHostKeys() throws InterruptedException, IOException {
|
||||
if (!site.ssh_key.exists() //
|
||||
&& !site.ssh_rsa.exists() //
|
||||
|
@@ -20,6 +20,7 @@ import static com.google.inject.Stage.PRODUCTION;
|
||||
import com.google.gerrit.lifecycle.LifecycleModule;
|
||||
import com.google.gerrit.server.config.GerritServerConfigModule;
|
||||
import com.google.gerrit.server.config.SitePath;
|
||||
import com.google.gerrit.server.git.LocalDiskRepositoryManager;
|
||||
import com.google.gerrit.server.schema.DataSourceProvider;
|
||||
import com.google.gerrit.server.schema.DatabaseModule;
|
||||
import com.google.gerrit.server.schema.SchemaModule;
|
||||
@@ -164,6 +165,7 @@ public abstract class SiteProgram extends AbstractProgram {
|
||||
modules.add(new GerritServerConfigModule());
|
||||
modules.add(new DatabaseModule());
|
||||
modules.add(new SchemaModule());
|
||||
modules.add(new LocalDiskRepositoryManager.Module());
|
||||
|
||||
try {
|
||||
return Guice.createInjector(PRODUCTION, modules);
|
||||
|
@@ -0,0 +1,22 @@
|
||||
// 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.
|
||||
//
|
||||
// Gerrit Code Review (version @@VERSION@@)
|
||||
|
||||
syntax = "proto2";
|
||||
|
||||
option java_api_version = 2;
|
||||
option java_package = "com.google.gerrit.proto.reviewdb";
|
||||
|
||||
package devtools.gerritcodereview;
|
@@ -45,6 +45,10 @@ public final class AccountAgreement implements AbstractAgreement {
|
||||
return accountId;
|
||||
}
|
||||
|
||||
public ContributorAgreement.Id getContributorAgreementId() {
|
||||
return claId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.google.gwtorm.client.Key<?>[] members() {
|
||||
return new com.google.gwtorm.client.Key<?>[] {claId};
|
||||
|
@@ -47,6 +47,10 @@ public final class AccountGroupAgreement implements AbstractAgreement {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
public ContributorAgreement.Id getContributorAgreementId() {
|
||||
return claId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.google.gwtorm.client.Key<?>[] members() {
|
||||
return new com.google.gwtorm.client.Key<?>[] {claId};
|
||||
|
@@ -49,6 +49,14 @@ public final class AccountGroupIncludeAudit {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
public AccountGroup.Id getIncludedId() {
|
||||
return includeId;
|
||||
}
|
||||
|
||||
public Timestamp getAddedOn() {
|
||||
return addedOn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.google.gwtorm.client.Key<?>[] members() {
|
||||
return new com.google.gwtorm.client.Key<?>[] {includeId};
|
||||
|
@@ -49,6 +49,14 @@ public final class AccountGroupMemberAudit {
|
||||
return accountId;
|
||||
}
|
||||
|
||||
public AccountGroup.Id getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
public Timestamp getAddedOn() {
|
||||
return addedOn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.google.gwtorm.client.Key<?>[] members() {
|
||||
return new com.google.gwtorm.client.Key<?>[] {groupId};
|
||||
|
@@ -27,7 +27,7 @@ public interface AccountGroupNameAccess extends
|
||||
AccountGroupName get(AccountGroup.NameKey name) throws OrmException;
|
||||
|
||||
@Query("ORDER BY name")
|
||||
ResultSet<AccountGroupName> all();
|
||||
ResultSet<AccountGroupName> all() throws OrmException;
|
||||
|
||||
@Query("WHERE name.name >= ? AND name.name <= ? ORDER BY name LIMIT ?")
|
||||
ResultSet<AccountGroupName> suggestByName(String nameA, String nameB,
|
||||
|
@@ -56,6 +56,14 @@ public final class AccountProjectWatch {
|
||||
return accountId;
|
||||
}
|
||||
|
||||
public Project.NameKey getProjectName() {
|
||||
return projectName;
|
||||
}
|
||||
|
||||
public Filter getFilter() {
|
||||
return filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.google.gwtorm.client.Key<?>[] members() {
|
||||
return new com.google.gwtorm.client.Key<?>[] {projectName, filter};
|
||||
|
@@ -73,6 +73,9 @@ public enum AuthType {
|
||||
*/
|
||||
LDAP_BIND,
|
||||
|
||||
/** Login is managed by additional, unspecified code. */
|
||||
CUSTOM_EXTENSION,
|
||||
|
||||
/** Development mode to enable becoming anyone you want. */
|
||||
DEVELOPMENT_BECOME_ANY_ACCOUNT;
|
||||
}
|
||||
|
@@ -28,37 +28,29 @@ public interface PatchLineCommentAccess extends
|
||||
@Query("WHERE key.patchKey.patchSetId.changeId = ?")
|
||||
ResultSet<PatchLineComment> byChange(Change.Id id) throws OrmException;
|
||||
|
||||
@Query("WHERE key.patchKey = ? AND status = '"
|
||||
+ PatchLineComment.STATUS_PUBLISHED + "' ORDER BY lineNbr,writtenOn")
|
||||
ResultSet<PatchLineComment> published(Patch.Key patch) throws OrmException;
|
||||
|
||||
@Query("WHERE key.patchKey.patchSetId.changeId = ?"
|
||||
+ " AND key.patchKey.fileName = ? AND status = '"
|
||||
+ PatchLineComment.STATUS_PUBLISHED + "' ORDER BY lineNbr,writtenOn")
|
||||
ResultSet<PatchLineComment> published(Change.Id id, String file)
|
||||
ResultSet<PatchLineComment> publishedByChangeFile(Change.Id id, String file)
|
||||
throws OrmException;
|
||||
|
||||
@Query("WHERE key.patchKey.patchSetId = ? AND status = '"
|
||||
+ PatchLineComment.STATUS_PUBLISHED + "'")
|
||||
ResultSet<PatchLineComment> published(PatchSet.Id patchset)
|
||||
ResultSet<PatchLineComment> publishedByPatchSet(PatchSet.Id patchset)
|
||||
throws OrmException;
|
||||
|
||||
@Query("WHERE key.patchKey.patchSetId = ? AND status = '"
|
||||
+ PatchLineComment.STATUS_DRAFT
|
||||
+ "' AND author = ? ORDER BY key.patchKey,lineNbr,writtenOn")
|
||||
ResultSet<PatchLineComment> draft(PatchSet.Id patchset, Account.Id author)
|
||||
throws OrmException;
|
||||
|
||||
@Query("WHERE key.patchKey = ? AND status = '"
|
||||
+ PatchLineComment.STATUS_DRAFT
|
||||
+ "' AND author = ? ORDER BY lineNbr,writtenOn")
|
||||
ResultSet<PatchLineComment> draft(Patch.Key patch, Account.Id author)
|
||||
ResultSet<PatchLineComment> draftByPatchSetAuthor
|
||||
(PatchSet.Id patchset, Account.Id author)
|
||||
throws OrmException;
|
||||
|
||||
@Query("WHERE key.patchKey.patchSetId.changeId = ?"
|
||||
+ " AND key.patchKey.fileName = ? AND author = ? AND status = '"
|
||||
+ PatchLineComment.STATUS_DRAFT + "' ORDER BY lineNbr,writtenOn")
|
||||
ResultSet<PatchLineComment> draft(Change.Id id, String file, Account.Id author)
|
||||
ResultSet<PatchLineComment> draftByChangeFileAuthor
|
||||
(Change.Id id, String file, Account.Id author)
|
||||
throws OrmException;
|
||||
|
||||
@Query("WHERE status = '" + PatchLineComment.STATUS_DRAFT
|
||||
|
@@ -27,10 +27,6 @@ public interface PatchSetAccess extends Access<PatchSet, PatchSet.Id> {
|
||||
@Query("WHERE id.changeId = ? ORDER BY id.patchSetId")
|
||||
ResultSet<PatchSet> byChange(Change.Id id) throws OrmException;
|
||||
|
||||
@Query("WHERE id.changeId = ? AND revision = ?")
|
||||
ResultSet<PatchSet> byChangeRevision(Change.Id id, RevId rev)
|
||||
throws OrmException;
|
||||
|
||||
@Query("WHERE revision = ? LIMIT 2")
|
||||
ResultSet<PatchSet> byRevision(RevId rev) throws OrmException;
|
||||
|
||||
|
@@ -51,6 +51,14 @@ public final class PatchSetApproval {
|
||||
return patchSetId;
|
||||
}
|
||||
|
||||
public Account.Id getAccountId() {
|
||||
return accountId;
|
||||
}
|
||||
|
||||
public ApprovalCategory.Id getCategoryId() {
|
||||
return categoryId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.google.gwtorm.client.Key<?>[] members() {
|
||||
return new com.google.gwtorm.client.Key<?>[] {accountId, categoryId};
|
||||
|
@@ -32,85 +32,85 @@ import com.google.gwtorm.client.Sequence;
|
||||
public interface ReviewDb extends Schema {
|
||||
/* If you change anything, update SchemaVersion.C to use a new version. */
|
||||
|
||||
@Relation
|
||||
@Relation(id = 1)
|
||||
SchemaVersionAccess schemaVersion();
|
||||
|
||||
@Relation
|
||||
@Relation(id = 2)
|
||||
SystemConfigAccess systemConfig();
|
||||
|
||||
@Relation
|
||||
@Relation(id = 3)
|
||||
ApprovalCategoryAccess approvalCategories();
|
||||
|
||||
@Relation
|
||||
@Relation(id = 4)
|
||||
ApprovalCategoryValueAccess approvalCategoryValues();
|
||||
|
||||
@Relation
|
||||
@Relation(id = 5)
|
||||
ContributorAgreementAccess contributorAgreements();
|
||||
|
||||
@Relation
|
||||
@Relation(id = 6)
|
||||
AccountAccess accounts();
|
||||
|
||||
@Relation
|
||||
@Relation(id = 7)
|
||||
AccountExternalIdAccess accountExternalIds();
|
||||
|
||||
@Relation
|
||||
@Relation(id = 8)
|
||||
AccountSshKeyAccess accountSshKeys();
|
||||
|
||||
@Relation
|
||||
@Relation(id = 9)
|
||||
AccountAgreementAccess accountAgreements();
|
||||
|
||||
@Relation
|
||||
@Relation(id = 10)
|
||||
AccountGroupAccess accountGroups();
|
||||
|
||||
@Relation
|
||||
@Relation(id = 11)
|
||||
AccountGroupNameAccess accountGroupNames();
|
||||
|
||||
@Relation
|
||||
@Relation(id = 12)
|
||||
AccountGroupMemberAccess accountGroupMembers();
|
||||
|
||||
@Relation
|
||||
@Relation(id = 13)
|
||||
AccountGroupMemberAuditAccess accountGroupMembersAudit();
|
||||
|
||||
@Relation
|
||||
@Relation(id = 14)
|
||||
AccountGroupIncludeAccess accountGroupIncludes();
|
||||
|
||||
@Relation
|
||||
@Relation(id = 15)
|
||||
AccountGroupIncludeAuditAccess accountGroupIncludesAudit();
|
||||
|
||||
@Relation
|
||||
@Relation(id = 16)
|
||||
AccountGroupAgreementAccess accountGroupAgreements();
|
||||
|
||||
@Relation
|
||||
@Relation(id = 17)
|
||||
AccountDiffPreferenceAccess accountDiffPreferences();
|
||||
|
||||
@Relation
|
||||
@Relation(id = 18)
|
||||
StarredChangeAccess starredChanges();
|
||||
|
||||
@Relation
|
||||
@Relation(id = 19)
|
||||
AccountProjectWatchAccess accountProjectWatches();
|
||||
|
||||
@Relation
|
||||
@Relation(id = 20)
|
||||
AccountPatchReviewAccess accountPatchReviews();
|
||||
|
||||
@Relation
|
||||
@Relation(id = 21)
|
||||
ChangeAccess changes();
|
||||
|
||||
@Relation
|
||||
@Relation(id = 22)
|
||||
PatchSetApprovalAccess patchSetApprovals();
|
||||
|
||||
@Relation
|
||||
@Relation(id = 23)
|
||||
ChangeMessageAccess changeMessages();
|
||||
|
||||
@Relation
|
||||
@Relation(id = 24)
|
||||
PatchSetAccess patchSets();
|
||||
|
||||
@Relation
|
||||
@Relation(id = 25)
|
||||
PatchSetAncestorAccess patchSetAncestors();
|
||||
|
||||
@Relation
|
||||
@Relation(id = 26)
|
||||
PatchLineCommentAccess patchComments();
|
||||
|
||||
@Relation
|
||||
@Relation(id = 27)
|
||||
TrackingIdAccess trackingIds();
|
||||
|
||||
/** Create the next unique id for an {@link Account}. */
|
||||
|
@@ -43,6 +43,10 @@ public class StarredChange {
|
||||
return accountId;
|
||||
}
|
||||
|
||||
public Change.Id getChangeId() {
|
||||
return changeId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.google.gwtorm.client.Key<?>[] members() {
|
||||
return new com.google.gwtorm.client.Key<?>[] {changeId};
|
||||
@@ -59,6 +63,10 @@ public class StarredChange {
|
||||
key = k;
|
||||
}
|
||||
|
||||
public StarredChange.Key getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public Account.Id getAccountId() {
|
||||
return key.accountId;
|
||||
}
|
||||
|
@@ -102,6 +102,14 @@ public final class TrackingId {
|
||||
return changeId;
|
||||
}
|
||||
|
||||
public TrackingId.Id getTrackingId() {
|
||||
return trackingId;
|
||||
}
|
||||
|
||||
public TrackingId.System getTrackingSystem() {
|
||||
return trackingSystem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.google.gwtorm.client.Key<?>[] members() {
|
||||
return new com.google.gwtorm.client.Key<?>[] {trackingId, trackingSystem};
|
||||
@@ -123,6 +131,10 @@ public final class TrackingId {
|
||||
key = new Key(ch, new TrackingId.Id(id), new TrackingId.System(s));
|
||||
}
|
||||
|
||||
public TrackingId.Key getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public Change.Id getChangeId() {
|
||||
return key.changeId;
|
||||
}
|
||||
|
@@ -215,6 +215,18 @@ limitations under the License.
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
@@ -90,6 +90,7 @@ public class RulesCache {
|
||||
}
|
||||
}
|
||||
|
||||
private final boolean enableProjectRules;
|
||||
private final File cacheDir;
|
||||
private final File rulesDir;
|
||||
private final GitRepositoryManager gitMgr;
|
||||
@@ -99,6 +100,7 @@ public class RulesCache {
|
||||
@Inject
|
||||
protected RulesCache(@GerritServerConfig Config config, SitePaths site,
|
||||
GitRepositoryManager gm) {
|
||||
enableProjectRules = config.getBoolean("rules", null, "enable", true);
|
||||
cacheDir = site.resolve(config.getString("cache", null, "directory"));
|
||||
rulesDir = cacheDir != null ? new File(cacheDir, "rules") : null;
|
||||
gitMgr = gm;
|
||||
@@ -117,7 +119,7 @@ public class RulesCache {
|
||||
Project.NameKey project,
|
||||
ObjectId rulesId)
|
||||
throws CompileException {
|
||||
if (project == null || rulesId == null) {
|
||||
if (!enableProjectRules || project == null || rulesId == null) {
|
||||
return defaultMachine;
|
||||
}
|
||||
|
||||
|
@@ -51,7 +51,7 @@ public final class StoredValues {
|
||||
PatchSetInfoFactory patchInfoFactory =
|
||||
env.getInjector().getInstance(PatchSetInfoFactory.class);
|
||||
try {
|
||||
return patchInfoFactory.get(patchSetId);
|
||||
return patchInfoFactory.get(REVIEW_DB.get(engine), patchSetId);
|
||||
} catch (PatchSetInfoNotAvailableException e) {
|
||||
throw new SystemException(e.getMessage());
|
||||
}
|
||||
|
@@ -33,16 +33,21 @@ public class RequestCleanup implements Runnable {
|
||||
LoggerFactory.getLogger(RequestCleanup.class);
|
||||
|
||||
private final List<Runnable> cleanup = new LinkedList<Runnable>();
|
||||
private boolean ran;
|
||||
|
||||
/** Register a task to be completed after the request ends. */
|
||||
public void add(final Runnable task) {
|
||||
synchronized (cleanup) {
|
||||
if (ran) {
|
||||
throw new IllegalStateException("Request has already been cleaned up");
|
||||
}
|
||||
cleanup.add(task);
|
||||
}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
synchronized (cleanup) {
|
||||
ran = true;
|
||||
for (final Iterator<Runnable> i = cleanup.iterator(); i.hasNext();) {
|
||||
try {
|
||||
i.next().run();
|
||||
|
@@ -21,7 +21,7 @@ import com.google.inject.Inject;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
public final class DefaultRealm implements Realm {
|
||||
public class DefaultRealm implements Realm {
|
||||
private final EmailExpander emailExpander;
|
||||
private final AccountByEmailCache byEmail;
|
||||
|
||||
|
@@ -22,7 +22,6 @@ import com.google.gerrit.server.config.ConfigUtil;
|
||||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.gerrit.server.config.SitePaths;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.ProvisionException;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import net.sf.ehcache.CacheManager;
|
||||
@@ -214,8 +213,8 @@ public class CachePool {
|
||||
}
|
||||
}
|
||||
|
||||
private void configureDefaultCache() {
|
||||
final CacheConfiguration c = new CacheConfiguration();
|
||||
private CacheConfiguration newConfiguration() {
|
||||
CacheConfiguration c = new CacheConfiguration();
|
||||
|
||||
c.setMaxElementsInMemory(1024);
|
||||
c.setMemoryStoreEvictionPolicyFromObject(MemoryStoreEvictionPolicy.LFU);
|
||||
@@ -232,19 +231,17 @@ public class CachePool {
|
||||
c.setDiskSpoolBufferSizeMB(5);
|
||||
c.setDiskExpiryThreadIntervalSeconds(60 * 60);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
mgr.setDefaultCacheConfiguration(c);
|
||||
private void configureDefaultCache() {
|
||||
mgr.setDefaultCacheConfiguration(newConfiguration());
|
||||
}
|
||||
|
||||
private CacheConfiguration newCache(final String name) {
|
||||
try {
|
||||
final CacheConfiguration c;
|
||||
c = mgr.getDefaultCacheConfiguration().clone();
|
||||
c.setName(name);
|
||||
return c;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new ProvisionException("Cannot configure cache " + name, e);
|
||||
}
|
||||
CacheConfiguration c = newConfiguration();
|
||||
c.setName(name);
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -140,6 +140,7 @@ public class AuthConfig {
|
||||
case LDAP:
|
||||
case LDAP_BIND:
|
||||
case CLIENT_SSL_CERT_LDAP:
|
||||
case CUSTOM_EXTENSION:
|
||||
// Its safe to assume yes for an HTTP authentication type, as the
|
||||
// only way in is through some external system that the admin trusts
|
||||
//
|
||||
|
@@ -17,7 +17,6 @@ package com.google.gerrit.server.config;
|
||||
import static com.google.inject.Scopes.SINGLETON;
|
||||
|
||||
import com.google.gerrit.common.data.ApprovalTypes;
|
||||
import com.google.gerrit.lifecycle.LifecycleListener;
|
||||
import com.google.gerrit.lifecycle.LifecycleModule;
|
||||
import com.google.gerrit.reviewdb.AuthType;
|
||||
import com.google.gerrit.rules.PrologModule;
|
||||
@@ -43,17 +42,13 @@ import com.google.gerrit.server.git.ChangeMergeQueue;
|
||||
import com.google.gerrit.server.git.GitModule;
|
||||
import com.google.gerrit.server.git.MergeQueue;
|
||||
import com.google.gerrit.server.git.PushAllProjectsOp;
|
||||
import com.google.gerrit.server.git.PushReplication;
|
||||
import com.google.gerrit.server.git.ReloadSubmitQueueOp;
|
||||
import com.google.gerrit.server.git.ReplicationQueue;
|
||||
import com.google.gerrit.server.git.SecureCredentialsProvider;
|
||||
import com.google.gerrit.server.git.TagCache;
|
||||
import com.google.gerrit.server.git.TransferConfig;
|
||||
import com.google.gerrit.server.git.WorkQueue;
|
||||
import com.google.gerrit.server.mail.EmailSender;
|
||||
import com.google.gerrit.server.mail.FromAddressGenerator;
|
||||
import com.google.gerrit.server.mail.FromAddressGeneratorProvider;
|
||||
import com.google.gerrit.server.mail.SmtpEmailSender;
|
||||
import com.google.gerrit.server.mail.VelocityRuntimeProvider;
|
||||
import com.google.gerrit.server.patch.PatchListCacheImpl;
|
||||
import com.google.gerrit.server.patch.PatchSetInfoFactory;
|
||||
import com.google.gerrit.server.project.AccessControlModule;
|
||||
@@ -68,51 +63,14 @@ import com.google.gerrit.server.util.IdGenerator;
|
||||
import com.google.gerrit.server.workflow.FunctionState;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import org.apache.velocity.app.Velocity;
|
||||
import org.apache.velocity.runtime.RuntimeConstants;
|
||||
import org.apache.velocity.runtime.RuntimeInstance;
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
|
||||
/** Starts global state with standard dependencies. */
|
||||
public class GerritGlobalModule extends FactoryModule {
|
||||
private final AuthType loginType;
|
||||
|
||||
public static class VelocityLifecycle implements LifecycleListener {
|
||||
private final SitePaths site;
|
||||
|
||||
@Inject
|
||||
VelocityLifecycle(final SitePaths site) {
|
||||
this.site = site;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
String rl = "resource.loader";
|
||||
String pkg = "org.apache.velocity.runtime.resource.loader";
|
||||
Properties p = new Properties();
|
||||
|
||||
p.setProperty(rl, "file, class");
|
||||
p.setProperty("file." + rl + ".class", pkg + ".FileResourceLoader");
|
||||
p.setProperty("file." + rl + ".path", site.mail_dir.getAbsolutePath());
|
||||
p.setProperty("class." + rl + ".class", pkg + ".ClasspathResourceLoader");
|
||||
p.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS,
|
||||
"org.apache.velocity.runtime.log.SimpleLog4JLogSystem" );
|
||||
p.setProperty("runtime.log.logsystem.log4j.category", "velocity");
|
||||
|
||||
try {
|
||||
Velocity.init(p);
|
||||
} catch(Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
}
|
||||
}
|
||||
|
||||
@Inject
|
||||
GerritGlobalModule(final AuthConfig authConfig,
|
||||
@GerritServerConfig final Config config) {
|
||||
@@ -129,6 +87,9 @@ public class GerritGlobalModule extends FactoryModule {
|
||||
install(new LdapModule());
|
||||
break;
|
||||
|
||||
case CUSTOM_EXTENSION:
|
||||
break;
|
||||
|
||||
default:
|
||||
bind(Realm.class).to(DefaultRealm.class);
|
||||
break;
|
||||
@@ -161,21 +122,21 @@ public class GerritGlobalModule extends FactoryModule {
|
||||
bind(PermissionCollection.Factory.class);
|
||||
|
||||
bind(FileTypeRegistry.class).to(MimeUtilFileTypeRegistry.class);
|
||||
bind(WorkQueue.class);
|
||||
bind(ToolsCatalog.class);
|
||||
bind(EventFactory.class);
|
||||
bind(TransferConfig.class);
|
||||
|
||||
bind(ReplicationQueue.class).to(PushReplication.class).in(SINGLETON);
|
||||
factory(SecureCredentialsProvider.Factory.class);
|
||||
factory(PushAllProjectsOp.Factory.class);
|
||||
|
||||
bind(MergeQueue.class).to(ChangeMergeQueue.class).in(SINGLETON);
|
||||
factory(ReloadSubmitQueueOp.Factory.class);
|
||||
|
||||
bind(RuntimeInstance.class)
|
||||
.toProvider(VelocityRuntimeProvider.class)
|
||||
.in(SINGLETON);
|
||||
bind(FromAddressGenerator.class).toProvider(
|
||||
FromAddressGeneratorProvider.class).in(SINGLETON);
|
||||
bind(EmailSender.class).to(SmtpEmailSender.class).in(SINGLETON);
|
||||
|
||||
bind(PatchSetInfoFactory.class);
|
||||
bind(IdentifiedUser.GenericFactory.class).in(SINGLETON);
|
||||
@@ -188,8 +149,6 @@ public class GerritGlobalModule extends FactoryModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
listener().to(CachePool.Lifecycle.class);
|
||||
listener().to(WorkQueue.Lifecycle.class);
|
||||
listener().to(VelocityLifecycle.class);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@@ -17,7 +17,7 @@ package com.google.gerrit.server.config;
|
||||
import com.google.gerrit.reviewdb.ReviewDb;
|
||||
import com.google.gerrit.server.RequestCleanup;
|
||||
import com.google.gwtorm.client.OrmException;
|
||||
import com.google.gwtorm.jdbc.Database;
|
||||
import com.google.gwtorm.client.SchemaFactory;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.ProvisionException;
|
||||
@@ -26,11 +26,11 @@ import com.google.inject.Singleton;
|
||||
/** Provides {@link ReviewDb} database handle live only for this request. */
|
||||
@Singleton
|
||||
final class RequestScopedReviewDbProvider implements Provider<ReviewDb> {
|
||||
private final Database<ReviewDb> schema;
|
||||
private final SchemaFactory<ReviewDb> schema;
|
||||
private final Provider<RequestCleanup> cleanup;
|
||||
|
||||
@Inject
|
||||
RequestScopedReviewDbProvider(final Database<ReviewDb> schema,
|
||||
RequestScopedReviewDbProvider(final SchemaFactory<ReviewDb> schema,
|
||||
final Provider<RequestCleanup> cleanup) {
|
||||
this.schema = schema;
|
||||
this.cleanup = cleanup;
|
||||
|
@@ -15,9 +15,11 @@
|
||||
package com.google.gerrit.server.git;
|
||||
|
||||
import com.google.gerrit.lifecycle.LifecycleListener;
|
||||
import com.google.gerrit.lifecycle.LifecycleModule;
|
||||
import com.google.gerrit.reviewdb.Project;
|
||||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.gerrit.server.config.SitePaths;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
@@ -54,6 +56,20 @@ public class LocalDiskRepositoryManager implements GitRepositoryManager {
|
||||
private static final String UNNAMED =
|
||||
"Unnamed repository; edit this file to name it for gitweb.";
|
||||
|
||||
public static class Module extends AbstractModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(GitRepositoryManager.class).to(LocalDiskRepositoryManager.class);
|
||||
|
||||
install(new LifecycleModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
listener().to(LocalDiskRepositoryManager.Lifecycle.class);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static class Lifecycle implements LifecycleListener {
|
||||
private final Config cfg;
|
||||
|
||||
|
@@ -1309,7 +1309,7 @@ public class MergeOp {
|
||||
// Go back to the patch set that was actually merged.
|
||||
//
|
||||
try {
|
||||
c.setCurrentPatchSet(patchSetInfoFactory.get(merged));
|
||||
c.setCurrentPatchSet(patchSetInfoFactory.get(schema, merged));
|
||||
} catch (PatchSetInfoNotAvailableException e1) {
|
||||
log.error("Cannot read merged patch set " + merged, e1);
|
||||
}
|
||||
|
@@ -72,6 +72,13 @@ import java.util.concurrent.TimeUnit;
|
||||
public class PushReplication implements ReplicationQueue {
|
||||
static final Logger log = LoggerFactory.getLogger(PushReplication.class);
|
||||
|
||||
public static class Module extends AbstractModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(ReplicationQueue.class).to(PushReplication.class);
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
// Install our own factory which always runs in batch mode, as we
|
||||
// have no UI available for interactive prompting.
|
||||
|
@@ -15,6 +15,7 @@
|
||||
package com.google.gerrit.server.git;
|
||||
|
||||
import com.google.gerrit.lifecycle.LifecycleListener;
|
||||
import com.google.gerrit.lifecycle.LifecycleModule;
|
||||
import com.google.gerrit.reviewdb.Project.NameKey;
|
||||
import com.google.gerrit.server.util.IdGenerator;
|
||||
import com.google.inject.Inject;
|
||||
@@ -61,6 +62,14 @@ public class WorkQueue {
|
||||
}
|
||||
}
|
||||
|
||||
public static class Module extends LifecycleModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(WorkQueue.class);
|
||||
listener().to(Lifecycle.class);
|
||||
}
|
||||
}
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(WorkQueue.class);
|
||||
private static final UncaughtExceptionHandler LOG_UNCAUGHT_EXCEPTION =
|
||||
new UncaughtExceptionHandler() {
|
||||
|
@@ -16,8 +16,8 @@ package com.google.gerrit.server.mail;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
class Address {
|
||||
static Address parse(final String in) {
|
||||
public class Address {
|
||||
public static Address parse(final String in) {
|
||||
final int lt = in.indexOf('<');
|
||||
final int gt = in.indexOf('>');
|
||||
final int at = in.indexOf("@");
|
||||
@@ -37,15 +37,23 @@ class Address {
|
||||
final String name;
|
||||
final String email;
|
||||
|
||||
Address(String email) {
|
||||
public Address(String email) {
|
||||
this(null, email);
|
||||
}
|
||||
|
||||
Address(String name, String email) {
|
||||
public Address(String name, String email) {
|
||||
this.name = name;
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
@@ -55,7 +63,7 @@ class Address {
|
||||
}
|
||||
}
|
||||
|
||||
String toHeaderString() throws UnsupportedEncodingException {
|
||||
public String toHeaderString() throws UnsupportedEncodingException {
|
||||
if (name != null) {
|
||||
return quotedPhrase(name) + " <" + email + ">";
|
||||
} else if (isSimple()) {
|
||||
|
@@ -72,7 +72,8 @@ public abstract class ChangeEmail extends OutgoingEmail {
|
||||
final IdentifiedUser user = args.identifiedUserFactory.create(id);
|
||||
final Set<AccountGroup.UUID> gids = user.getEffectiveGroups();
|
||||
for (final AccountGroup.UUID gid : gids) {
|
||||
if (args.groupCache.get(gid).isEmailOnlyAuthors()) {
|
||||
AccountGroup group = args.groupCache.get(gid);
|
||||
if (group != null && group.isEmailOnlyAuthors()) {
|
||||
emailOnlyAuthors = true;
|
||||
break;
|
||||
}
|
||||
@@ -136,7 +137,7 @@ public abstract class ChangeEmail extends OutgoingEmail {
|
||||
|
||||
if (patchSet != null && patchSetInfo == null) {
|
||||
try {
|
||||
patchSetInfo = args.patchSetInfoFactory.get(patchSet.getId());
|
||||
patchSetInfo = args.patchSetInfoFactory.get(args.db.get(), patchSet.getId());
|
||||
} catch (PatchSetInfoNotAvailableException err) {
|
||||
patchSetInfo = null;
|
||||
}
|
||||
|
@@ -21,7 +21,6 @@ import com.google.gerrit.server.account.AccountCache;
|
||||
import com.google.gerrit.server.account.GroupCache;
|
||||
import com.google.gerrit.server.config.AllProjectsName;
|
||||
import com.google.gerrit.server.config.CanonicalWebUrl;
|
||||
import com.google.gerrit.server.config.SitePaths;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.patch.PatchListCache;
|
||||
import com.google.gerrit.server.patch.PatchSetInfoFactory;
|
||||
@@ -31,6 +30,8 @@ import com.google.gerrit.server.query.change.ChangeQueryRewriter;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
|
||||
import org.apache.velocity.runtime.RuntimeInstance;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
class EmailArguments {
|
||||
@@ -49,7 +50,7 @@ class EmailArguments {
|
||||
final ChangeQueryBuilder.Factory queryBuilder;
|
||||
final Provider<ChangeQueryRewriter> queryRewriter;
|
||||
final Provider<ReviewDb> db;
|
||||
final SitePaths site;
|
||||
final RuntimeInstance velocityRuntime;
|
||||
|
||||
@Inject
|
||||
EmailArguments(GitRepositoryManager server, ProjectCache projectCache,
|
||||
@@ -61,7 +62,7 @@ class EmailArguments {
|
||||
AllProjectsName allProjectsName,
|
||||
ChangeQueryBuilder.Factory queryBuilder,
|
||||
Provider<ChangeQueryRewriter> queryRewriter, Provider<ReviewDb> db,
|
||||
SitePaths site) {
|
||||
RuntimeInstance velocityRuntime) {
|
||||
this.server = server;
|
||||
this.projectCache = projectCache;
|
||||
this.groupCache = groupCache;
|
||||
@@ -76,6 +77,6 @@ class EmailArguments {
|
||||
this.queryBuilder = queryBuilder;
|
||||
this.queryRewriter = queryRewriter;
|
||||
this.db = db;
|
||||
this.site = site;
|
||||
this.velocityRuntime = velocityRuntime;
|
||||
}
|
||||
}
|
||||
|
@@ -20,28 +20,33 @@ import java.io.Writer;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
abstract class EmailHeader {
|
||||
abstract boolean isEmpty();
|
||||
public abstract class EmailHeader {
|
||||
public abstract boolean isEmpty();
|
||||
|
||||
abstract void write(Writer w) throws IOException;
|
||||
public abstract void write(Writer w) throws IOException;
|
||||
|
||||
static class String extends EmailHeader {
|
||||
public static class String extends EmailHeader {
|
||||
private java.lang.String value;
|
||||
|
||||
String(java.lang.String v) {
|
||||
public String(java.lang.String v) {
|
||||
value = v;
|
||||
}
|
||||
|
||||
public java.lang.String getString() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isEmpty() {
|
||||
public boolean isEmpty() {
|
||||
return value == null || value.length() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
void write(Writer w) throws IOException {
|
||||
public void write(Writer w) throws IOException {
|
||||
if (needsQuotedPrintable(value)) {
|
||||
w.write(quotedPrintable(value));
|
||||
} else {
|
||||
@@ -84,20 +89,24 @@ abstract class EmailHeader {
|
||||
return r.toString();
|
||||
}
|
||||
|
||||
static class Date extends EmailHeader {
|
||||
public static class Date extends EmailHeader {
|
||||
private java.util.Date value;
|
||||
|
||||
Date(java.util.Date v) {
|
||||
public Date(java.util.Date v) {
|
||||
value = v;
|
||||
}
|
||||
|
||||
public java.util.Date getDate() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isEmpty() {
|
||||
public boolean isEmpty() {
|
||||
return value == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
void write(Writer w) throws IOException {
|
||||
public void write(Writer w) throws IOException {
|
||||
final SimpleDateFormat fmt;
|
||||
// Mon, 1 Jun 2009 10:49:44 -0700
|
||||
fmt = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z", Locale.ENGLISH);
|
||||
@@ -105,17 +114,21 @@ abstract class EmailHeader {
|
||||
}
|
||||
}
|
||||
|
||||
static class AddressList extends EmailHeader {
|
||||
public static class AddressList extends EmailHeader {
|
||||
private final List<Address> list = new ArrayList<Address>();
|
||||
|
||||
AddressList() {
|
||||
public AddressList() {
|
||||
}
|
||||
|
||||
AddressList(Address addr) {
|
||||
public AddressList(Address addr) {
|
||||
add(addr);
|
||||
}
|
||||
|
||||
void add(Address addr) {
|
||||
public List<Address> getAddressList() {
|
||||
return Collections.unmodifiableList(list);
|
||||
}
|
||||
|
||||
public void add(Address addr) {
|
||||
list.add(addr);
|
||||
}
|
||||
|
||||
@@ -128,12 +141,12 @@ abstract class EmailHeader {
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isEmpty() {
|
||||
public boolean isEmpty() {
|
||||
return list.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
void write(Writer w) throws IOException {
|
||||
public void write(Writer w) throws IOException {
|
||||
int len = 8;
|
||||
boolean firstAddress = true;
|
||||
boolean needComma = false;
|
||||
|
@@ -20,12 +20,16 @@ import com.google.gerrit.server.account.AccountState;
|
||||
import com.google.gerrit.server.mail.EmailHeader.AddressList;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.velocity.Template;
|
||||
import org.apache.velocity.VelocityContext;
|
||||
import org.apache.velocity.app.Velocity;
|
||||
import org.apache.velocity.context.InternalContextAdapterImpl;
|
||||
import org.apache.velocity.runtime.RuntimeInstance;
|
||||
import org.apache.velocity.runtime.parser.node.SimpleNode;
|
||||
import org.eclipse.jgit.util.SystemReader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
@@ -349,24 +353,41 @@ public abstract class OutgoingEmail {
|
||||
|
||||
protected String velocify(String template) throws EmailException {
|
||||
try {
|
||||
StringWriter w = new StringWriter();
|
||||
Velocity.evaluate(velocityContext, w, "OutgoingEmail", template);
|
||||
return w.toString();
|
||||
} catch(Exception e) {
|
||||
throw new EmailException("Velocity template " + template, e);
|
||||
RuntimeInstance runtime = args.velocityRuntime;
|
||||
String templateName = "OutgoingEmail";
|
||||
SimpleNode tree = runtime.parse(new StringReader(template), templateName);
|
||||
InternalContextAdapterImpl ica = new InternalContextAdapterImpl(velocityContext);
|
||||
ica.pushCurrentTemplateName(templateName);
|
||||
try {
|
||||
tree.init(ica, runtime);
|
||||
StringWriter w = new StringWriter();
|
||||
tree.render(ica, w);
|
||||
return w.toString();
|
||||
} finally {
|
||||
ica.popCurrentTemplateName();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new EmailException("Cannot format velocity template: " + template, e);
|
||||
}
|
||||
}
|
||||
|
||||
protected String velocifyFile(String name) throws EmailException {
|
||||
if (!Velocity.resourceExists(name)) {
|
||||
name = "com/google/gerrit/server/mail/" + name;
|
||||
}
|
||||
try {
|
||||
RuntimeInstance runtime = args.velocityRuntime;
|
||||
Template template;
|
||||
try {
|
||||
template = runtime.getTemplate(name, "UTF-8");
|
||||
} catch (org.apache.velocity.exception.ResourceNotFoundException notFound) {
|
||||
name = "com/google/gerrit/server/mail/" + name;
|
||||
template = runtime.getTemplate(name, "UTF-8");
|
||||
}
|
||||
StringWriter w = new StringWriter();
|
||||
Velocity.mergeTemplate(name, "UTF-8", velocityContext, w);
|
||||
template.merge(velocityContext, w);
|
||||
return w.toString();
|
||||
} catch(Exception e) {
|
||||
throw new EmailException("Velocity template " + name + ".\n", e);
|
||||
} catch (EmailException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new EmailException("Cannot format velocity template " + name, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -17,6 +17,7 @@ package com.google.gerrit.server.mail;
|
||||
import com.google.gerrit.common.Version;
|
||||
import com.google.gerrit.server.config.ConfigUtil;
|
||||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
@@ -40,6 +41,13 @@ import java.util.Set;
|
||||
/** Sends email via a nearby SMTP server. */
|
||||
@Singleton
|
||||
public class SmtpEmailSender implements EmailSender {
|
||||
public static class Module extends AbstractModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(EmailSender.class).to(SmtpEmailSender.class);
|
||||
}
|
||||
}
|
||||
|
||||
public static enum Encryption {
|
||||
NONE, SSL, TLS;
|
||||
}
|
||||
|
@@ -0,0 +1,63 @@
|
||||
// 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.mail;
|
||||
|
||||
import com.google.gerrit.server.config.SitePaths;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.ProvisionException;
|
||||
|
||||
import org.apache.velocity.runtime.RuntimeConstants;
|
||||
import org.apache.velocity.runtime.RuntimeInstance;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/** Configures Velocity template engine for sending email. */
|
||||
public class VelocityRuntimeProvider implements Provider<RuntimeInstance> {
|
||||
private final SitePaths site;
|
||||
|
||||
@Inject
|
||||
VelocityRuntimeProvider(SitePaths site) {
|
||||
this.site = site;
|
||||
}
|
||||
|
||||
public RuntimeInstance get() {
|
||||
String rl = "resource.loader";
|
||||
String pkg = "org.apache.velocity.runtime.resource.loader";
|
||||
|
||||
Properties p = new Properties();
|
||||
p.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS,
|
||||
"org.apache.velocity.runtime.log.SimpleLog4JLogSystem" );
|
||||
p.setProperty("runtime.log.logsystem.log4j.category", "velocity");
|
||||
|
||||
if (site.mail_dir.isDirectory()) {
|
||||
p.setProperty(rl, "file, class");
|
||||
p.setProperty("file." + rl + ".class", pkg + ".FileResourceLoader");
|
||||
p.setProperty("file." + rl + ".path", site.mail_dir.getAbsolutePath());
|
||||
p.setProperty("class." + rl + ".class", pkg + ".ClasspathResourceLoader");
|
||||
} else {
|
||||
p.setProperty(rl, "class");
|
||||
p.setProperty("class." + rl + ".class", pkg + ".ClasspathResourceLoader");
|
||||
}
|
||||
|
||||
RuntimeInstance ri = new RuntimeInstance();
|
||||
try {
|
||||
ri.init(p);
|
||||
} catch (Exception err) {
|
||||
throw new ProvisionException("Cannot configure Velocity templates", err);
|
||||
}
|
||||
return ri;
|
||||
}
|
||||
}
|
@@ -25,7 +25,6 @@ import com.google.gerrit.reviewdb.UserIdentity;
|
||||
import com.google.gerrit.server.account.AccountByEmailCache;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gwtorm.client.OrmException;
|
||||
import com.google.gwtorm.client.SchemaFactory;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
@@ -49,15 +48,12 @@ import java.util.Set;
|
||||
@Singleton
|
||||
public class PatchSetInfoFactory {
|
||||
private final GitRepositoryManager repoManager;
|
||||
private final SchemaFactory<ReviewDb> schemaFactory;
|
||||
private final AccountByEmailCache byEmailCache;
|
||||
|
||||
@Inject
|
||||
public PatchSetInfoFactory(final GitRepositoryManager grm,
|
||||
final SchemaFactory<ReviewDb> schemaFactory,
|
||||
final AccountByEmailCache byEmailCache) {
|
||||
this.repoManager = grm;
|
||||
this.schemaFactory = schemaFactory;
|
||||
this.byEmailCache = byEmailCache;
|
||||
}
|
||||
|
||||
@@ -68,16 +64,13 @@ public class PatchSetInfoFactory {
|
||||
info.setAuthor(toUserIdentity(src.getAuthorIdent()));
|
||||
info.setCommitter(toUserIdentity(src.getCommitterIdent()));
|
||||
info.setRevId(src.getName());
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
public PatchSetInfo get(PatchSet.Id patchSetId)
|
||||
throws PatchSetInfoNotAvailableException {
|
||||
ReviewDb db = null;
|
||||
public PatchSetInfo get(ReviewDb db, PatchSet.Id patchSetId)
|
||||
throws PatchSetInfoNotAvailableException {
|
||||
Repository repo = null;
|
||||
try {
|
||||
db = schemaFactory.open();
|
||||
final PatchSet patchSet = db.patchSets().get(patchSetId);
|
||||
final Change change = db.changes().get(patchSet.getId().getParentKey());
|
||||
final Project.NameKey projectKey = change.getProject();
|
||||
@@ -97,9 +90,6 @@ public class PatchSetInfoFactory {
|
||||
} catch (IOException e) {
|
||||
throw new PatchSetInfoNotAvailableException(e);
|
||||
} finally {
|
||||
if (db != null) {
|
||||
db.close();
|
||||
}
|
||||
if (repo != null) {
|
||||
repo.close();
|
||||
}
|
||||
|
@@ -116,18 +116,25 @@ public class PublishComments implements Callable<VoidResult> {
|
||||
}
|
||||
drafts = drafts();
|
||||
|
||||
publishDrafts();
|
||||
db.changes().beginTransaction(changeId);
|
||||
try {
|
||||
publishDrafts();
|
||||
|
||||
final boolean isCurrent = patchSetId.equals(change.currentPatchSetId());
|
||||
if (isCurrent && change.getStatus().isOpen()) {
|
||||
publishApprovals(ctl);
|
||||
} else if (! approvals.isEmpty()) {
|
||||
throw new InvalidChangeOperationException("Change is closed");
|
||||
} else {
|
||||
publishMessageOnly();
|
||||
final boolean isCurrent = patchSetId.equals(change.currentPatchSetId());
|
||||
if (isCurrent && change.getStatus().isOpen()) {
|
||||
publishApprovals(ctl);
|
||||
} else if (!approvals.isEmpty()) {
|
||||
throw new InvalidChangeOperationException("Change is closed");
|
||||
} else {
|
||||
publishMessageOnly();
|
||||
}
|
||||
|
||||
touchChange();
|
||||
db.commit();
|
||||
} finally {
|
||||
db.rollback();
|
||||
}
|
||||
|
||||
touchChange();
|
||||
email();
|
||||
fireHook();
|
||||
return VoidResult.INSTANCE;
|
||||
@@ -280,7 +287,7 @@ public class PublishComments implements Callable<VoidResult> {
|
||||
}
|
||||
|
||||
private List<PatchLineComment> drafts() throws OrmException {
|
||||
return db.patchComments().draft(patchSetId, user.getAccountId()).toList();
|
||||
return db.patchComments().draftByPatchSetAuthor(patchSetId, user.getAccountId()).toList();
|
||||
}
|
||||
|
||||
private void email() {
|
||||
@@ -288,7 +295,7 @@ public class PublishComments implements Callable<VoidResult> {
|
||||
if (message != null) {
|
||||
final CommentSender cm = commentSenderFactory.create(change);
|
||||
cm.setFrom(user.getAccountId());
|
||||
cm.setPatchSet(patchSet, patchSetInfoFactory.get(patchSetId));
|
||||
cm.setPatchSet(patchSet, patchSetInfoFactory.get(db, patchSetId));
|
||||
cm.setChangeMessage(message);
|
||||
cm.setPatchLineComments(drafts);
|
||||
cm.send();
|
||||
|
@@ -16,7 +16,6 @@ package com.google.gerrit.server.schema;
|
||||
|
||||
import static com.google.inject.Scopes.SINGLETON;
|
||||
|
||||
import com.google.gerrit.lifecycle.LifecycleModule;
|
||||
import com.google.gerrit.server.GerritPersonIdent;
|
||||
import com.google.gerrit.server.GerritPersonIdentProvider;
|
||||
import com.google.gerrit.server.config.AllProjectsName;
|
||||
@@ -24,8 +23,6 @@ import com.google.gerrit.server.config.AllProjectsNameProvider;
|
||||
import com.google.gerrit.server.config.AnonymousCowardName;
|
||||
import com.google.gerrit.server.config.AnonymousCowardNameProvider;
|
||||
import com.google.gerrit.server.config.FactoryModule;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.git.LocalDiskRepositoryManager;
|
||||
|
||||
import org.eclipse.jgit.lib.PersonIdent;
|
||||
|
||||
@@ -44,13 +41,5 @@ public class SchemaModule extends FactoryModule {
|
||||
|
||||
bind(String.class).annotatedWith(AnonymousCowardName.class).toProvider(
|
||||
AnonymousCowardNameProvider.class);
|
||||
|
||||
bind(GitRepositoryManager.class).to(LocalDiskRepositoryManager.class);
|
||||
install(new LifecycleModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
listener().to(LocalDiskRepositoryManager.Lifecycle.class);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,29 @@
|
||||
// Copyright (C) 2010 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.ssh;
|
||||
|
||||
import com.google.gerrit.server.ssh.SshInfo;
|
||||
|
||||
import com.jcraft.jsch.HostKey;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
class NoSshInfo implements SshInfo {
|
||||
@Override
|
||||
public List<HostKey> getHostKeys() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
// Copyright (C) 2010 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.ssh;
|
||||
|
||||
import com.google.gerrit.common.errors.InvalidSshKeyException;
|
||||
import com.google.gerrit.reviewdb.AccountSshKey;
|
||||
|
||||
class NoSshKeyCache implements SshKeyCache {
|
||||
@Override
|
||||
public void evict(String username) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccountSshKey create(AccountSshKey.Id id, String encoded)
|
||||
throws InvalidSshKeyException {
|
||||
throw new InvalidSshKeyException();
|
||||
}
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
// Copyright (C) 2010 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.ssh;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
|
||||
/**
|
||||
* Disables the SSH support by stubbing out relevant objects.
|
||||
*/
|
||||
public class NoSshModule extends AbstractModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(SshInfo.class).to(NoSshInfo.class);
|
||||
bind(SshKeyCache.class).to(NoSshKeyCache.class);
|
||||
}
|
||||
}
|
@@ -216,7 +216,7 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
|
||||
|
||||
@Override
|
||||
public synchronized void start() {
|
||||
if (acceptor == null) {
|
||||
if (acceptor == null && !listen.isEmpty()) {
|
||||
checkConfig();
|
||||
|
||||
acceptor = createAcceptor();
|
||||
@@ -257,6 +257,10 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
|
||||
}
|
||||
|
||||
private List<HostKey> computeHostKeys() {
|
||||
if (listen.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final List<PublicKey> keys = myHostKeys();
|
||||
final ArrayList<HostKey> r = new ArrayList<HostKey>();
|
||||
for (final PublicKey pub : keys) {
|
||||
@@ -348,6 +352,10 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
|
||||
return bind;
|
||||
}
|
||||
|
||||
if (want.length == 1 && isOff(want[0])) {
|
||||
return bind;
|
||||
}
|
||||
|
||||
for (final String desc : want) {
|
||||
try {
|
||||
bind.add(SocketUtil.resolve(desc, DEFAULT_PORT));
|
||||
@@ -358,6 +366,12 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
|
||||
return bind;
|
||||
}
|
||||
|
||||
private static boolean isOff(String listenHostname) {
|
||||
return "off".equalsIgnoreCase(listenHostname)
|
||||
|| "none".equalsIgnoreCase(listenHostname)
|
||||
|| "no".equalsIgnoreCase(listenHostname);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void initProviderBouncyCastle() {
|
||||
setKeyExchangeFactories(Arrays.<NamedFactory<KeyExchange>> asList(
|
||||
|
@@ -25,6 +25,10 @@ import com.google.gerrit.server.config.GerritGlobalModule;
|
||||
import com.google.gerrit.server.config.GerritServerConfigModule;
|
||||
import com.google.gerrit.server.config.MasterNodeStartup;
|
||||
import com.google.gerrit.server.config.SitePath;
|
||||
import com.google.gerrit.server.git.LocalDiskRepositoryManager;
|
||||
import com.google.gerrit.server.git.PushReplication;
|
||||
import com.google.gerrit.server.git.WorkQueue;
|
||||
import com.google.gerrit.server.mail.SmtpEmailSender;
|
||||
import com.google.gerrit.server.schema.DataSourceProvider;
|
||||
import com.google.gerrit.server.schema.DatabaseModule;
|
||||
import com.google.gerrit.server.schema.SchemaModule;
|
||||
@@ -169,6 +173,7 @@ public class WebAppInitializer extends GuiceServletContextListener {
|
||||
modules.add(new GerritServerConfigModule());
|
||||
}
|
||||
modules.add(new SchemaModule());
|
||||
modules.add(new LocalDiskRepositoryManager.Module());
|
||||
modules.add(SchemaVersionCheck.module());
|
||||
modules.add(new AuthConfigModule());
|
||||
return dbInjector.createChildInjector(modules);
|
||||
@@ -176,7 +181,10 @@ public class WebAppInitializer extends GuiceServletContextListener {
|
||||
|
||||
private Injector createSysInjector() {
|
||||
final List<Module> modules = new ArrayList<Module>();
|
||||
modules.add(new WorkQueue.Module());
|
||||
modules.add(cfgInjector.getInstance(GerritGlobalModule.class));
|
||||
modules.add(new SmtpEmailSender.Module());
|
||||
modules.add(new PushReplication.Module());
|
||||
modules.add(new CanonicalWebUrlModule() {
|
||||
@Override
|
||||
protected Class<? extends Provider<String>> provider() {
|
||||
@@ -197,6 +205,9 @@ public class WebAppInitializer extends GuiceServletContextListener {
|
||||
private Injector createWebInjector() {
|
||||
final List<Module> modules = new ArrayList<Module>();
|
||||
modules.add(sshInjector.getInstance(WebModule.class));
|
||||
modules.add(sysInjector.getInstance(GitOverHttpModule.class));
|
||||
modules.add(sshInjector.getInstance(WebSshGlueModule.class));
|
||||
modules.add(CacheBasedWebSession.module());
|
||||
return sysInjector.createChildInjector(modules);
|
||||
}
|
||||
|
||||
|
2
pom.xml
2
pom.xml
@@ -47,7 +47,7 @@ limitations under the License.
|
||||
|
||||
<properties>
|
||||
<jgitVersion>1.0.0.201106090707-r.19-g8d88a84</jgitVersion>
|
||||
<gwtormVersion>1.1.5</gwtormVersion>
|
||||
<gwtormVersion>1.2-SNAPSHOT</gwtormVersion>
|
||||
<gwtjsonrpcVersion>1.2.5</gwtjsonrpcVersion>
|
||||
<gwtexpuiVersion>1.2.5</gwtexpuiVersion>
|
||||
<gwtVersion>2.3.0</gwtVersion>
|
||||
|
Reference in New Issue
Block a user