Merge changes I3bbfc470,I628c4c99 * changes: Prevent injecting IdentifiedUser for project REST endpoints Refactor UiAction specific code out of change specific code
diff --git a/Documentation/access-control.txt b/Documentation/access-control.txt index 9c964e0..db806cc 100644 --- a/Documentation/access-control.txt +++ b/Documentation/access-control.txt
@@ -600,7 +600,7 @@ [[category_push_merge]] Push Merge Commits -~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~ The `Push Merge Commit` access right permits the user to upload merge commits. It's an add-on to the <<category_push,Push>> access right, and
diff --git a/Documentation/cmd-version.txt b/Documentation/cmd-version.txt index d1f94ea..aa08848 100644 --- a/Documentation/cmd-version.txt +++ b/Documentation/cmd-version.txt
@@ -1,5 +1,5 @@ gerrit version -================ +============== NAME ----
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt index 3c35054..339b0c0 100644 --- a/Documentation/config-gerrit.txt +++ b/Documentation/config-gerrit.txt
@@ -335,7 +335,17 @@ + Target for the "Obtain Password" link. Used only when `auth.type` is `LDAP`, `LDAP_BIND` or `CUSTOM_EXTENSION`. + +[[auth.switchAccountUrl]]auth.switchAccountUrl:: + +URL to switch user identities and login as a different account than +the currently active account. This is disabled by default except when +`auth.type` is `OPENID` and `DEVELOPMENT_BECOME_ANY_ACCOUNT`. If set +the "Switch Account" link is displayed next to "Sign Out". ++ +When `auth.type` does not normally enable this URL administrators may +set this to `login/` or `$canonicalWebUrl/login`, allowing users to +begin a new web session. [[auth.cookiePath]]auth.cookiePath:: +
diff --git a/Documentation/config-labels.txt b/Documentation/config-labels.txt index 7df55fe..a1cf9ee 100644 --- a/Documentation/config-labels.txt +++ b/Documentation/config-labels.txt
@@ -177,7 +177,7 @@ [[label_abbreviation]] `label.Label-Name.abbreviation` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ An abbreviated name for a label shown as a compact column header, for example on project dashboards. Defaults to all the uppercase characters
diff --git a/Documentation/config-login-register.txt b/Documentation/config-login-register.txt index d0e0fc5..867f0d4 100644 --- a/Documentation/config-login-register.txt +++ b/Documentation/config-login-register.txt
@@ -135,4 +135,9 @@ git clone ssh://user@localhost:29418/REPOSITORY_NAME.git user@host:~$ ----- \ No newline at end of file +---- + + +GERRIT +------ +Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/database-setup.txt b/Documentation/database-setup.txt index b1561cb..3800473 100644 --- a/Documentation/database-setup.txt +++ b/Documentation/database-setup.txt
@@ -108,3 +108,8 @@ [database] password = secret_pasword ---- + + +GERRIT +------ +Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/dev-release.txt b/Documentation/dev-release.txt index c06d16a..f9d0d0e 100644 --- a/Documentation/dev-release.txt +++ b/Documentation/dev-release.txt
@@ -83,7 +83,7 @@ Create the Actual Release ---------------------------- +------------------------- To create a Gerrit release the following steps have to be done:
diff --git a/Documentation/error-has-duplicates.txt b/Documentation/error-has-duplicates.txt index e9e42f4..b5175c0 100644 --- a/Documentation/error-has-duplicates.txt +++ b/Documentation/error-has-duplicates.txt
@@ -1,5 +1,5 @@ -... has duplicates -================== +\... has duplicates +=================== With this error message Gerrit rejects to push a commit if its commit message contains a Change-ID for which multiple changes can be found
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt index 7dbdf94..96e19cc 100644 --- a/Documentation/rest-api-accounts.txt +++ b/Documentation/rest-api-accounts.txt
@@ -947,7 +947,7 @@ [[email-id]] \{email-id\} -~~~~~~~~~~~~~~ +~~~~~~~~~~~~ An email address, or `preferred` for the preferred email address of the user. @@ -958,7 +958,7 @@ [[ssh-key-id]] \{ssh-key-id\} -~~~~~~~~~~~~ +~~~~~~~~~~~~~~ The sequence number of the SSH key.
diff --git a/Documentation/rest-api-plugins.txt b/Documentation/rest-api-plugins.txt new file mode 100644 index 0000000..099970c --- /dev/null +++ b/Documentation/rest-api-plugins.txt
@@ -0,0 +1,106 @@ +Gerrit Code Review - /plugins/ REST API +======================================= + +This page describes the plugin related REST endpoints. +Please also take note of the general information on the +link:rest-api.html[REST API]. + +Endpoints +--------- + +[[plugin-endpoints]] +Plugin Endpoints +---------------- + +[[install-plugin]] +Install Plugin +~~~~~~~~~~~~~~ +[verse] +'PUT /plugins/link:#plugin-id[\{plugin-id\}]' + +Installs a new plugin on the Gerrit server. If a plugin with the +specified name already exists it is overwritten. + +The plugin jar can either be sent as binary data in the request body +or a URL to the plugin jar must be provided in the request body inside +a link:#plugin-input[PluginInput] entity. + +.Request +---- + PUT /plugins/delete-project HTTP/1.0 + Content-Type: application/json;charset=UTF-8 + + { + "url": "file:///gerrit/plugins/delete-project/delete-project-2.8.jar" + } +---- + +To provide the plugin jar as binary data in the request body the +following curl command can be used: + +---- + curl --digest --user admin:TNNuLkWsIV8w -X PUT --data-binary @delete-project-2.8.jar 'http://gerrit:8080/a/plugins/delete-project' +---- + +As response a link:#plugin-info[PluginInfo] entity is returned that +describes the plugin. + +.Response +---- + HTTP/1.1 201 Created + Content-Disposition: attachment + Content-Type: application/json;charset=UTF-8 + + )]}' + { + "kind": "gerritcodereview#plugin", + "id": "delete-project", + "version": "2.8" + } +---- + +If an existing plugin was overwritten the response is "`200 OK`". + + +[[ids]] +IDs +--- + +[[plugin-id]] +\{plugin-id\} +~~~~~~~~~~~~~ +The ID of the plugin. + + +[[json-entities]] +JSON Entities +------------- + +[[plugin-info]] +PluginInfo +~~~~~~~~~~ +The `PluginInfo` entity describes a plugin. + +[options="header",width="50%",cols="1,6"] +|====================== +|Field Name|Description +|`kind` |`gerritcodereview#plugin` +|`id` |The ID of the plugin. +|`version` |The version of the plugin. +|====================== + +[[plugin-input]] +PluginInput +~~~~~~~~~~~ +The `PluginInput` entity describes a plugin that should be installed. + +[options="header",width="50%",cols="1,6"] +|====================== +|Field Name|Description +|`url` |URL to the plugin jar. +|====================== + + +GERRIT +------ +Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/rest-api.txt b/Documentation/rest-api.txt index e3fc105..7eed6ef 100644 --- a/Documentation/rest-api.txt +++ b/Documentation/rest-api.txt
@@ -19,6 +19,8 @@ Config related REST endpoints link:rest-api-groups.html[/groups/]:: Group related REST endpoints +link:rest-api-plugins.html[/plugins/]:: + Plugin related REST endpoints link:rest-api-projects.html[/projects/]:: Project related REST endpoints
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java index 324f00b..f85e333 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java
@@ -28,6 +28,7 @@ protected String registerText; protected String loginUrl; protected String loginText; + protected String switchAccountUrl; protected String httpPasswordUrl; protected String reportBugUrl; protected boolean gitBasicAuth; @@ -75,6 +76,14 @@ registerUrl = u; } + public String getSwitchAccountUrl() { + return switchAccountUrl; + } + + public void setSwitchAccountUrl(String u) { + switchAccountUrl = u; + } + public String getRegisterText() { return registerText; }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java index 683f058..fb72085 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java
@@ -18,9 +18,7 @@ public interface GerritConstants extends Constants { String menuSignIn(); - String menuSignOut(); String menuRegister(); - String menuSettings(); String reportBug(); String signInDialogTitle();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties index defc7e4..6f3dca5 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties
@@ -1,7 +1,5 @@ menuSignIn = Sign In -menuSignOut = Sign Out menuRegister = Register -menuSettings = Settings reportBug = Report Bug signInDialogTitle = Code Review - Sign In
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/UserPopupPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/UserPopupPanel.java index d5bbd17..90348db 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/UserPopupPanel.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/UserPopupPanel.java
@@ -16,11 +16,12 @@ import com.google.gerrit.client.account.AccountInfo; import com.google.gerrit.client.ui.InlineHyperlink; +import com.google.gerrit.reviewdb.client.AuthType; import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.AnchorElement; import com.google.gwt.dom.client.Element; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; -import com.google.gwt.user.client.ui.Anchor; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.Widget; import com.google.gwtexpui.user.client.PluginSafePopupPanel; @@ -33,7 +34,8 @@ @UiField Label userName; @UiField Label userEmail; @UiField Element userLinks; - @UiField Anchor logout; + @UiField AnchorElement switchAccount; + @UiField AnchorElement logout; @UiField InlineHyperlink settings; public UserPopupPanel(AccountInfo account, boolean canLogOut, @@ -49,11 +51,22 @@ userEmail.setText(account.email()); } if (showSettingsLink) { + if (Gerrit.getConfig().getSwitchAccountUrl() != null) { + switchAccount.setHref(Gerrit.getConfig().getSwitchAccountUrl()); + } else if (Gerrit.getConfig().getAuthType() == AuthType.DEVELOPMENT_BECOME_ANY_ACCOUNT + || Gerrit.getConfig().getAuthType() == AuthType.OPENID) { + switchAccount.setHref(Gerrit.selfRedirect("/login/")); + } else { + switchAccount.removeFromParent(); + switchAccount = null; + } if (canLogOut) { logout.setHref(Gerrit.selfRedirect("/logout")); } else { - logout.setVisible(false); + logout.removeFromParent(); + logout = null; } + } else { settings.removeFromParent(); settings = null;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/UserPopupPanel.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/UserPopupPanel.ui.xml index 4a4f4ca..cd51485 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/UserPopupPanel.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/UserPopupPanel.ui.xml
@@ -19,8 +19,6 @@ xmlns:g='urn:import:com.google.gwt.user.client.ui' xmlns:gerrit='urn:import:com.google.gerrit.client' xmlns:u='urn:import:com.google.gerrit.client.ui'> - <ui:with field='constants' type='com.google.gerrit.client.GerritConstants'/> - <ui:style> .panel { padding: 8px; @@ -36,16 +34,20 @@ .userName { font-weight: bold; } - .userLinks { - min-width: 175px; - } .email { padding-bottom: 6px; } - .logout { - padding-left: 16px; + .userLinks { + min-width: 250px; + } + .userLinksRight { float: right; } + .switchAccount { + border-right: 1px solid black; + padding-right: 0.5em; + margin-right: 0.5em; + } </ui:style> <g:HTMLPanel styleName='{style.panel}'> @@ -59,9 +61,10 @@ <u:InlineHyperlink ui:field='settings' targetHistoryToken='/settings/'> <ui:msg>Settings</ui:msg> </u:InlineHyperlink> - <g:Anchor ui:field='logout' styleName="{style.logout}"> - <ui:text from='{constants.menuSignOut}' /> - </g:Anchor> + <span class='{style.userLinksRight}'> + <a ui:field='switchAccount' class='{style.switchAccount}'><ui:msg>Switch Account</ui:msg></a + ><a ui:field='logout'><ui:msg>Sign Out</ui:msg></a> + </span> </div> </g:HTMLPanel> </ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.java index e1219be..970b317 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.java
@@ -46,7 +46,6 @@ import com.google.gerrit.client.ui.CommentLinkProcessor; import com.google.gerrit.client.ui.Screen; import com.google.gerrit.client.ui.UserActivityMonitor; -import com.google.gerrit.common.PageLinks; import com.google.gerrit.common.changes.ListChangesOption; import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.PatchSet; @@ -57,7 +56,6 @@ import com.google.gwt.core.client.JsArrayString; import com.google.gwt.dom.client.AnchorElement; import com.google.gwt.dom.client.Element; -import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.KeyPressEvent; import com.google.gwt.event.logical.shared.CloseEvent; @@ -72,10 +70,8 @@ import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.HTMLPanel; -import com.google.gwt.user.client.ui.ListBox; import com.google.gwt.user.client.ui.PopupPanel; import com.google.gwt.user.client.ui.ToggleButton; -import com.google.gwt.user.client.ui.UIObject; import com.google.gwtexpui.clippy.client.CopyableLabel; import com.google.gwtexpui.globalkey.client.GlobalKey; import com.google.gwtexpui.globalkey.client.KeyCommand; @@ -84,7 +80,6 @@ import java.sql.Timestamp; import java.util.ArrayList; -import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.List; @@ -139,14 +134,13 @@ @UiField Element actionDate; @UiField Actions actions; - @UiField Element revisionParent; - @UiField ListBox revisionList; @UiField Labels labels; @UiField CommitBox commit; @UiField RelatedChanges related; @UiField FileTable files; @UiField FlowPanel history; + @UiField Button revisions; @UiField Button download; @UiField Button reply; @UiField Button expandAll; @@ -155,6 +149,7 @@ @UiField QuickApprove quickApprove; private ReplyAction replyAction; private EditMessageAction editMessageAction; + private RevisionsAction revisionsAction; private DownloadAction downloadAction; public ChangeScreen2(Change.Id changeId, String revision, boolean openReplyBox) { @@ -179,8 +174,10 @@ void loadChangeInfo(boolean fg, AsyncCallback<ChangeInfo> cb) { RestApi call = ChangeApi.detail(changeId.get()); ChangeList.addOptions(call, EnumSet.of( - ListChangesOption.ALL_REVISIONS, - ListChangesOption.CURRENT_ACTIONS)); + ListChangesOption.CURRENT_ACTIONS, + fg && revision != null + ? ListChangesOption.ALL_REVISIONS + : ListChangesOption.CURRENT_REVISION)); if (!fg) { call.background(); } @@ -247,7 +244,13 @@ } } - private void renderDownload(ChangeInfo info, String revision) { + private void initRevisionsAction(ChangeInfo info, String revision) { + revisionsAction = new RevisionsAction( + info.legacy_id(), revision, + style, headerLine, revisions); + } + + private void initDownloadAction(ChangeInfo info, String revision) { downloadAction = new DownloadAction( info.legacy_id(), info.project(), @@ -341,14 +344,9 @@ downloadAction.show(); } - @UiHandler("revisionList") - void onChangeRevision(ChangeEvent e) { - int idx = revisionList.getSelectedIndex(); - if (0 <= idx) { - String n = revisionList.getValue(idx); - revisionList.setEnabled(false); - Gerrit.display(PageLinks.toChange2(changeId, n)); - } + @UiHandler("revisions") + void onRevision(ClickEvent e) { + revisionsAction.show(); } @UiHandler("reply") @@ -597,9 +595,9 @@ renderOwner(info); renderReviewers(info); renderActionTextDate(info); - renderDownload(info, revision); - renderRevisions(info); renderHistory(info); + initRevisionsAction(info, revision); + initDownloadAction(info, revision); actions.display(info, revision); star.setValue(info.starred()); @@ -660,40 +658,13 @@ ccText.setInnerSafeHtml(labels.formatUserList(cc.values())); } - private void renderRevisions(ChangeInfo info) { - if (info.revisions().size() == 1) { - UIObject.setVisible(revisionParent, false); - return; - } - - JsArray<RevisionInfo> list = info.revisions().values(); - RevisionInfo.sortRevisionInfoByNumber(list); - if (Gerrit.isSignedIn() - && Gerrit.getUserAccount().getGeneralPreferences() - .isReversePatchSetOrder()) { - Collections.reverse(Natives.asList(list)); - } - - int selected = -1; - for (int i = 0; i < list.length(); i++) { - RevisionInfo r = list.get(i); - revisionList.addItem( - r._number() + ": " + r.name().substring(0, 6), - "" + r._number()); - if (revision.equals(r.name())) { - selected = i; - } - } - if (0 <= selected) { - revisionList.setSelectedIndex(selected); - } - } - private void renderOwner(ChangeInfo info) { // TODO info card hover - ownerText.setInnerText(info.owner().name() != null + String name = info.owner().name() != null ? info.owner().name() - : Gerrit.getConfig().getAnonymousCowardName()); + : Gerrit.getConfig().getAnonymousCowardName(); + ownerText.setInnerText(name); + ownerText.setTitle(name); } private void renderSubmitType(String action) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.ui.xml index 8e4b8f4..6cf7b7b 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.ui.xml
@@ -61,6 +61,10 @@ position: absolute; top: 0; left: 29px; + width: 245px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } .idStatus { position: absolute; @@ -97,6 +101,7 @@ margin: 0; padding-left: 2px; padding-right: 2px; + min-width: 100px; } .popdown button div { padding-left: 6px; @@ -256,14 +261,14 @@ </g:Button> </div> - <div class='{style.popdown}'> - <div ui:field='revisionParent' style='display: inline-block;'> - <ui:msg>Revision <g:ListBox ui:field='revisionList'/></ui:msg> - </div> + <g:FlowPanel styleName='{style.popdown}'> + <g:Button ui:field='revisions' styleName=''> + <div><ui:msg>Revisions</ui:msg></div> + </g:Button> <g:Button ui:field='download' styleName=''> <div><ui:msg>Download</ui:msg></div> </g:Button> - </div> + </g:FlowPanel> </g:HTMLPanel> <table class='{style.headerTable}'>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Constants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Constants.java index ce2fe7b..ff00e92 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Constants.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Constants.java
@@ -19,4 +19,9 @@ String nextChange(); String openChange(); String reviewedFileTitle(); + + String ps(); + String commit(); + String date(); + String author(); }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Constants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Constants.properties index 95f378c..bb9450f 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Constants.properties +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Constants.properties
@@ -2,3 +2,8 @@ nextChange = Next related change openChange = Open related change reviewedFileTitle = Mark file as reviewed (Shortcut: r) + +ps = PS +commit = Commit +date = Date +author = Author
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RevisionsAction.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RevisionsAction.java new file mode 100644 index 0000000..d66127b --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RevisionsAction.java
@@ -0,0 +1,37 @@ +// Copyright (C) 2013 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.client.change; + +import com.google.gerrit.reviewdb.client.Change; +import com.google.gwt.user.client.ui.UIObject; +import com.google.gwt.user.client.ui.Widget; + +class RevisionsAction extends RightSidePopdownAction { + private final RevisionsBox revisionBox; + + RevisionsAction( + Change.Id changeId, + String revision, + ChangeScreen2.Style style, + UIObject relativeTo, + Widget downloadButton) { + super(style, relativeTo, downloadButton); + this.revisionBox = new RevisionsBox(changeId, revision); + } + + Widget getWidget() { + return revisionBox; + } +}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RevisionsBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RevisionsBox.java new file mode 100644 index 0000000..c0b2112 --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RevisionsBox.java
@@ -0,0 +1,219 @@ +// Copyright (C) 2013 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.client.change; + +import com.google.gerrit.client.FormatUtil; +import com.google.gerrit.client.Gerrit; +import com.google.gerrit.client.changes.ChangeApi; +import com.google.gerrit.client.changes.ChangeInfo; +import com.google.gerrit.client.changes.ChangeInfo.CommitInfo; +import com.google.gerrit.client.changes.ChangeInfo.RevisionInfo; +import com.google.gerrit.client.changes.ChangeList; +import com.google.gerrit.client.rpc.NativeMap; +import com.google.gerrit.client.rpc.Natives; +import com.google.gerrit.client.rpc.RestApi; +import com.google.gerrit.client.ui.FancyFlexTableImpl; +import com.google.gerrit.common.PageLinks; +import com.google.gerrit.common.changes.ListChangesOption; +import com.google.gerrit.reviewdb.client.Change; +import com.google.gwt.core.client.GWT; +import com.google.gwt.core.client.JsArray; +import com.google.gwt.dom.client.NativeEvent; +import com.google.gwt.resources.client.CssResource; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.EventListener; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.FlexTable; +import com.google.gwt.user.client.ui.HTMLPanel; +import com.google.gwt.user.client.ui.PopupPanel; +import com.google.gwt.user.client.ui.Widget; +import com.google.gwt.user.client.ui.impl.HyperlinkImpl; +import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder; + +import java.util.Collections; +import java.util.EnumSet; + +class RevisionsBox extends Composite { + interface Binder extends UiBinder<HTMLPanel, RevisionsBox> {} + private static final Binder uiBinder = GWT.create(Binder.class); + + private static final String OPEN; + private static final HyperlinkImpl link = GWT.create(HyperlinkImpl.class); + + static { + OPEN = DOM.createUniqueId().replace('-', '_'); + init(OPEN); + } + + private static final native void init(String o) /*-{ + $wnd[o] = $entry(function(e,i) { + return @com.google.gerrit.client.change.RevisionsBox::onOpen(Lcom/google/gwt/dom/client/NativeEvent;I)(e,i); + }); + }-*/; + + private static boolean onOpen(NativeEvent e, int idx) { + if (link.handleAsClick(e.<Event> cast())) { + RevisionsBox t = getRevisionBox(e); + if (t != null) { + t.onOpenRow(idx); + e.preventDefault(); + return false; + } + } + return true; + } + + private static RevisionsBox getRevisionBox(NativeEvent event) { + com.google.gwt.user.client.Element e = event.getEventTarget().cast(); + for (e = DOM.getParent(e); e != null; e = DOM.getParent(e)) { + EventListener l = DOM.getEventListener(e); + if (l instanceof RevisionsBox) { + return (RevisionsBox) l; + } + } + return null; + } + + interface Style extends CssResource { + String current(); + String legacy_id(); + String commit(); + } + + private final Change.Id changeId; + private final String revision; + private boolean loaded; + private JsArray<RevisionInfo> revisions; + + @UiField FlexTable table; + @UiField Style style; + + RevisionsBox(Change.Id changeId, String revision) { + this.changeId = changeId; + this.revision = revision; + initWidget(uiBinder.createAndBindUi(this)); + } + + @Override + protected void onLoad() { + if (!loaded) { + RestApi call = ChangeApi.detail(changeId.get()); + ChangeList.addOptions(call, EnumSet.of( + ListChangesOption.ALL_REVISIONS, + ListChangesOption.ALL_COMMITS)); + call.get(new AsyncCallback<ChangeInfo>() { + @Override + public void onSuccess(ChangeInfo result) { + render(result.revisions()); + loaded = true; + } + + @Override + public void onFailure(Throwable caught) { + } + }); + } + } + + private void onOpenRow(int idx) { + closeParent(); + Gerrit.display(url(revisions.get(idx))); + } + + private void render(NativeMap<RevisionInfo> map) { + map.copyKeysIntoChildren("name"); + + revisions = map.values(); + RevisionInfo.sortRevisionInfoByNumber(revisions); + Collections.reverse(Natives.asList(revisions)); + + SafeHtmlBuilder sb = new SafeHtmlBuilder(); + header(sb); + for (int i = 0; i < revisions.length(); i++) { + revision(sb, i, revisions.get(i)); + } + + GWT.<FancyFlexTableImpl> create(FancyFlexTableImpl.class) + .resetHtml(table, sb); + } + + private void header(SafeHtmlBuilder sb) { + sb.openTr() + .openTh() + .setStyleName(style.legacy_id()) + .append(Resources.C.ps()) + .closeTh() + .openTh().append(Resources.C.commit()).closeTh() + .openTh().append(Resources.C.date()).closeTh() + .openTh().append(Resources.C.author()).closeTh() + .closeTr(); + } + + private void revision(SafeHtmlBuilder sb, int index, RevisionInfo r) { + CommitInfo c = r.commit(); + sb.openTr(); + if (revision.equals(r.name())) { + sb.setStyleName(style.current()); + } + + sb.openTd() + .setStyleName(style.legacy_id()) + .append(r._number()) + .closeTd(); + + sb.openTd() + .setStyleName(style.commit()) + .openAnchor() + .setAttribute("href", "#" + url(r)) + .setAttribute("onclick", OPEN + "(event," + index + ")") + .append(r.name().substring(0, 10)) + .closeAnchor() + .closeTd(); + + sb.openTd() + .append(FormatUtil.shortFormatDayTime(c.committer().date())) + .closeTd(); + + String an = c.author() != null ? c.author().name() : null; + String cn = c.committer() != null ? c.committer().name() : null; + sb.openTd(); + sb.append(an); + if (!"".equals(an) && !"".equals(cn) && !an.equals(cn)) { + sb.append(" / ").append(cn); + } + sb.closeTd(); + + sb.closeTr(); + } + + private String url(RevisionInfo r) { + return PageLinks.toChange2( + changeId, + String.valueOf(r._number())); + } + + private void closeParent() { + for (Widget w = getParent(); w != null; w = w.getParent()) { + if (w instanceof PopupPanel) { + ((PopupPanel) w).hide(true); + break; + } + } + } +}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RevisionsBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RevisionsBox.ui.xml new file mode 100644 index 0000000..bf1ddd4 --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/RevisionsBox.ui.xml
@@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +Copyright (C) 2013 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. +--> +<ui:UiBinder + xmlns:ui='urn:ui:com.google.gwt.uibinder' + xmlns:g='urn:import:com.google.gwt.user.client.ui'> + <ui:with field='res' type='com.google.gerrit.client.change.Resources'/> + <ui:style type='com.google.gerrit.client.change.RevisionsBox.Style'> + @eval selectionColor com.google.gerrit.client.Gerrit.getTheme().selectionColor; + + .revisionBox { + min-width: 300px; + margin: 10px 0px 5px 5px; + } + + .scroll { + min-width: 300px; + height: 200px; + } + + .table { + border-spacing: 0; + width: 100%; + } + + .table td, .table th { + padding-left: 5px; + padding-right: 5px; + border-right: 2px solid #ddd; + white-space: nowrap; + } + + .table tr.current { + background-color: selectionColor; + } + + .legacy_id { + min-width: 50px; + text-align: right; + font-weight: bold; + } + + .commit { + font-family: monospace; + } + </ui:style> + <g:HTMLPanel styleName='{style.revisionBox}'> + <g:ScrollPanel styleName='{style.scroll}'> + <g:FlexTable ui:field='table' styleName='{style.table}'/> + </g:ScrollPanel> + </g:HTMLPanel> +</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImpl.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImpl.java index 2c7451a..2836e0f 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImpl.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImpl.java
@@ -14,13 +14,13 @@ package com.google.gerrit.client.ui; -import com.google.gerrit.client.ui.FancyFlexTable.MyFlexTable; import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.ui.FlexTable; import com.google.gwt.user.client.ui.HTMLTable; import com.google.gwtexpui.safehtml.client.SafeHtml; public class FancyFlexTableImpl { - public void resetHtml(final MyFlexTable myTable, final SafeHtml body) { + public void resetHtml(final FlexTable myTable, final SafeHtml body) { SafeHtml.setInnerHTML(getBodyElement(myTable), body); }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImplIE6.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImplIE6.java index 17e8ddd..34b6bee 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImplIE6.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImplIE6.java
@@ -14,16 +14,16 @@ package com.google.gerrit.client.ui; -import com.google.gerrit.client.ui.FancyFlexTable.MyFlexTable; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.ui.FlexTable; import com.google.gwt.user.client.ui.HTMLTable; import com.google.gwtexpui.safehtml.client.SafeHtml; import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder; public class FancyFlexTableImplIE6 extends FancyFlexTableImpl { @Override - public void resetHtml(final MyFlexTable myTable, final SafeHtml bodyHtml) { + public void resetHtml(final FlexTable myTable, final SafeHtml bodyHtml) { final Element oldBody = getBodyElement(myTable); final Element newBody = parseBody(bodyHtml); assert newBody != null;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java index 208d282..677a615 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java
@@ -106,6 +106,7 @@ case OPENID_SSO: break; } + config.setSwitchAccountUrl(cfg.getString("auth", null, "switchAccountUrl")); config.setUseContributorAgreements(cfg.getBoolean("auth", "contributoragreements", false)); config.setGitDaemonUrl(cfg.getString("gerrit", null, "canonicalgiturl"));
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java index 7ba65d3..d8a2648 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
@@ -46,8 +46,6 @@ import com.google.common.net.HttpHeaders; import com.google.gerrit.audit.AuditService; import com.google.gerrit.audit.HttpAuditEvent; -import com.google.gerrit.extensions.annotations.CapabilityScope; -import com.google.gerrit.extensions.annotations.RequiresCapability; import com.google.gerrit.extensions.registration.DynamicMap; import com.google.gerrit.extensions.restapi.AcceptsCreate; import com.google.gerrit.extensions.restapi.AcceptsPost; @@ -76,7 +74,7 @@ import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.OptionUtil; import com.google.gerrit.server.OutputFormat; -import com.google.gerrit.server.account.CapabilityControl; +import com.google.gerrit.server.account.CapabilityUtils; import com.google.gson.ExclusionStrategy; import com.google.gson.FieldAttributes; import com.google.gson.FieldNamingPolicy; @@ -200,7 +198,8 @@ List<IdString> path = splitPath(req); RestCollection<RestResource, RestResource> rc = members.get(); - checkAccessAnnotations(null, rc.getClass()); + CapabilityUtils.checkRequiresCapability(globals.currentUser, + null, rc.getClass()); RestResource rsrc = TopLevelResource.INSTANCE; ViewData viewData = new ViewData(null, null); @@ -238,7 +237,7 @@ viewData = view(rc, req.getMethod(), path); } } - checkAccessAnnotations(viewData); + checkRequiresCapability(viewData); while (viewData.view instanceof RestCollection<?,?>) { @SuppressWarnings("unchecked") @@ -279,7 +278,7 @@ viewData = view(c, req.getMethod(), path); } } - checkAccessAnnotations(viewData); + checkRequiresCapability(viewData); } if (notModified(req, rsrc)) { @@ -870,37 +869,9 @@ return !("GET".equals(method) || "HEAD".equals(method)); } - private void checkAccessAnnotations(ViewData viewData) throws AuthException { - checkAccessAnnotations(viewData.pluginName, viewData.view.getClass()); - } - - private void checkAccessAnnotations(String pluginName, Class<?> clazz) - throws AuthException { - RequiresCapability rc = clazz.getAnnotation(RequiresCapability.class); - if (rc != null) { - CurrentUser user = globals.currentUser.get(); - CapabilityControl ctl = user.getCapabilities(); - String capability = rc.value(); - - if (pluginName != null && !"gerrit".equals(pluginName) - && (rc.scope() == CapabilityScope.PLUGIN - || rc.scope() == CapabilityScope.CONTEXT)) { - capability = String.format("%s-%s", pluginName, rc.value()); - } else if (rc.scope() == CapabilityScope.PLUGIN) { - log.error(String.format( - "Class %s uses @%s(scope=%s), but is not within a plugin", - clazz.getName(), - RequiresCapability.class.getSimpleName(), - CapabilityScope.PLUGIN.name())); - throw new AuthException("cannot check capability"); - } - - if (!ctl.canPerform(capability) && !ctl.canAdministrateServer()) { - throw new AuthException(String.format( - "Capability %s is required to access this resource", - capability)); - } - } + private void checkRequiresCapability(ViewData viewData) throws AuthException { + CapabilityUtils.checkRequiresCapability(globals.currentUser, + viewData.pluginName, viewData.view.getClass()); } private static void handleException(Throwable err, HttpServletRequest req,
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetDetailFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetDetailFactory.java index 461a263..35af351 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetDetailFactory.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetDetailFactory.java
@@ -48,6 +48,7 @@ import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; +import com.google.inject.util.Providers; import org.eclipse.jgit.lib.ObjectId; import org.slf4j.Logger; @@ -179,7 +180,8 @@ detail.setCommands(Lists.newArrayList(Iterables.transform( UiActions.sorted(UiActions.plugins(UiActions.from( revisions, - new RevisionResource(new ChangeResource(control), patchSet)))), + new RevisionResource(new ChangeResource(control), patchSet), + Providers.of(user)))), new Function<UiAction.Description, UiCommandDetail>() { @Override public UiCommandDetail apply(UiAction.Description in) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/CapabilityUtils.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/CapabilityUtils.java new file mode 100644 index 0000000..6b68032 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/CapabilityUtils.java
@@ -0,0 +1,85 @@ +// Copyright (C) 2013 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.account; + +import com.google.gerrit.extensions.annotations.CapabilityScope; +import com.google.gerrit.extensions.annotations.RequiresCapability; +import com.google.gerrit.extensions.restapi.AuthException; +import com.google.gerrit.server.CurrentUser; +import com.google.gerrit.server.account.CapabilityControl; +import com.google.inject.Provider; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.annotation.Annotation; + +public class CapabilityUtils { + private static final Logger log = LoggerFactory + .getLogger(CapabilityUtils.class); + + public static void checkRequiresCapability(Provider<CurrentUser> userProvider, + String pluginName, Class<?> clazz) + throws AuthException { + RequiresCapability rc = getClassAnnotation(clazz, RequiresCapability.class); + if (rc != null) { + CurrentUser user = userProvider.get(); + CapabilityControl ctl = user.getCapabilities(); + if (ctl.canAdministrateServer()) { + return; + } + + String capability = rc.value(); + if (pluginName != null && !"gerrit".equals(pluginName) + && (rc.scope() == CapabilityScope.PLUGIN + || rc.scope() == CapabilityScope.CONTEXT)) { + capability = String.format("%s-%s", pluginName, rc.value()); + } else if (rc.scope() == CapabilityScope.PLUGIN) { + log.error(String.format( + "Class %s uses @%s(scope=%s), but is not within a plugin", + clazz.getName(), + RequiresCapability.class.getSimpleName(), + CapabilityScope.PLUGIN.name())); + throw new AuthException("cannot check capability"); + } + + if (!ctl.canPerform(capability)) { + throw new AuthException(String.format( + "Capability %s is required to access this resource", + capability)); + } + } + } + + /** + * Find an instance of the specified annotation, walking up the inheritance + * tree if necessary. + * + * @param <T> Annotation type to search for + * @param clazz root class to search, may be null + * @param annotationClass class object of Annotation subclass to search for + * @return the requested annotation or null if none + */ + private static <T extends Annotation> T getClassAnnotation(Class<?> clazz, + Class<T> annotationClass) { + for (; clazz != null; clazz = clazz.getSuperclass()) { + T t = clazz.getAnnotation(annotationClass); + if (t != null) { + return t; + } + } + return null; + } +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java index 04c1882..663519f 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
@@ -124,7 +124,7 @@ private final Provider<ReviewDb> db; private final LabelNormalizer labelNormalizer; - private final CurrentUser user; + private final Provider<CurrentUser> user; private final AnonymousUser anonymous; private final IdentifiedUser.GenericFactory userFactory; private final ChangeControl.GenericFactory changeControlGenericFactory; @@ -145,7 +145,7 @@ ChangeJson( Provider<ReviewDb> db, LabelNormalizer ln, - CurrentUser u, + Provider<CurrentUser> userProvider, AnonymousUser au, IdentifiedUser.GenericFactory uf, ChangeControl.GenericFactory ccf, @@ -158,7 +158,7 @@ Revisions revisions) { this.db = db; this.labelNormalizer = ln; - this.user = u; + this.user = userProvider; this.anonymous = au; this.userFactory = uf; this.changeControlGenericFactory = ccf; @@ -257,7 +257,9 @@ out.updated = in.getLastUpdatedOn(); out._number = in.getId().get(); out._sortkey = in.getSortKey(); - out.starred = user.getStarredChanges().contains(in.getId()) ? true : null; + out.starred = user.get().getStarredChanges().contains(in.getId()) + ? true + : null; out.reviewed = in.getStatus().isOpen() && isChangeReviewed(cd) ? true : null; out.labels = labelsFor(cd, has(LABELS), has(DETAILED_LABELS)); @@ -291,7 +293,8 @@ out.actions = Maps.newTreeMap(); for (UiAction.Description d : UiActions.from( changes, - new ChangeResource(control(cd)))) { + new ChangeResource(control(cd)), + user)) { out.actions.put(d.getId(), new ActionInfo(d)); } } @@ -312,7 +315,8 @@ if (changeControlUserFactory != null) { ctrl = changeControlUserFactory.controlFor(cd.change(db)); } else { - ctrl = changeControlGenericFactory.controlFor(cd.change(db), user); + ctrl = changeControlGenericFactory.controlFor(cd.change(db), + user.get()); } } catch (NoSuchChangeException e) { return null; @@ -777,7 +781,8 @@ out.actions = Maps.newTreeMap(); for (UiAction.Description d : UiActions.from( revisions, - new RevisionResource(new ChangeResource(control(cd)), in))) { + new RevisionResource(new ChangeResource(control(cd)), in), + user)) { out.actions.put(d.getId(), new ActionInfo(d)); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/extensions/webui/UiActions.java b/gerrit-server/src/main/java/com/google/gerrit/server/extensions/webui/UiActions.java index 9aae367..fa64f4a 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/extensions/webui/UiActions.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/extensions/webui/UiActions.java
@@ -20,11 +20,15 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.gerrit.extensions.registration.DynamicMap; +import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.RestCollection; import com.google.gerrit.extensions.restapi.RestResource; import com.google.gerrit.extensions.restapi.RestView; import com.google.gerrit.extensions.webui.PrivateInternals_UiActionDescription; import com.google.gerrit.extensions.webui.UiAction; +import com.google.gerrit.server.CurrentUser; +import com.google.gerrit.server.account.CapabilityUtils; +import com.google.inject.Provider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -70,13 +74,15 @@ public static <R extends RestResource> Iterable<UiAction.Description> from( RestCollection<?, R> collection, - R resource) { - return from(collection.views(), resource); + R resource, + Provider<CurrentUser> userProvider) { + return from(collection.views(), resource, userProvider); } public static <R extends RestResource> Iterable<UiAction.Description> from( DynamicMap<RestView<R>> views, - final R resource) { + final R resource, + final Provider<CurrentUser> userProvider) { return Iterables.filter( Iterables.transform( views, @@ -103,6 +109,13 @@ return null; } + try { + CapabilityUtils.checkRequiresCapability(userProvider, + e.getPluginName(), view.getClass()); + } catch (AuthException exc) { + return null; + } + UiAction.Description dsc = ((UiAction<R>) view).getDescription(resource); if (dsc == null || !dsc.isVisible()) {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DispatchCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DispatchCommand.java index 0fa5d25..fa5ab53 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DispatchCommand.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DispatchCommand.java
@@ -17,10 +17,9 @@ import com.google.common.base.Strings; import com.google.common.collect.Sets; import com.google.common.util.concurrent.Atomics; -import com.google.gerrit.extensions.annotations.CapabilityScope; -import com.google.gerrit.extensions.annotations.RequiresCapability; +import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.server.CurrentUser; -import com.google.gerrit.server.account.CapabilityControl; +import com.google.gerrit.server.account.CapabilityUtils; import com.google.gerrit.server.args4j.SubcommandHandler; import com.google.inject.Inject; import com.google.inject.Provider; @@ -29,8 +28,6 @@ import org.apache.sshd.server.Command; import org.apache.sshd.server.Environment; import org.kohsuke.args4j.Argument; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.StringWriter; @@ -43,9 +40,6 @@ * Command that dispatches to a subcommand from its command table. */ final class DispatchCommand extends BaseCommand { - private static final Logger log = LoggerFactory - .getLogger(DispatchCommand.class); - interface Factory { DispatchCommand create(Map<String, CommandProvider> map); } @@ -121,36 +115,16 @@ private void checkRequiresCapability(Command cmd) throws UnloggedFailure { - RequiresCapability rc = cmd.getClass().getAnnotation(RequiresCapability.class); - if (rc != null) { - CurrentUser user = currentUser.get(); - CapabilityControl ctl = user.getCapabilities(); - String capability = rc.value(); - - if (cmd instanceof BaseCommand) { - String pluginName = ((BaseCommand) cmd).getPluginName(); - if (pluginName != null && !"gerrit".equals(pluginName) - && (rc.scope() == CapabilityScope.PLUGIN - || rc.scope() == CapabilityScope.CONTEXT)) { - capability = String.format("%s-%s", pluginName, rc.value()); - } else if (rc.scope() == CapabilityScope.PLUGIN) { - log.error(String.format( - "Class %s uses @%s(scope=%s), but is not within a plugin", - cmd.getClass().getName(), - RequiresCapability.class.getSimpleName(), - CapabilityScope.PLUGIN.name())); - throw new UnloggedFailure( - BaseCommand.STATUS_NOT_ADMIN, - "fatal: cannot check capability"); - } - } - - if (!ctl.canPerform(capability) && !ctl.canAdministrateServer()) { - String msg = String.format( - "fatal: %s does not have \"%s\" capability.", - user.getUserName(), capability); - throw new UnloggedFailure(BaseCommand.STATUS_NOT_ADMIN, msg); - } + String pluginName = null; + if (cmd instanceof BaseCommand) { + pluginName = ((BaseCommand) cmd).getPluginName(); + } + try { + CapabilityUtils.checkRequiresCapability(currentUser, + pluginName, cmd.getClass()); + } catch (AuthException e) { + throw new UnloggedFailure(BaseCommand.STATUS_NOT_ADMIN, + e.getMessage()); } }
diff --git a/lib/commons/BUCK b/lib/commons/BUCK index 6f412e4..fd066ef 100644 --- a/lib/commons/BUCK +++ b/lib/commons/BUCK
@@ -73,6 +73,7 @@ id = 'commons-io:commons-io:1.4', sha1 = 'a8762d07e76cfde2395257a5da47ba7c1dbd3dce', license = 'Apache2.0', + exclude = ['META-INF/MANIFEST.MF'], ) maven_jar(