Merge changes I812deb6a,I803fe308,I1524f0c8,I823fa302,Iae4e63ab, ... into stable-3.4
* changes:
Documentation/user-review-ui: drop change owner description
polygerrit-ui: add a tooltip for 'Updated' metadata field
Documentation/user-review: new screenshot for file level comments
Documentation/user-review: trim outdated info from diff prefs
Documentation/user-review: remove syntax coloring screenshot
Documentation/user-review: update diff preferences section
Documentation/user-review: remove VIM search reference
Documentation/user-review: remove scrollbar screenshot
Documentation/user-review: update toggle review screenshot
Documentation/user-review: remove mention of red bar
Documentation/user-review: update quick approve screenshot
Documentation/user-review: remove screenshot for last update
Documentation/user-review-ui: remove mention of VIM navigation
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 1052c8e..5f2ea09 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -604,7 +604,14 @@
By default this is set to `LDAP` when link:#auth.type[`auth.type`] is `LDAP`
and `OAUTH` when link:#auth.type[`auth.type`] is `OAUTH`.
Otherwise, the default value is `HTTP`.
-
++
+When gitBasicAuthPolicy is set to `LDAP` or `HTTP_LDAP` and the user
+is authenticating with the LDAP username/password, the Git client config
+needs to have `http.cookieFile` set to a local file, otherwise every
+single call would trigger a full LDAP authentication and groups resolution
+which could introduce a noticeable latency on the overall execution
+and produce unwanted load to the LDAP server.
++
[[auth.gitOAuthProvider]]auth.gitOAuthProvider::
+
Selects the OAuth 2 provider to authenticate git over HTTP traffic with.
diff --git a/Documentation/user-upload.txt b/Documentation/user-upload.txt
index cdaf155..0670968 100644
--- a/Documentation/user-upload.txt
+++ b/Documentation/user-upload.txt
@@ -29,6 +29,13 @@
* Both, the HTTP and the LDAP passwords (in this order) if `gitBasicAuthPolicy`
is `HTTP_LDAP`.
+When gitBasicAuthPolicy is set to `LDAP` or `HTTP_LDAP` and the user
+is authenticating with the LDAP username/password, the Git client config
+needs to have `http.cookieFile` set to a local file, otherwise every
+single call would trigger a full LDAP authentication and groups resolution
+which could introduce a noticeable latency on the overall execution
+and produce unwanted load to the LDAP server.
+
When gitBasicAuthPolicy is not `LDAP`, the user's HTTP credentials can
be regenerated by going to `Settings`, and then accessing the `HTTP
Password` tab. Revocation can effectively be done by regenerating the
diff --git a/java/com/google/gerrit/httpd/CacheBasedWebSession.java b/java/com/google/gerrit/httpd/CacheBasedWebSession.java
index a3a67e5..7212e3e 100644
--- a/java/com/google/gerrit/httpd/CacheBasedWebSession.java
+++ b/java/com/google/gerrit/httpd/CacheBasedWebSession.java
@@ -76,29 +76,27 @@
this.identified = identified;
this.byIdCache = byIdCache;
- if (request.getRequestURI() == null || !GitSmartHttpTools.isGitClient(request)) {
- String cookie = readCookie(request);
- if (cookie != null) {
- authFromCookie(cookie);
- } else {
- String token;
- try {
- token = ParameterParser.getQueryParams(request).accessToken();
- } catch (BadRequestException e) {
- token = null;
- }
- if (token != null) {
- authFromQueryParameter(token);
- }
+ String cookie = readCookie(request);
+ if (cookie != null) {
+ authFromCookie(cookie);
+ } else if (request.getRequestURI() == null || !GitSmartHttpTools.isGitClient(request)) {
+ String token;
+ try {
+ token = ParameterParser.getQueryParams(request).accessToken();
+ } catch (BadRequestException e) {
+ token = null;
}
- if (val != null && !checkAccountStatus(val.getAccountId())) {
- val = null;
- okPaths.clear();
+ if (token != null) {
+ authFromQueryParameter(token);
}
- if (val != null && val.needsCookieRefresh()) {
- // Session is more than half old; update cache entry with new expiration date.
- val = manager.createVal(key, val);
- }
+ }
+ if (val != null && !checkAccountStatus(val.getAccountId())) {
+ val = null;
+ okPaths.clear();
+ }
+ if (val != null && val.needsCookieRefresh()) {
+ // Session is more than half old; update cache entry with new expiration date.
+ val = manager.createVal(key, val);
}
}
diff --git a/java/com/google/gerrit/httpd/ProjectBasicAuthFilter.java b/java/com/google/gerrit/httpd/ProjectBasicAuthFilter.java
index ca780d0..7fcd4f8 100644
--- a/java/com/google/gerrit/httpd/ProjectBasicAuthFilter.java
+++ b/java/com/google/gerrit/httpd/ProjectBasicAuthFilter.java
@@ -22,6 +22,7 @@
import com.google.common.base.Strings;
import com.google.common.flogger.FluentLogger;
import com.google.common.io.BaseEncoding;
+import com.google.gerrit.common.Nullable;
import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.client.GitBasicAuthPolicy;
import com.google.gerrit.extensions.registration.DynamicItem;
@@ -144,7 +145,7 @@
if (gitBasicAuthPolicy == GitBasicAuthPolicy.HTTP
|| gitBasicAuthPolicy == GitBasicAuthPolicy.HTTP_LDAP) {
if (PasswordVerifier.checkPassword(who.externalIds(), username, password)) {
- return succeedAuthentication(who);
+ return succeedAuthentication(who, null);
}
}
@@ -157,11 +158,11 @@
try {
AuthResult whoAuthResult = accountManager.authenticate(whoAuth);
- setUserIdentified(whoAuthResult.getAccountId());
+ setUserIdentified(whoAuthResult.getAccountId(), whoAuthResult);
return true;
} catch (NoSuchUserException e) {
if (PasswordVerifier.checkPassword(who.externalIds(), username, password)) {
- return succeedAuthentication(who);
+ return succeedAuthentication(who, null);
}
logger.atWarning().withCause(e).log(authenticationFailedMsg(username, req));
rsp.sendError(SC_UNAUTHORIZED);
@@ -183,8 +184,8 @@
}
}
- private boolean succeedAuthentication(AccountState who) {
- setUserIdentified(who.account().id());
+ private boolean succeedAuthentication(AccountState who, @Nullable AuthResult whoAuthResult) {
+ setUserIdentified(who.account().id(), whoAuthResult);
return true;
}
@@ -201,11 +202,15 @@
return String.format("Authentication from %s failed for %s", req.getRemoteAddr(), username);
}
- private void setUserIdentified(Account.Id id) {
+ private void setUserIdentified(Account.Id id, @Nullable AuthResult whoAuthResult) {
WebSession ws = session.get();
ws.setUserAccountId(id);
ws.setAccessPathOk(AccessPath.GIT, true);
ws.setAccessPathOk(AccessPath.REST_API, true);
+
+ if (whoAuthResult != null) {
+ ws.login(whoAuthResult, false);
+ }
}
private String encoding(HttpServletRequest req) {
diff --git a/javatests/com/google/gerrit/httpd/BUILD b/javatests/com/google/gerrit/httpd/BUILD
index 75f005e..121cbc4 100644
--- a/javatests/com/google/gerrit/httpd/BUILD
+++ b/javatests/com/google/gerrit/httpd/BUILD
@@ -18,6 +18,7 @@
"//lib:junit",
"//lib:servlet-api-without-neverlink",
"//lib:soy",
+ "//lib/bouncycastle:bcprov",
"//lib/guice",
"//lib/guice:guice-servlet",
"//lib/mockito",
diff --git a/javatests/com/google/gerrit/httpd/ProjectBasicAuthFilterTest.java b/javatests/com/google/gerrit/httpd/ProjectBasicAuthFilterTest.java
index f7792ed..735abbf 100644
--- a/javatests/com/google/gerrit/httpd/ProjectBasicAuthFilterTest.java
+++ b/javatests/com/google/gerrit/httpd/ProjectBasicAuthFilterTest.java
@@ -19,12 +19,15 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import com.google.common.collect.ImmutableSet;
import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.client.GitBasicAuthPolicy;
import com.google.gerrit.extensions.registration.DynamicItem;
+import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountException;
import com.google.gerrit.server.account.AccountManager;
@@ -55,11 +58,17 @@
private static final String AUTH_USER_B64 =
B64_ENC.encodeToString(AUTH_USER.getBytes(StandardCharsets.UTF_8));
private static final String AUTH_PASSWORD = "jd123";
+ private static final String GERRIT_COOKIE_KEY = "GerritAccount";
+ private static final String AUTH_COOKIE_VALUE = "gerritcookie";
+ private static final ExternalId AUTH_USER_PASSWORD_EXTERNAL_ID =
+ ExternalId.createWithPassword(
+ ExternalId.Key.create(ExternalId.SCHEME_USERNAME, AUTH_USER),
+ AUTH_ACCOUNT_ID,
+ null,
+ AUTH_PASSWORD);
@Mock private DynamicItem<WebSession> webSessionItem;
- @Mock private WebSession webSession;
-
@Mock private AccountCache accountCache;
@Mock private AccountState accountState;
@@ -74,21 +83,57 @@
@Captor private ArgumentCaptor<HttpServletResponse> filterResponseCaptor;
+ @Mock private IdentifiedUser.RequestFactory userRequestFactory;
+
+ @Mock private WebSessionManager webSessionManager;
+
+ private WebSession webSession;
private FakeHttpServletRequest req;
private HttpServletResponse res;
+ private AuthResult authSuccessful;
@Before
public void setUp() throws Exception {
- doReturn(webSession).when(webSessionItem).get();
- doReturn(Optional.of(accountState)).when(accountCache).getByUsername(AUTH_USER);
- doReturn(account).when(accountState).account();
-
req = new FakeHttpServletRequest();
res = new FakeHttpServletResponse();
+
+ authSuccessful =
+ new AuthResult(AUTH_ACCOUNT_ID, ExternalId.Key.create("username", AUTH_USER), false);
+ doReturn(Optional.of(accountState)).when(accountCache).getByUsername(AUTH_USER);
+ doReturn(Optional.of(accountState)).when(accountCache).get(AUTH_ACCOUNT_ID);
+ doReturn(account).when(accountState).account();
+ doReturn(ImmutableSet.builder().add(AUTH_USER_PASSWORD_EXTERNAL_ID).build())
+ .when(accountState)
+ .externalIds();
+
+ doReturn(new WebSessionManager.Key(AUTH_COOKIE_VALUE)).when(webSessionManager).createKey(any());
+ WebSessionManager.Val webSessionValue =
+ new WebSessionManager.Val(AUTH_ACCOUNT_ID, 0L, false, null, 0L, "", "");
+ doReturn(webSessionValue)
+ .when(webSessionManager)
+ .createVal(any(), any(), eq(false), any(), any(), any());
+ }
+
+ private void initWebSessionWithCookie(String cookie) {
+ req.addHeader("Cookie", cookie);
+ initWebSessionWithoutCookie();
+ }
+
+ private void initWebSessionWithoutCookie() {
+ webSession =
+ new CacheBasedWebSession(
+ req, res, webSessionManager, authConfig, null, userRequestFactory, accountCache) {};
+ doReturn(webSession).when(webSessionItem).get();
+ }
+
+ private void initMockedWebSession() {
+ webSession = mock(WebSession.class);
+ doReturn(webSession).when(webSessionItem).get();
}
@Test
public void shouldAllowAnonymousRequest() throws Exception {
+ initMockedWebSession();
res.setStatus(HttpServletResponse.SC_OK);
ProjectBasicAuthFilter basicAuthFilter =
@@ -102,6 +147,7 @@
@Test
public void shouldRequestAuthenticationForBasicAuthRequest() throws Exception {
+ initMockedWebSession();
req.addHeader("Authorization", "Basic " + AUTH_USER_B64);
res.setStatus(HttpServletResponse.SC_OK);
@@ -116,16 +162,11 @@
}
@Test
- public void shouldAuthenticateSucessfullyAgainstRealm() throws Exception {
- req.addHeader(
- "Authorization",
- "Basic "
- + B64_ENC.encodeToString(
- (AUTH_USER + ":" + AUTH_PASSWORD).getBytes(StandardCharsets.UTF_8)));
+ public void shouldAuthenticateSucessfullyAgainstRealmAndReturnCookie() throws Exception {
+ initWebSessionWithoutCookie();
+ requestBasicAuth(req);
res.setStatus(HttpServletResponse.SC_OK);
- AuthResult authSuccessful =
- new AuthResult(AUTH_ACCOUNT_ID, ExternalId.Key.create("username", AUTH_USER), false);
doReturn(true).when(account).isActive();
doReturn(authSuccessful).when(accountManager).authenticate(any());
doReturn(GitBasicAuthPolicy.LDAP).when(authConfig).getGitBasicAuthPolicy();
@@ -139,18 +180,51 @@
verify(chain).doFilter(eq(req), any());
assertThat(res.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
+ assertThat(res.getHeader("Set-Cookie")).contains(GERRIT_COOKIE_KEY);
+ }
+
+ @Test
+ public void shouldValidateUserPasswordAndNotReturnCookie() throws Exception {
+ initWebSessionWithoutCookie();
+ requestBasicAuth(req);
+ res.setStatus(HttpServletResponse.SC_OK);
+
+ doReturn(true).when(account).isActive();
+ doReturn(GitBasicAuthPolicy.HTTP).when(authConfig).getGitBasicAuthPolicy();
+
+ ProjectBasicAuthFilter basicAuthFilter =
+ new ProjectBasicAuthFilter(webSessionItem, accountCache, accountManager, authConfig);
+
+ basicAuthFilter.doFilter(req, res, chain);
+
+ verify(accountManager, never()).authenticate(any());
+
+ verify(chain).doFilter(eq(req), any());
+ assertThat(res.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
+ assertThat(res.getHeader("Set-Cookie")).isNull();
}
@Test
public void shouldNotReauthenticateIfAlreadySignedIn() throws Exception {
- req.addHeader(
- "Authorization",
- "Basic "
- + B64_ENC.encodeToString(
- (AUTH_USER + ":" + AUTH_PASSWORD).getBytes(StandardCharsets.UTF_8)));
+ initMockedWebSession();
+ doReturn(true).when(webSession).isSignedIn();
+ requestBasicAuth(req);
res.setStatus(HttpServletResponse.SC_OK);
- doReturn(true).when(webSession).isSignedIn();
+ ProjectBasicAuthFilter basicAuthFilter =
+ new ProjectBasicAuthFilter(webSessionItem, accountCache, accountManager, authConfig);
+
+ basicAuthFilter.doFilter(req, res, chain);
+
+ verify(accountManager, never()).authenticate(any());
+ verify(chain).doFilter(eq(req), any());
+ assertThat(res.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
+ }
+
+ @Test
+ public void shouldNotReauthenticateIfHasExistingCookie() throws Exception {
+ initWebSessionWithCookie("GerritAccount=" + AUTH_COOKIE_VALUE);
+ res.setStatus(HttpServletResponse.SC_OK);
ProjectBasicAuthFilter basicAuthFilter =
new ProjectBasicAuthFilter(webSessionItem, accountCache, accountManager, authConfig);
@@ -164,11 +238,8 @@
@Test
public void shouldFailedAuthenticationAgainstRealm() throws Exception {
- req.addHeader(
- "Authorization",
- "Basic "
- + B64_ENC.encodeToString(
- (AUTH_USER + ":" + AUTH_PASSWORD).getBytes(StandardCharsets.UTF_8)));
+ initMockedWebSession();
+ requestBasicAuth(req);
doReturn(true).when(account).isActive();
doThrow(new AccountException("Authentication error")).when(accountManager).authenticate(any());
@@ -184,4 +255,12 @@
verify(chain, never()).doFilter(any(), any());
assertThat(res.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
}
+
+ private void requestBasicAuth(FakeHttpServletRequest fakeReq) {
+ fakeReq.addHeader(
+ "Authorization",
+ "Basic "
+ + B64_ENC.encodeToString(
+ (AUTH_USER + ":" + AUTH_PASSWORD).getBytes(StandardCharsets.UTF_8)));
+ }
}
diff --git a/javatests/com/google/gerrit/util/http/testutil/FakeHttpServletRequest.java b/javatests/com/google/gerrit/util/http/testutil/FakeHttpServletRequest.java
index a4175e3..2efa94b 100644
--- a/javatests/com/google/gerrit/util/http/testutil/FakeHttpServletRequest.java
+++ b/javatests/com/google/gerrit/util/http/testutil/FakeHttpServletRequest.java
@@ -17,6 +17,7 @@
import static com.google.common.base.Preconditions.checkArgument;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
+import static java.util.stream.Collectors.toList;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
@@ -257,7 +258,15 @@
@Override
public Cookie[] getCookies() {
- return new Cookie[0];
+ return Splitter.on(";").splitToList(Strings.nullToEmpty(getHeader("Cookie"))).stream()
+ .filter(s -> !s.isEmpty())
+ .map(
+ (String cookieValue) -> {
+ String[] kv = cookieValue.split("=");
+ return new Cookie(kv[0], kv[1]);
+ })
+ .collect(toList())
+ .toArray(new Cookie[0]);
}
@Override
diff --git a/javatests/com/google/gerrit/util/http/testutil/FakeHttpServletResponse.java b/javatests/com/google/gerrit/util/http/testutil/FakeHttpServletResponse.java
index 9a98ecd..f39b875 100644
--- a/javatests/com/google/gerrit/util/http/testutil/FakeHttpServletResponse.java
+++ b/javatests/com/google/gerrit/util/http/testutil/FakeHttpServletResponse.java
@@ -161,7 +161,7 @@
@Override
public void addCookie(Cookie cookie) {
- throw new UnsupportedOperationException();
+ addHeader("Set-Cookie", cookie.getName() + "=" + cookie.getValue());
}
@Override
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.ts b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.ts
index 874f572..eb943f7 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.ts
@@ -345,7 +345,8 @@
contextGroups,
showAbove,
showBelow,
- numLines
+ numLines,
+ viewMode
)
);
if (showBelow) {
@@ -364,19 +365,36 @@
contextGroups: GrDiffGroup[],
showAbove: boolean,
showBelow: boolean,
- numLines: number
+ numLines: number,
+ viewMode: DiffViewMode
): HTMLElement {
- const row = this._createElement('tr', 'contextDivider');
- if (!(showAbove && showBelow)) {
- row.classList.add('collapsed');
+ const row = this._createElement('tr', 'dividerRow');
+ if (showAbove && !showBelow) {
+ row.classList.add('showAboveOnly');
+ } else if (!showAbove && showBelow) {
+ row.classList.add('showBelowOnly');
+ } else {
+ // Note that !showAbove && !showBelow also intentionally creates
+ // "showBoth". This means the file is completely collapsed, which is
+ // unusual, but at least happens in one test.
+ row.classList.add('showBoth');
}
- const element = this._createElement('td', 'dividerCell');
- row.appendChild(element);
+ row.appendChild(this._createBlameCell(0));
+ if (viewMode === DiffViewMode.SIDE_BY_SIDE) {
+ row.appendChild(this._createElement('td'));
+ }
+
+ const cell = this._createElement('td', 'dividerCell');
+ cell.setAttribute('colspan', '3');
+ row.appendChild(cell);
+ const verticalFlex = this._createElement('div', 'verticalFlex');
+ cell.appendChild(verticalFlex);
+ const horizontalFlex = this._createElement('div', 'horizontalFlex');
+ verticalFlex.appendChild(horizontalFlex);
const showAllContainer = this._createElement('div', 'aboveBelowButtons');
- element.appendChild(showAllContainer);
-
+ horizontalFlex.appendChild(showAllContainer);
const showAllButton = this._createContextButton(
ContextButtonType.ALL,
section,
@@ -415,7 +433,7 @@
)
);
}
- element.appendChild(container);
+ horizontalFlex.appendChild(container);
}
return row;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_html.ts b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_html.ts
index a93d5b5..e254728 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_html.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_html.ts
@@ -318,38 +318,64 @@
height: calc(var(--line-height-normal) + var(--spacing-s));
}
- .contextDivider {
- height: var(--divider-height);
- /* Create a positioning context. */
- transform: translateX(0px);
+ .dividerCell {
+ vertical-align: top;
}
- .contextDivider.collapsed {
- /* Hide divider gap, but still show child elements (expansion buttons). */
+ .dividerRow.showBoth .dividerCell {
+ height: var(--divider-height);
+ }
+ .dividerRow.showAboveOnly .dividerCell,
+ .dividerRow.showBelowOnly .dividerCell {
height: 0;
}
- .dividerCell {
- width: 100%;
- height: 100%;
+
+ .verticalFlex {
+ display: flex;
+ flex-direction: column;
+ position: relative;
+ }
+ .dividerRow.showBoth .verticalFlex {
+ justify-content: center;
+ margin-top: calc(0px - var(--line-height-normal) - var(--spacing-s));
+ margin-bottom: calc(0px - var(--line-height-normal) - var(--spacing-s));
+ height: calc(
+ 2 * var(--line-height-normal) + 2 * var(--spacing-s) +
+ var(--divider-height) - 1px
+ );
+ }
+ .dividerRow.showAboveOnly .verticalFlex {
+ justify-content: flex-end;
+ /* margin-top has to make room for height+1px. */
+ margin-top: calc(-1px - var(--line-height-normal) - var(--spacing-s));
+ height: calc(var(--line-height-normal) + var(--spacing-s));
+ }
+ .dividerRow.showBelowOnly .verticalFlex {
+ justify-content: flex-start;
+ /* This just pushes the container down 1 pixel as to render below the
+ 1px border-top of the padding row below. The same could be achieved
+ by position:relative; top:1px.*/
+ margin-top: 1px;
+ margin-bottom: calc(0px - var(--line-height-normal) - var(--spacing-s));
+ }
+
+ .horizontalFlex {
display: flex;
justify-content: center;
- position: absolute;
- top: 0;
- left: 0;
+ }
+ .dividerRow.showBoth .horizontalFlex {
+ align-items: center;
+ }
+ .dividerRow.showAboveOnly .horizontalFlex {
+ align-items: end;
+ }
+ .dividerRow.showBelowOnly .horizontalFlex {
+ align-items: start;
}
.contextControlButton {
background-color: var(--default-button-background-color);
font: var(--context-control-button-font, inherit);
- /* All position is relative to container, so ignore sibling buttons. */
- position: absolute;
- }
- .contextControlButton:first-child {
- /* First button needs to claim width to display without text wrapping. */
- position: relative;
}
.centeredButton {
- /* Center over divider. */
- top: 50%;
- transform: translateY(-50%);
--gr-button: {
color: var(--diff-context-control-color);
border-style: solid;
@@ -368,15 +394,17 @@
.aboveBelowButtons {
display: flex;
flex-direction: column;
+ justify-content: center;
margin-left: var(--spacing-m);
- position: relative;
}
.aboveBelowButtons:first-child {
margin-left: 0;
}
+ .dividerRow.showBoth .aboveButton {
+ /* The size of the gap between the above and below button. */
+ margin-bottom: calc(var(--divider-height) + 1px);
+ }
.aboveButton {
- /* Display over preceding content / background placeholder. */
- transform: translateY(-100%);
--gr-button: {
color: var(--diff-context-control-color);
border-style: solid;
@@ -393,8 +421,6 @@
}
}
.belowButton {
- /* Display over following content / background placeholder. */
- top: calc(100% + var(--divider-border));
--gr-button: {
color: var(--diff-context-control-color);
border-style: solid;
diff --git a/tools/maven/gerrit-acceptance-framework_pom.xml b/tools/maven/gerrit-acceptance-framework_pom.xml
index 8e47603..00aeb47 100644
--- a/tools/maven/gerrit-acceptance-framework_pom.xml
+++ b/tools/maven/gerrit-acceptance-framework_pom.xml
@@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-acceptance-framework</artifactId>
- <version>3.4.0-rc4</version>
+ <version>3.4.0-rc5</version>
<packaging>jar</packaging>
<name>Gerrit Code Review - Acceptance Test Framework</name>
<description>Framework for Gerrit's acceptance tests</description>
diff --git a/tools/maven/gerrit-extension-api_pom.xml b/tools/maven/gerrit-extension-api_pom.xml
index 78767dd..2df031a 100644
--- a/tools/maven/gerrit-extension-api_pom.xml
+++ b/tools/maven/gerrit-extension-api_pom.xml
@@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-extension-api</artifactId>
- <version>3.4.0-rc4</version>
+ <version>3.4.0-rc5</version>
<packaging>jar</packaging>
<name>Gerrit Code Review - Extension API</name>
<description>API for Gerrit Extensions</description>
diff --git a/tools/maven/gerrit-plugin-api_pom.xml b/tools/maven/gerrit-plugin-api_pom.xml
index 3ceda6f..95fc0bf 100644
--- a/tools/maven/gerrit-plugin-api_pom.xml
+++ b/tools/maven/gerrit-plugin-api_pom.xml
@@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-plugin-api</artifactId>
- <version>3.4.0-rc4</version>
+ <version>3.4.0-rc5</version>
<packaging>jar</packaging>
<name>Gerrit Code Review - Plugin API</name>
<description>API for Gerrit Plugins</description>
diff --git a/tools/maven/gerrit-war_pom.xml b/tools/maven/gerrit-war_pom.xml
index c3a64c8..6ea6cbf 100644
--- a/tools/maven/gerrit-war_pom.xml
+++ b/tools/maven/gerrit-war_pom.xml
@@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-war</artifactId>
- <version>3.4.0-rc4</version>
+ <version>3.4.0-rc5</version>
<packaging>war</packaging>
<name>Gerrit Code Review - WAR</name>
<description>Gerrit WAR</description>
diff --git a/version.bzl b/version.bzl
index 75804f8..f74b684 100644
--- a/version.bzl
+++ b/version.bzl
@@ -2,4 +2,4 @@
# Used by :api_install and :api_deploy targets
# when talking to the destination repository.
#
-GERRIT_VERSION = "3.4.0-rc4"
+GERRIT_VERSION = "3.4.0-rc5"