Merge changes I4fad66f5,I6ddb8258
* changes: Settings screen for OAuth tokens REST API for retrieving OAuth access tokens
This commit is contained in:
@@ -420,6 +420,43 @@ Deletes the HTTP password of an account.
|
|||||||
HTTP/1.1 204 No Content
|
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]]
|
||||||
=== 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.
|
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]]
|
[[preferences-info]]
|
||||||
=== PreferencesInfo
|
=== PreferencesInfo
|
||||||
The `PreferencesInfo` entity contains information about a user's preferences.
|
The `PreferencesInfo` entity contains information about a user's preferences.
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ public class PageLinks {
|
|||||||
public static final String SETTINGS_SSHKEYS = "/settings/ssh-keys";
|
public static final String SETTINGS_SSHKEYS = "/settings/ssh-keys";
|
||||||
public static final String SETTINGS_GPGKEYS = "/settings/gpg-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_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_WEBIDENT = "/settings/web-identities";
|
||||||
public static final String SETTINGS_MYGROUPS = "/settings/group-memberships";
|
public static final String SETTINGS_MYGROUPS = "/settings/group-memberships";
|
||||||
public static final String SETTINGS_AGREEMENTS = "/settings/agreements";
|
public static final String SETTINGS_AGREEMENTS = "/settings/agreements";
|
||||||
|
|||||||
@@ -31,16 +31,23 @@ public class OAuthToken implements Serializable {
|
|||||||
*/
|
*/
|
||||||
private final long expiresAt;
|
private final long expiresAt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The identifier of the OAuth provider that issued this token
|
||||||
|
* in the form <tt>"plugin-name:provider-name"</tt>, or {@code null}.
|
||||||
|
*/
|
||||||
|
private final String providerId;
|
||||||
|
|
||||||
public OAuthToken(String token, String secret, String raw) {
|
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,
|
public OAuthToken(String token, String secret, String raw,
|
||||||
long expiresAt) {
|
long expiresAt, String providerId) {
|
||||||
this.token = token;
|
this.token = token;
|
||||||
this.secret = secret;
|
this.secret = secret;
|
||||||
this.raw = raw;
|
this.raw = raw;
|
||||||
this.expiresAt = expiresAt;
|
this.expiresAt = expiresAt;
|
||||||
|
this.providerId = providerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getToken() {
|
public String getToken() {
|
||||||
@@ -62,4 +69,8 @@ public class OAuthToken implements Serializable {
|
|||||||
public boolean isExpired() {
|
public boolean isExpired() {
|
||||||
return System.currentTimeMillis() > expiresAt;
|
return System.currentTimeMillis() > expiresAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getProviderId() {
|
||||||
|
return providerId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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; }-*/;
|
||||||
|
}
|
||||||
@@ -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_EXTENSION;
|
||||||
import static com.google.gerrit.common.PageLinks.SETTINGS_GPGKEYS;
|
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_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_MYGROUPS;
|
||||||
import static com.google.gerrit.common.PageLinks.SETTINGS_NEW_AGREEMENT;
|
import static com.google.gerrit.common.PageLinks.SETTINGS_NEW_AGREEMENT;
|
||||||
import static com.google.gerrit.common.PageLinks.SETTINGS_PREFERENCES;
|
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.MyGpgKeysScreen;
|
||||||
import com.google.gerrit.client.account.MyGroupsScreen;
|
import com.google.gerrit.client.account.MyGroupsScreen;
|
||||||
import com.google.gerrit.client.account.MyIdentitiesScreen;
|
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.MyPasswordScreen;
|
||||||
import com.google.gerrit.client.account.MyPreferencesScreen;
|
import com.google.gerrit.client.account.MyPreferencesScreen;
|
||||||
import com.google.gerrit.client.account.MyProfileScreen;
|
import com.google.gerrit.client.account.MyProfileScreen;
|
||||||
@@ -568,6 +570,12 @@ public class Dispatcher {
|
|||||||
return new MyPasswordScreen();
|
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)
|
if (matchExact(MY_GROUPS, token)
|
||||||
|| matchExact(SETTINGS_MYGROUPS, token)) {
|
|| matchExact(SETTINGS_MYGROUPS, token)) {
|
||||||
return new MyGroupsScreen();
|
return new MyGroupsScreen();
|
||||||
|
|||||||
@@ -105,6 +105,14 @@ public interface GerritCss extends CssResource {
|
|||||||
String menuScreenMenuBar();
|
String menuScreenMenuBar();
|
||||||
String needsReview();
|
String needsReview();
|
||||||
String negscore();
|
String negscore();
|
||||||
|
String oauthExpires();
|
||||||
|
String oauthInfoBlock();
|
||||||
|
String oauthPanel();
|
||||||
|
String oauthPanelCookieEntry();
|
||||||
|
String oauthPanelCookieHeading();
|
||||||
|
String oauthPanelNetRCEntry();
|
||||||
|
String oauthPanelNetRCHeading();
|
||||||
|
String oauthToken();
|
||||||
String pagingLink();
|
String pagingLink();
|
||||||
String patchSetActions();
|
String patchSetActions();
|
||||||
String pluginProjectConfigInheritedValue();
|
String pluginProjectConfigInheritedValue();
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ public interface AccountConstants extends Constants {
|
|||||||
String tabGpgKeys();
|
String tabGpgKeys();
|
||||||
String tabHttpAccess();
|
String tabHttpAccess();
|
||||||
String tabMyGroups();
|
String tabMyGroups();
|
||||||
|
String tabOAuthToken();
|
||||||
String tabPreferences();
|
String tabPreferences();
|
||||||
String tabSshKeys();
|
String tabSshKeys();
|
||||||
String tabWatchedProjects();
|
String tabWatchedProjects();
|
||||||
@@ -81,6 +82,12 @@ public interface AccountConstants extends Constants {
|
|||||||
String invalidUserName();
|
String invalidUserName();
|
||||||
String invalidUserEmail();
|
String invalidUserEmail();
|
||||||
|
|
||||||
|
String labelOAuthToken();
|
||||||
|
String labelOAuthExpires();
|
||||||
|
String labelOAuthNetRCEntry();
|
||||||
|
String labelOAuthGitCookie();
|
||||||
|
String labelOAuthExpired();
|
||||||
|
|
||||||
String sshKeyInvalid();
|
String sshKeyInvalid();
|
||||||
String sshKeyAlgorithm();
|
String sshKeyAlgorithm();
|
||||||
String sshKeyKey();
|
String sshKeyKey();
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ tabDiffPreferences = Diff Preferences
|
|||||||
tabEditPreferences = Edit Preferences
|
tabEditPreferences = Edit Preferences
|
||||||
tabGpgKeys = GPG Public Keys
|
tabGpgKeys = GPG Public Keys
|
||||||
tabHttpAccess = HTTP Password
|
tabHttpAccess = HTTP Password
|
||||||
|
tabOAuthToken = OAuth Token
|
||||||
tabMyGroups = Groups
|
tabMyGroups = Groups
|
||||||
tabPreferences = Preferences
|
tabPreferences = Preferences
|
||||||
tabSshKeys = SSH Public Keys
|
tabSshKeys = SSH Public Keys
|
||||||
@@ -68,6 +69,13 @@ linkEditFullName = Edit
|
|||||||
linkReloadContact = Reload
|
linkReloadContact = Reload
|
||||||
invalidUserName = Username must contain only letters, numbers, _, - or .
|
invalidUserName = Username must contain only letters, numbers, _, - or .
|
||||||
invalidUserEmail = Email format is wrong.
|
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
|
sshKeyInvalid = Invalid Key
|
||||||
sshKeyAlgorithm = Algorithm
|
sshKeyAlgorithm = Algorithm
|
||||||
sshKeyKey = Key
|
sshKeyKey = Key
|
||||||
|
|||||||
@@ -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<GeneralPreferences>(this) {
|
||||||
|
@Override
|
||||||
|
protected void preDisplay(GeneralPreferences prefs) {
|
||||||
|
display(prefs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void display(final GeneralPreferences prefs) {
|
||||||
|
AccountApi.self().view("oauthtoken")
|
||||||
|
.get(new GerritCallback<OAuthTokenInfo>() {
|
||||||
|
@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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -47,6 +47,10 @@ public abstract class SettingsScreen extends MenuScreen {
|
|||||||
if (Gerrit.info().auth().isHttpPasswordSettingsEnabled()) {
|
if (Gerrit.info().auth().isHttpPasswordSettingsEnabled()) {
|
||||||
linkByGerrit(Util.C.tabHttpAccess(), PageLinks.SETTINGS_HTTP_PASSWORD);
|
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()) {
|
if (Gerrit.info().gerrit().editGpgKeys()) {
|
||||||
linkByGerrit(Util.C.tabGpgKeys(), PageLinks.SETTINGS_GPGKEYS);
|
linkByGerrit(Util.C.tabGpgKeys(), PageLinks.SETTINGS_GPGKEYS);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -828,6 +828,70 @@ a:hover.downloadLink {
|
|||||||
margin-bottom: 10px;
|
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 **/
|
||||||
.commentedActionDialog .gwt-DisclosurePanel .header td {
|
.commentedActionDialog .gwt-DisclosurePanel .header td {
|
||||||
|
|||||||
@@ -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<AccountResource>{
|
||||||
|
|
||||||
|
private static final String BEARER_TYPE = "bearer";
|
||||||
|
|
||||||
|
private final Provider<CurrentUser> self;
|
||||||
|
private final OAuthTokenCache tokenCache;
|
||||||
|
private final String hostName;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
GetOAuthToken(Provider<CurrentUser> self,
|
||||||
|
OAuthTokenCache tokenCache,
|
||||||
|
@CanonicalWebUrl Provider<String> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -66,6 +66,8 @@ public class Module extends RestApiModule {
|
|||||||
get(SSH_KEY_KIND).to(GetSshKey.class);
|
get(SSH_KEY_KIND).to(GetSshKey.class);
|
||||||
delete(SSH_KEY_KIND).to(DeleteSshKey.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").to(GetAvatar.class);
|
||||||
get(ACCOUNT_KIND, "avatar.change.url").to(GetAvatarChangeUrl.class);
|
get(ACCOUNT_KIND, "avatar.change.url").to(GetAvatarChangeUrl.class);
|
||||||
|
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ public class GetServerInfo implements RestReadView<ConfigResource> {
|
|||||||
info.useContributorAgreements = toBoolean(cfg.isUseContributorAgreements());
|
info.useContributorAgreements = toBoolean(cfg.isUseContributorAgreements());
|
||||||
info.editableAccountFields = new ArrayList<>(realm.getEditableFields());
|
info.editableAccountFields = new ArrayList<>(realm.getEditableFields());
|
||||||
info.switchAccountUrl = cfg.getSwitchAccountUrl();
|
info.switchAccountUrl = cfg.getSwitchAccountUrl();
|
||||||
|
info.isGitBasicAuth = toBoolean(cfg.isGitBasicAuth());
|
||||||
|
|
||||||
switch (info.authType) {
|
switch (info.authType) {
|
||||||
case LDAP:
|
case LDAP:
|
||||||
@@ -135,7 +136,6 @@ public class GetServerInfo implements RestReadView<ConfigResource> {
|
|||||||
info.registerUrl = cfg.getRegisterUrl();
|
info.registerUrl = cfg.getRegisterUrl();
|
||||||
info.registerText = cfg.getRegisterText();
|
info.registerText = cfg.getRegisterText();
|
||||||
info.editFullNameUrl = cfg.getEditFullNameUrl();
|
info.editFullNameUrl = cfg.getEditFullNameUrl();
|
||||||
info.isGitBasicAuth = toBoolean(cfg.isGitBasicAuth());
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CUSTOM_EXTENSION:
|
case CUSTOM_EXTENSION:
|
||||||
|
|||||||
Reference in New Issue
Block a user