REST API for retrieving OAuth access tokens
As preparation for an UI to retrieve OAuth tokens, a new endpoint
for the account REST API is added that returns a previously
obtained OAuth token:
GET /a/accounts/self/oauthtoken
The response will be 200 OK in case a token is available and the
response will contain a JSON body of the form
)]}'
{
"username": "johndow",
"resource_host": "git.example.org",
"access_token": "eyJhbGciOiJSUzI1NiJ9.eyJqdGkiOi...",
"providerId": "oauth-plugin:oauth-provider",
"expires_at": "922337203775",
"type": "bearer"
}
If there is no token available, or the token has already expired,
404 is returned. Attempts to retrieve a token of another user are
rejected with 403 Forbidden.
Change-Id: I6ddb825890e88c49bd8c5e66b8c5508cef7df347
Signed-off-by: Michael Ochmann <michael.ochmann@sap.com>
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
|
||||||
--
|
--
|
||||||
@@ -2075,6 +2112,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.
|
||||||
|
|||||||
@@ -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,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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -64,6 +64,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);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user