/*
GetAccessTokenRequest.java
Copyright (c) 2014 NTT DOCOMO,INC.
Released under the MIT license
http://opensource.org/licenses/mit-license.php
*/
package org.deviceconnect.android.manager.request;
import android.content.Intent;
import android.os.Bundle;
import org.deviceconnect.android.localoauth.AccessTokenData;
import org.deviceconnect.android.localoauth.AccessTokenScope;
import org.deviceconnect.android.localoauth.ConfirmAuthParams;
import org.deviceconnect.android.localoauth.LocalOAuth2Main;
import org.deviceconnect.android.localoauth.PublishAccessTokenListener;
import org.deviceconnect.android.localoauth.exception.AuthorizationException;
import org.deviceconnect.android.manager.DConnectSettings;
import org.deviceconnect.android.manager.profile.AuthorizationProfile;
import org.deviceconnect.message.DConnectMessage;
import org.deviceconnect.message.DConnectMessage.ErrorCode;
import org.deviceconnect.profile.AuthorizationProfileConstants;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
/**
* LocalOAuthにアクセストークンを要求するリクエスト.
* @author NTT DOCOMO, INC.
*/
public class GetAccessTokenRequest extends DConnectRequest {
/** ロックオブジェクト. */
private final Object mLockObj = new Object();
@Override
public boolean hasRequestCode(final int requestCode) {
return false;
}
@Override
public void run() {
try {
getAccessToken();
} catch (AuthorizationException e) {
setAuthorizationError(mResponse, e.getMessage());
} catch (UnsupportedEncodingException e) {
setInvalidRequestParameterError(mResponse, e.getMessage());
} catch (IllegalArgumentException e) {
setInvalidRequestParameterError(mResponse, e.getMessage());
} catch (IllegalStateException e) {
setInvalidRequestParameterError(mResponse, e.getMessage());
}
sendResponse(mResponse);
}
/**
* アクセストークンの取得の処理を行う.
* @throws AuthorizationException 認証に失敗した場合に発生
* @throws UnsupportedEncodingException 文字のエンコードに失敗した場合に発生
*/
private void getAccessToken() throws AuthorizationException, UnsupportedEncodingException {
String serviceId = mRequest.getStringExtra(DConnectMessage.EXTRA_SERVICE_ID);
String clientId = mRequest.getStringExtra(AuthorizationProfile.PARAM_CLIENT_ID);
String scopeParam = mRequest.getStringExtra(AuthorizationProfile.PARAM_SCOPE);
String[] scopes = null;
String applicationName = mRequest.getStringExtra(AuthorizationProfile.PARAM_APPLICATION_NAME);
if (scopeParam != null) {
scopes = parseScopes(scopeParam.toLowerCase()); // XXXX パスの大文字小文字を無視
}
// TODO _typeからアプリorデバイスプラグインかを判別できる?
ConfirmAuthParams params = new ConfirmAuthParams.Builder().context(mContext).serviceId(serviceId)
.clientId(clientId).scopes(scopes).applicationName(applicationName)
.isForDevicePlugin(false)
.keyword(DConnectSettings.getInstance().getKeyword())
.build();
// Local OAuthでAccessTokenを作成する。
final AccessTokenData[] token = new AccessTokenData[1];
LocalOAuth2Main.confirmPublishAccessToken(params, new PublishAccessTokenListener() {
@Override
public void onReceiveAccessToken(final AccessTokenData accessTokenData) {
token[0] = accessTokenData;
synchronized (mLockObj) {
mLockObj.notifyAll();
}
}
@Override
public void onReceiveException(final Exception exception) {
token[0] = null;
synchronized (mLockObj) {
mLockObj.notifyAll();
}
}
});
// ユーザからのレスポンスを待つ
if (token[0] == null) {
waitForResponse();
}
if (token[0] != null && token[0].getAccessToken() != null) {
mResponse.putExtra(DConnectMessage.EXTRA_RESULT, DConnectMessage.RESULT_OK);
mResponse.putExtra(AuthorizationProfile.PARAM_ACCESS_TOKEN, token[0].getAccessToken());
AccessTokenScope[] atScopes = token[0].getScopes();
if (atScopes != null) {
List<Bundle> s = new ArrayList<Bundle>();
for (int i = 0; i < atScopes.length; i++) {
Bundle b = new Bundle();
b.putString(AuthorizationProfileConstants.PARAM_SCOPE,
atScopes[i].getScope());
b.putLong(AuthorizationProfileConstants.PARAM_EXPIRE_PERIOD,
atScopes[i].getExpirePeriod());
s.add(b);
}
mResponse.putExtra(AuthorizationProfileConstants.PARAM_SCOPES,
s.toArray(new Bundle[s.size()]));
}
} else {
setAuthorizationError(mResponse, "Cannot create a access token.");
}
sendResponse(mResponse);
}
/**
* レスポンスのエラーコードに 認証エラー を設定し、指定されたエラーメッセージを設定する.
*
* @param response レスポンスパラメータ
* @param message エラーメッセージ
*/
private static void setAuthorizationError(final Intent response, final String message) {
setError(response, ErrorCode.AUTHORIZATION, message);
}
/**
* レスポンスのエラーコードに 不正なパラメータエラー を設定し、指定されたエラーメッセージを設定する.
*
* @param response レスポンスパラメータ
* @param message エラーメッセージ
*/
private static void setInvalidRequestParameterError(final Intent response, final String message) {
setError(response, ErrorCode.INVALID_REQUEST_PARAMETER, message);
}
/**
* レスポンスにエラーを設定する.
* <p>
* GotAPI 1.0仕様により、空文字のアクセストークンを設定する.
* </p>
*
* @param response エラーを設定するレスポンスパラメータ
* @param error エラーコード
* @param message エラーメッセージ
*/
private static void setError(final Intent response, final ErrorCode error, final String message) {
response.putExtra(DConnectMessage.EXTRA_RESULT, DConnectMessage.RESULT_ERROR);
response.putExtra(DConnectMessage.EXTRA_ERROR_CODE, error.getCode());
response.putExtra(DConnectMessage.EXTRA_ERROR_MESSAGE, (message == null ? error.toString() : message));
// GotAPI対応: エラーの場合は、空文字のアクセストークンを返す
response.putExtra(AuthorizationProfile.PARAM_ACCESS_TOKEN, "");
}
/**
* レスポンスが返ってくるまでの間スレッドを停止する.
* タイムアウトは設定していない。
*/
private void waitForResponse() {
synchronized (mLockObj) {
try {
mLockObj.wait();
} catch (InterruptedException e) {
return;
}
}
}
/**
* スコープを分割して、配列に変換します.
* @param scope スコープ
* @return 分割されたスコープ. scopeが<code>null</code>または空文字の場合は<code>null</code>
*/
private String[] parseScopes(final String scope) {
if (scope == null) {
return null;
}
String[] scopes = scope.split(",");
for (int i = 0; i < scopes.length; i++) {
String s = scopes[i].trim();
if (s.equals("")) {
return null;
}
scopes[i] = s;
}
return scopes;
}
}