diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index c034cf0429..98ea303b2a 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -420,6 +420,43 @@ Deletes the HTTP password of an account.
HTTP/1.1 204 No Content
----
+[[get-oauth-token]]
+=== Get OAuth Access Token
+--
+'GET /accounts/link:#account-id[\{account-id\}]/oauthtoken'
+--
+
+Returns a previously obtained OAuth access token.
+
+.Request
+----
+ GET /accounts/self/oauthtoken HTTP/1.1
+----
+
+As a response, an link:#oauth-token-info[OAuthTokenInfo] entity is returned
+that describes the OAuth access token.
+
+.Response
+----
+ HTTP/1.1 200 OK
+ Content-Disposition: attachment
+ Content-Type: application/json; charset=UTF-8
+
+ )]}'
+ {
+ "username": "johndow",
+ "resource_host": "gerrit.example.org",
+ "access_token": "eyJhbGciOiJSUzI1NiJ9.eyJqdGkiOi",
+ "provider_id": "oauth-plugin:oauth-provider",
+ "expires_at": "922337203775807",
+ "type": "bearer"
+ }
+----
+
+If there is no token available, or the token has already expired,
+"`404 Not Found`" is returned as response. Requests to obtain an access
+token of another user are rejected with "`403 Forbidden`".
+
[[list-account-emails]]
=== List Account Emails
--
@@ -2214,6 +2251,22 @@ If empty or not set and `generate` is false or not set, the HTTP
password is deleted.
|============================
+[[oauth-token-info]]
+=== OAuthTokenInfo
+The `OAuthTokenInfo` entity contains information about an OAuth access token.
+
+[options="header",cols="1,^1,5"]
+|========================
+|Field Name ||Description
+|`username` ||The owner of the OAuth access token.
+|`resource_host` ||The host of the Gerrit instance.
+|`access_token` ||The actual token value.
+|`provider_id` |optional|
+The identifier of the OAuth provider in the form `plugin-name:provider-name`.
+|`expires_at` |optional|Time of expiration of this token in milliseconds.
+|`type` ||The type of the OAuth access token, always `bearer`.
+|========================
+
[[preferences-info]]
=== PreferencesInfo
The `PreferencesInfo` entity contains information about a user's preferences.
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java b/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java
index 3957a30bf2..ca3e6eab5b 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java
@@ -29,6 +29,7 @@ public class PageLinks {
public static final String SETTINGS_SSHKEYS = "/settings/ssh-keys";
public static final String SETTINGS_GPGKEYS = "/settings/gpg-keys";
public static final String SETTINGS_HTTP_PASSWORD = "/settings/http-password";
+ public static final String SETTINGS_OAUTH_TOKEN = "/settings/oauth-token";
public static final String SETTINGS_WEBIDENT = "/settings/web-identities";
public static final String SETTINGS_MYGROUPS = "/settings/group-memberships";
public static final String SETTINGS_AGREEMENTS = "/settings/agreements";
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/auth/oauth/OAuthToken.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/auth/oauth/OAuthToken.java
index 99b2cfa8ea..788f4201ce 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/auth/oauth/OAuthToken.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/auth/oauth/OAuthToken.java
@@ -31,16 +31,23 @@ public class OAuthToken implements Serializable {
*/
private final long expiresAt;
+ /**
+ * The identifier of the OAuth provider that issued this token
+ * in the form "plugin-name:provider-name", or {@code null}.
+ */
+ private final String providerId;
+
public OAuthToken(String token, String secret, String raw) {
- this(token, secret, raw, Long.MAX_VALUE);
+ this(token, secret, raw, Long.MAX_VALUE, null);
}
public OAuthToken(String token, String secret, String raw,
- long expiresAt) {
+ long expiresAt, String providerId) {
this.token = token;
this.secret = secret;
this.raw = raw;
this.expiresAt = expiresAt;
+ this.providerId = providerId;
}
public String getToken() {
@@ -62,4 +69,8 @@ public class OAuthToken implements Serializable {
public boolean isExpired() {
return System.currentTimeMillis() > expiresAt;
}
+
+ public String getProviderId() {
+ return providerId;
+ }
}
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/OAuthTokenInfo.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/OAuthTokenInfo.java
new file mode 100644
index 0000000000..08fd130286
--- /dev/null
+++ b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/OAuthTokenInfo.java
@@ -0,0 +1,31 @@
+// Copyright (C) 2016 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.info;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+
+public class OAuthTokenInfo extends JavaScriptObject {
+
+ protected OAuthTokenInfo() {
+ }
+
+ public final native String username() /*-{ return this.username; }-*/;
+ public final native String resourceHost() /*-{ return this.resource_host; }-*/;
+ public final native String accessToken() /*-{ return this.access_token; }-*/;
+ public final native String providerId() /*-{ return this.provider_id; }-*/;
+ public final native String expiresAt() /*-{ return this.expires_at; }-*/;
+ public final native String type() /*-{ return this.type; }-*/;
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
index 57e79d11ad..ad3489a133 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
@@ -33,6 +33,7 @@ import static com.google.gerrit.common.PageLinks.SETTINGS_EDIT_PREFERENCES;
import static com.google.gerrit.common.PageLinks.SETTINGS_EXTENSION;
import static com.google.gerrit.common.PageLinks.SETTINGS_GPGKEYS;
import static com.google.gerrit.common.PageLinks.SETTINGS_HTTP_PASSWORD;
+import static com.google.gerrit.common.PageLinks.SETTINGS_OAUTH_TOKEN;
import static com.google.gerrit.common.PageLinks.SETTINGS_MYGROUPS;
import static com.google.gerrit.common.PageLinks.SETTINGS_NEW_AGREEMENT;
import static com.google.gerrit.common.PageLinks.SETTINGS_PREFERENCES;
@@ -48,6 +49,7 @@ import com.google.gerrit.client.account.MyEditPreferencesScreen;
import com.google.gerrit.client.account.MyGpgKeysScreen;
import com.google.gerrit.client.account.MyGroupsScreen;
import com.google.gerrit.client.account.MyIdentitiesScreen;
+import com.google.gerrit.client.account.MyOAuthTokenScreen;
import com.google.gerrit.client.account.MyPasswordScreen;
import com.google.gerrit.client.account.MyPreferencesScreen;
import com.google.gerrit.client.account.MyProfileScreen;
@@ -568,6 +570,12 @@ public class Dispatcher {
return new MyPasswordScreen();
}
+ if (matchExact(SETTINGS_OAUTH_TOKEN, token)
+ && Gerrit.info().auth().isOAuth()
+ && Gerrit.info().auth().isGitBasicAuth()) {
+ return new MyOAuthTokenScreen();
+ }
+
if (matchExact(MY_GROUPS, token)
|| matchExact(SETTINGS_MYGROUPS, token)) {
return new MyGroupsScreen();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java
index 980f27d29b..32e30d4d42 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java
@@ -105,6 +105,14 @@ public interface GerritCss extends CssResource {
String menuScreenMenuBar();
String needsReview();
String negscore();
+ String oauthExpires();
+ String oauthInfoBlock();
+ String oauthPanel();
+ String oauthPanelCookieEntry();
+ String oauthPanelCookieHeading();
+ String oauthPanelNetRCEntry();
+ String oauthPanelNetRCHeading();
+ String oauthToken();
String pagingLink();
String patchSetActions();
String pluginProjectConfigInheritedValue();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.java
index 549923c59f..a084612ff0 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.java
@@ -57,6 +57,7 @@ public interface AccountConstants extends Constants {
String tabGpgKeys();
String tabHttpAccess();
String tabMyGroups();
+ String tabOAuthToken();
String tabPreferences();
String tabSshKeys();
String tabWatchedProjects();
@@ -81,6 +82,12 @@ public interface AccountConstants extends Constants {
String invalidUserName();
String invalidUserEmail();
+ String labelOAuthToken();
+ String labelOAuthExpires();
+ String labelOAuthNetRCEntry();
+ String labelOAuthGitCookie();
+ String labelOAuthExpired();
+
String sshKeyInvalid();
String sshKeyAlgorithm();
String sshKeyKey();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties
index 0ed262066c..8cd8dc7cb8 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties
@@ -44,6 +44,7 @@ tabDiffPreferences = Diff Preferences
tabEditPreferences = Edit Preferences
tabGpgKeys = GPG Public Keys
tabHttpAccess = HTTP Password
+tabOAuthToken = OAuth Token
tabMyGroups = Groups
tabPreferences = Preferences
tabSshKeys = SSH Public Keys
@@ -68,6 +69,13 @@ linkEditFullName = Edit
linkReloadContact = Reload
invalidUserName = Username must contain only letters, numbers, _, - or .
invalidUserEmail = Email format is wrong.
+
+labelOAuthToken = Access Token
+labelOAuthExpires = Expires
+labelOAuthNetRCEntry = Entry for ~/.netrc
+labelOAuthGitCookie = Entry for ~/.gitcookies
+labelOAuthExpired = To obtain an access token please sign out and sign in again.
+
sshKeyInvalid = Invalid Key
sshKeyAlgorithm = Algorithm
sshKeyKey = Key
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyOAuthTokenScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyOAuthTokenScreen.java
new file mode 100644
index 0000000000..a4c92fee17
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyOAuthTokenScreen.java
@@ -0,0 +1,197 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.client.account;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.info.GeneralPreferences;
+import com.google.gerrit.client.info.OAuthTokenInfo;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.rpc.ScreenLoadCallback;
+import com.google.gwt.i18n.client.DateTimeFormat;
+import com.google.gwt.i18n.client.LocaleInfo;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.Widget;
+import com.google.gwtexpui.clippy.client.CopyableLabel;
+
+import java.util.Date;
+
+public class MyOAuthTokenScreen extends SettingsScreen {
+ private CopyableLabel tokenLabel;
+ private Label expiresLabel;
+ private Label expiredNote;
+ private CopyableLabel netrcValue;
+ private CopyableLabel cookieValue;
+ private FlowPanel flow;
+ private Grid grid;
+
+ @Override
+ protected void onInitUI() {
+ super.onInitUI();
+
+ tokenLabel = new CopyableLabel("");
+ tokenLabel.addStyleName(Gerrit.RESOURCES.css().oauthToken());
+
+ expiresLabel = new Label("");
+ expiresLabel.addStyleName(Gerrit.RESOURCES.css().oauthExpires());
+
+ grid = new Grid(2, 2);
+ grid.setStyleName(Gerrit.RESOURCES.css().infoBlock());
+ grid.addStyleName(Gerrit.RESOURCES.css().oauthInfoBlock());
+ add(grid);
+
+ expiredNote = new Label(Util.C.labelOAuthExpired());
+ expiredNote.setVisible(false);
+ add(expiredNote);
+
+ row(grid, 0, Util.C.labelOAuthToken(), tokenLabel);
+ row(grid, 1, Util.C.labelOAuthExpires(), expiresLabel);
+
+ CellFormatter fmt = grid.getCellFormatter();
+ fmt.addStyleName(0, 0, Gerrit.RESOURCES.css().topmost());
+ fmt.addStyleName(0, 1, Gerrit.RESOURCES.css().topmost());
+ fmt.addStyleName(1, 0, Gerrit.RESOURCES.css().bottomheader());
+
+ flow = new FlowPanel();
+ flow.setStyleName(Gerrit.RESOURCES.css().oauthPanel());
+ add(flow);
+
+ Label netrcLabel = new Label(Util.C.labelOAuthNetRCEntry());
+ netrcLabel.setStyleName(Gerrit.RESOURCES.css().oauthPanelNetRCHeading());
+ flow.add(netrcLabel);
+ netrcValue= new CopyableLabel("");
+ netrcValue.setStyleName(Gerrit.RESOURCES.css().oauthPanelNetRCEntry());
+ flow.add(netrcValue);
+
+ Label cookieLabel = new Label(Util.C.labelOAuthGitCookie());
+ cookieLabel.setStyleName(Gerrit.RESOURCES.css().oauthPanelCookieHeading());
+ flow.add(cookieLabel);
+ cookieValue = new CopyableLabel("");
+ cookieValue.setStyleName(Gerrit.RESOURCES.css().oauthPanelCookieEntry());
+ flow.add(cookieValue);
+ }
+
+ private void row(Grid grid, int row, String name, Widget field) {
+ final CellFormatter fmt = grid.getCellFormatter();
+ if (LocaleInfo.getCurrentLocale().isRTL()) {
+ grid.setText(row, 1, name);
+ grid.setWidget(row, 0, field);
+ fmt.addStyleName(row, 1, Gerrit.RESOURCES.css().header());
+ } else {
+ grid.setText(row, 0, name);
+ grid.setWidget(row, 1, field);
+ fmt.addStyleName(row, 0, Gerrit.RESOURCES.css().header());
+ }
+ }
+
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+ AccountApi.self().view("preferences")
+ .get(new ScreenLoadCallback(this) {
+ @Override
+ protected void preDisplay(GeneralPreferences prefs) {
+ display(prefs);
+ }
+ });
+ }
+
+ private void display(final GeneralPreferences prefs) {
+ AccountApi.self().view("oauthtoken")
+ .get(new GerritCallback() {
+ @Override
+ public void onSuccess(OAuthTokenInfo tokenInfo) {
+ tokenLabel.setText(tokenInfo.accessToken());
+ expiresLabel.setText(getExpiresAt(tokenInfo, prefs));
+ netrcValue.setText(getNetRC(tokenInfo));
+ cookieValue.setText(getCookie(tokenInfo));
+ flow.setVisible(true);
+ expiredNote.setVisible(false);
+ }
+ @Override
+ public void onFailure(Throwable caught) {
+ if (isNoSuchEntity(caught) || isSigninFailure(caught)) {
+ tokenLabel.setText("");
+ expiresLabel.setText("");
+ netrcValue.setText("");
+ cookieValue.setText("");
+ flow.setVisible(false);
+ expiredNote.setVisible(true);
+ } else {
+ showFailure(caught);
+ }
+ }
+ });
+ }
+
+ private static long getExpiresAt(OAuthTokenInfo tokenInfo) {
+ if (tokenInfo.expiresAt() == null) {
+ return Long.MAX_VALUE;
+ }
+ long expiresAt;
+ try {
+ expiresAt = Long.parseLong(tokenInfo.expiresAt());
+ } catch (NumberFormatException e) {
+ return Long.MAX_VALUE;
+ }
+ return expiresAt;
+ }
+
+ private static long getExpiresAtSeconds(OAuthTokenInfo tokenInfo) {
+ return getExpiresAt(tokenInfo) / 1000L;
+ }
+
+ private static String getExpiresAt(OAuthTokenInfo tokenInfo,
+ GeneralPreferences prefs) {
+ long expiresAt = getExpiresAt(tokenInfo);
+ if (expiresAt == Long.MAX_VALUE) {
+ return "";
+ }
+ String dateFormat = prefs.dateFormat().getLongFormat();
+ String timeFormat = prefs.timeFormat().getFormat();
+ DateTimeFormat formatter = DateTimeFormat.getFormat(
+ dateFormat + " " + timeFormat);
+ return formatter.format(new Date(expiresAt));
+ }
+
+ private static String getNetRC(OAuthTokenInfo accessTokenInfo) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("machine ");
+ sb.append(accessTokenInfo.resourceHost());
+ sb.append(" login ");
+ sb.append(accessTokenInfo.username());
+ sb.append(" password ");
+ sb.append(accessTokenInfo.accessToken());
+ return sb.toString();
+ }
+
+ private static String getCookie(OAuthTokenInfo accessTokenInfo) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(accessTokenInfo.resourceHost());
+ sb.append("\tFALSE\t/\tTRUE\t");
+ sb.append(getExpiresAtSeconds(accessTokenInfo));
+ sb.append("\tgit-");
+ sb.append(accessTokenInfo.username());
+ sb.append('\t');
+ sb.append(accessTokenInfo.accessToken());
+ if (accessTokenInfo.providerId() != null) {
+ sb.append('@').append(accessTokenInfo.providerId());
+ }
+ return sb.toString();
+ }
+
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SettingsScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SettingsScreen.java
index 405ef68e4a..ee7407e6ef 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SettingsScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SettingsScreen.java
@@ -47,6 +47,10 @@ public abstract class SettingsScreen extends MenuScreen {
if (Gerrit.info().auth().isHttpPasswordSettingsEnabled()) {
linkByGerrit(Util.C.tabHttpAccess(), PageLinks.SETTINGS_HTTP_PASSWORD);
}
+ if (Gerrit.info().auth().isOAuth()
+ && Gerrit.info().auth().isGitBasicAuth()) {
+ linkByGerrit(Util.C.tabOAuthToken(), PageLinks.SETTINGS_OAUTH_TOKEN);
+ }
if (Gerrit.info().gerrit().editGpgKeys()) {
linkByGerrit(Util.C.tabGpgKeys(), PageLinks.SETTINGS_GPGKEYS);
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css
index 8653a95b48..419067299f 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css
@@ -828,6 +828,70 @@ a:hover.downloadLink {
margin-bottom: 10px;
}
+.oauthInfoBlock {
+ margin-bottom: 10px;
+}
+.oauthToken {
+ font-family: monospace;
+ font-size: small;
+ width: 40em;
+}
+.oauthToken span {
+ white-space: nowrap;
+ display: inline-block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ width: 38em;
+}
+.oauthExpires {
+ font-family: monospace;
+ font-size: small;
+ width: 40em;
+}
+.oauthPanel {
+ margin-top: 10px;
+ border: 1px solid trimColor;
+ padding: 5px 5px 5px 5px;
+}
+.oauthPanelNetRCHeading {
+ margin-top: 5px;
+ margin-left: 1em;
+ white-space: nowrap;
+}
+.oauthPanelNetRCEntry {
+ margin-top: 5px;
+ margin-left: 2em;
+ font-family: monospace;
+ font-size: small;
+ width: 80em;
+}
+.oauthPanelNetRCEntry span {
+ white-space: nowrap;
+ display: inline-block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ width: 78em;
+}
+.oauthPanelCookieHeading {
+ margin-top: 15px;
+ margin-left: 1em;
+ white-space: nowrap;
+}
+.oauthPanelCookieEntry {
+ margin-top: 5px;
+ margin-left: 2em;
+ font-family: monospace;
+ font-size: small;
+ width: 80em;
+}
+.oauthPanelCookieEntry span {
+ white-space: nowrap;
+ display: inline-block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ width: 78em;
+}
+
/** CommentedActionDialog **/
.commentedActionDialog .gwt-DisclosurePanel .header td {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetOAuthToken.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetOAuthToken.java
new file mode 100644
index 0000000000..b6ba3bca10
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetOAuthToken.java
@@ -0,0 +1,90 @@
+// Copyright (C) 2016 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.auth.oauth.OAuthToken;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.auth.oauth.OAuthTokenCache;
+import com.google.gerrit.server.config.CanonicalWebUrl;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+@Singleton
+class GetOAuthToken implements RestReadView{
+
+ private static final String BEARER_TYPE = "bearer";
+
+ private final Provider self;
+ private final OAuthTokenCache tokenCache;
+ private final String hostName;
+
+ @Inject
+ GetOAuthToken(Provider self,
+ OAuthTokenCache tokenCache,
+ @CanonicalWebUrl Provider urlProvider) {
+ this.self = self;
+ this.tokenCache = tokenCache;
+ this.hostName = getHostName(urlProvider.get());
+ }
+
+ @Override
+ public OAuthTokenInfo apply(AccountResource rsrc) throws AuthException,
+ ResourceNotFoundException {
+ if (self.get() != rsrc.getUser()) {
+ throw new AuthException("not allowed to get access token");
+ }
+ String username = rsrc.getUser().getAccount().getUserName();
+ if (username == null) {
+ throw new ResourceNotFoundException();
+ }
+ OAuthToken accessToken = tokenCache.get(username);
+ if (accessToken == null) {
+ throw new ResourceNotFoundException();
+ }
+ OAuthTokenInfo accessTokenInfo = new OAuthTokenInfo();
+ accessTokenInfo.username = username;
+ accessTokenInfo.resourceHost = hostName;
+ accessTokenInfo.accessToken = accessToken.getToken();
+ accessTokenInfo.providerId = accessToken.getProviderId();
+ accessTokenInfo.expiresAt = Long.toString(accessToken.getExpiresAt());
+ accessTokenInfo.type = BEARER_TYPE;
+ return accessTokenInfo;
+ }
+
+ private static String getHostName(String canonicalWebUrl) {
+ try {
+ return new URI(canonicalWebUrl).getHost();
+ } catch (URISyntaxException e) {
+ return null;
+ }
+ }
+
+ public static class OAuthTokenInfo {
+ public String username;
+ public String resourceHost;
+ public String accessToken;
+ public String providerId;
+ public String expiresAt;
+ public String type;
+ }
+
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java
index 52e9d4c8c2..9604322cf4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java
@@ -66,6 +66,8 @@ public class Module extends RestApiModule {
get(SSH_KEY_KIND).to(GetSshKey.class);
delete(SSH_KEY_KIND).to(DeleteSshKey.class);
+ get(ACCOUNT_KIND, "oauthtoken").to(GetOAuthToken.class);
+
get(ACCOUNT_KIND, "avatar").to(GetAvatar.class);
get(ACCOUNT_KIND, "avatar.change.url").to(GetAvatarChangeUrl.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GetServerInfo.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GetServerInfo.java
index 6c774cf7a4..b30616cefc 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GetServerInfo.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GetServerInfo.java
@@ -128,6 +128,7 @@ public class GetServerInfo implements RestReadView {
info.useContributorAgreements = toBoolean(cfg.isUseContributorAgreements());
info.editableAccountFields = new ArrayList<>(realm.getEditableFields());
info.switchAccountUrl = cfg.getSwitchAccountUrl();
+ info.isGitBasicAuth = toBoolean(cfg.isGitBasicAuth());
switch (info.authType) {
case LDAP:
@@ -135,7 +136,6 @@ public class GetServerInfo implements RestReadView {
info.registerUrl = cfg.getRegisterUrl();
info.registerText = cfg.getRegisterText();
info.editFullNameUrl = cfg.getEditFullNameUrl();
- info.isGitBasicAuth = toBoolean(cfg.isGitBasicAuth());
break;
case CUSTOM_EXTENSION: