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
|
||||
----
|
||||
|
||||
[[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
|
||||
--
|
||||
@@ -2075,6 +2112,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.
|
||||
|
||||
@@ -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 <tt>"plugin-name:provider-name"</tt>, 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user