/* * Copyright 2005-2014 Restlet * * The contents of this file are subject to the terms of one of the following * open source licenses: Apache 2.0 or LGPL 3.0 or LGPL 2.1 or CDDL 1.0 or EPL * 1.0 (the "Licenses"). You can select the license that you prefer but you may * not use this file except in compliance with one of these Licenses. * * You can obtain a copy of the Apache 2.0 license at * http://www.opensource.org/licenses/apache-2.0 * * You can obtain a copy of the LGPL 3.0 license at * http://www.opensource.org/licenses/lgpl-3.0 * * You can obtain a copy of the LGPL 2.1 license at * http://www.opensource.org/licenses/lgpl-2.1 * * You can obtain a copy of the CDDL 1.0 license at * http://www.opensource.org/licenses/cddl1 * * You can obtain a copy of the EPL 1.0 license at * http://www.opensource.org/licenses/eclipse-1.0 * * See the Licenses for the specific language governing permissions and * limitations under the Licenses. * * Alternatively, you can obtain a royalty free commercial license with less * limitations, transferable or non-transferable, directly at * http://www.restlet.com/products/restlet-framework * * Restlet is a registered trademark of Restlet */ package org.deviceconnect.android.localoauth; import android.content.Intent; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.util.Base64; import org.deviceconnect.android.BuildConfig; import org.deviceconnect.android.cipher.signature.AuthSignature; import org.deviceconnect.android.localoauth.activity.AccessTokenListActivity; import org.deviceconnect.android.localoauth.activity.ConfirmAuthActivity; import org.deviceconnect.android.localoauth.exception.AuthorizationException; import org.deviceconnect.android.localoauth.oauthserver.LoginPageServerResource; import org.deviceconnect.android.localoauth.oauthserver.SampleUser; import org.deviceconnect.android.localoauth.oauthserver.SampleUserManager; import org.deviceconnect.android.localoauth.oauthserver.db.LocalOAuthOpenHelper; import org.deviceconnect.android.localoauth.oauthserver.db.SQLiteClient; import org.deviceconnect.android.localoauth.oauthserver.db.SQLiteClientManager; import org.deviceconnect.android.localoauth.oauthserver.db.SQLiteToken; import org.deviceconnect.android.localoauth.oauthserver.db.SQLiteTokenManager; import org.deviceconnect.android.localoauth.temp.RedirectRepresentation; import org.deviceconnect.android.localoauth.temp.ResultRepresentation; import org.deviceconnect.android.logger.AndroidHandler; import org.json.JSONException; import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; import org.restlet.data.ClientInfo; import org.restlet.data.Cookie; import org.restlet.data.Form; import org.restlet.data.Reference; import org.restlet.ext.oauth.AccessTokenServerResource; import org.restlet.ext.oauth.AuthPageServerResource; import org.restlet.ext.oauth.AuthorizationBaseServerResource; import org.restlet.ext.oauth.AuthorizationServerResource; import org.restlet.ext.oauth.OAuthException; import org.restlet.ext.oauth.PackageInfoOAuth; import org.restlet.ext.oauth.internal.Client; import org.restlet.ext.oauth.internal.Client.ClientType; import org.restlet.ext.oauth.internal.ClientManager; import org.restlet.ext.oauth.internal.Scope; import org.restlet.ext.oauth.internal.ServerToken; import org.restlet.ext.oauth.internal.Token; import org.restlet.ext.oauth.internal.TokenManager; import org.restlet.representation.Representation; import org.restlet.representation.StringRepresentation; import org.restlet.util.Series; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import java.util.logging.SimpleFormatter; /** * Local OAuth API. */ public final class LocalOAuth2Main { /** authorization_code. */ public static final String AUTHORIZATION_CODE = "authorization_code"; /** プロセス間通信用メッセージID(threadIdがキューに残っているか確認通知). */ public static final int MSG_CHECK_THREADID_RESULT = 1002; /** プロセス間通信用メッセージID : 承認確認画面で許可/拒否されたかをMessageでDevice ConnectのServiceに送る. */ public static final int MSG_CONFIRM_APPROVAL = 1000; /** プロセス間通信用メッセージID : threadIdが有効かをMessageでDevice ConnectのServiceに送る. */ public static final int MSG_CONFIRM_CHECK_THREADID = 1001; /** ダミー値(RedirectURI). */ public static final String DUMMY_REDIRECTURI = "dummyRedirectURI"; /** ダミー値(OriginalRef). */ private static final String DUMMY_ORIGINALREF = "dummyOriginalRef"; /** ダミー値(Reference). */ private static final String DUMMY_REFERENCE = "dummyReference"; /** ダミー値(Scope). */ private static final String DUMMY_SCOPE1 = "scope1"; /** UserManager. */ private static SampleUserManager sUserManager; /** ClientManager. */ private static ClientManager sClientManager; /** TokenManager. */ private static TokenManager sTokenManager; /** DBHelper. */ private static LocalOAuthOpenHelper sDbHelper; /** ロガー. */ private static Logger sLogger = Logger.getLogger("org.deviceconnect.localoauth"); /** 自動テストモードフラグ. */ private static boolean sAutoTestMode = false; /** DBアクセス用Lockオブジェクト. */ private static Object sLockForDbAccess = new Object(); /** * Bindフラグ. * <p> * ConfirmAuthActivityがLocalOAuth2ServiceにBind状態を持つ。<br> * 基本的にConfirmAuthActivityは、1つだけ起動するようにするので、Bindされる数も1つになる。 * </p> */ private static boolean sBound; /** メッセンジャー. */ private static Messenger sMessenger = new Messenger(new ApprovalHandler()); /** 承認確認画面リクエストキュー(アクセスする際はsynchronizedが必要). */ private static List<ConfirmAuthRequest> sRequestQueue = new ArrayList<ConfirmAuthRequest>(); /** 承認確認画面リクエストキュー用Lockオブジェクト. */ private static Object sLockForRequestQueue = new Object(); /** * コンストラクタ. */ private LocalOAuth2Main() { } /** * SampleUserManagerを返す. * @return SampleUserManagerのインスタンス */ public static SampleUserManager getSampleUserManager() { return sUserManager; } /** * ClientManagerを返す. * * @return ClientManagerのインスタンス */ public static ClientManager getClientManager() { return sClientManager; } /** * (0)LocalOAuth2Serviceで使用されるBinderを返す. * <p> * ※LocalOAuthのユーザーは利用する必要はない. * </p> * @param intent Intent * @return Binder */ static IBinder onBind(final Intent intent) { sBound = true; return sMessenger.getBinder(); } /** * LocalOAuth2Serviceでbinderがunbindされた場合の処理を行う. */ static void onUnbind() { sBound = false; } /** * 自動テストモードフラグ設定(単体テスト用). * @param autoTestMode 自動テストモードモードフラグ(true: 有効にする / false: 無効にする) */ public static void setUseAutoTestMode(final boolean autoTestMode) { sAutoTestMode = autoTestMode; } /** * 自動テストモードが有効か判定する. * @return true: 有効 / false: 無効 */ public static boolean isAutoTestMode() { return sAutoTestMode; } /** * ロガーを取得する. * @return ロガー */ public static Logger getLogger() { return sLogger; } /** * (1)Local OAuthを初期化する. * <p> * - 変数を初期化する。<br> * - ユーザーを1件追加する。 * </p> * @param context コンテキスト */ public static void initialize(final android.content.Context context) { /* DB初期化処理 */ sDbHelper = new LocalOAuthOpenHelper(context); /* 初期化処理 */ sUserManager = new SampleUserManager(); sClientManager = new SQLiteClientManager(); sTokenManager = new SQLiteTokenManager(); /* ユーザー追加 */ addUserData(SampleUser.LOCALOAUTH_USER, SampleUser.LOCALOAUTH_PASS); /* ログレベル設定 */ Logger logger = getLogger(); if (BuildConfig.DEBUG) { AndroidHandler handler = new AndroidHandler(logger.getName()); handler.setFormatter(new SimpleFormatter()); handler.setLevel(Level.ALL); logger.addHandler(handler); logger.setLevel(Level.ALL); } else { logger.setLevel(Level.OFF); } } /** * (1)-2.LocalOAuth終了処理. */ public static void destroy() { /* DBをまとめてクローズ */ if (sDbHelper != null) { sDbHelper.close(); } sUserManager = null; sClientManager = null; sTokenManager = null; sDbHelper = null; } /** * (2)クライアントを登録する. * <p> * アプリやデバイスプラグインがインストールされるときに実行する. * </p> * @param packageInfo アプリ(Android)の場合は、パッケージ名を入れる。<br> * アプリ(Web)の場合は、パッケージ名にURLを入れる。<br> * デバイスプラグインの場合は、パッケージ名とサービスIDを入れる。<br> * @return 登録したクライアント情報(クライアントID, クライアントシークレット)を返す。<br> * nullは返らない。 * @throws AuthorizationException Authorization例外. */ public static ClientData createClient(final PackageInfoOAuth packageInfo) throws AuthorizationException { /* 引数チェック */ if (packageInfo == null) { throw new IllegalArgumentException("packageInfo is null."); } else if (packageInfo.getPackageName() == null) { throw new IllegalArgumentException("packageInfo.getPackageName() is null."); } else if (packageInfo.getPackageName().length() <= 0) { throw new IllegalArgumentException("packageInfo is empty."); } /* 長時間使用されていなかったclientIdをクリーンアップする(DBアクセスしたついでに有効クライアント数も取得する) */ int clientCount = cleanupClient(); /* クライアント数が上限まで使用されていれば例外発生 */ if (clientCount >= LocalOAuth2Settings.CLIENT_MAX) { throw new AuthorizationException(AuthorizationException.CLIENT_COUNTS_IS_FULL); } /* クライアント追加 */ ClientData clientData = null; SQLiteDatabase db = null; SQLiteTokenManager sqliteTokenManager = null; SQLiteClientManager sqliteClientManager = null; /* DBを同時アクセスさせない */ synchronized (sLockForDbAccess) { try { /* DBオープン */ db = sDbHelper.getWritableDatabase(); db.beginTransaction(); /* TokenManagerにDBオブジェクトを設定 */ sqliteTokenManager = (SQLiteTokenManager) sTokenManager; sqliteTokenManager.setDb(db); /* ClientManagerにDBオブジェクトを設定 */ sqliteClientManager = (SQLiteClientManager) sClientManager; sqliteClientManager.setDb(db); /* パッケージ情報に対応するクライアントIDがすでに登録済なら破棄する */ Client client = getClientManager().findByPackageInfo(packageInfo); if (client != null) { String clientId = client.getClientId(); removeTokenData(clientId); removeClientData(clientId); } /* クライアントデータを新規生成して返す */ client = addClientData(packageInfo); clientData = new ClientData(client.getClientId(), String.copyValueOf(client.getClientSecret())); /* コミット */ db.setTransactionSuccessful(); } catch (SQLiteException e) { throw new RuntimeException(e); } finally { if (db != null) { db.endTransaction(); db.close(); } /* TokenManagerのDBオブジェクトをクリア設定 */ if (sqliteTokenManager != null) { sqliteTokenManager.setDb(null); } /* ClientManagerのDBオブジェクトをクリア設定 */ if (sqliteClientManager != null) { sqliteClientManager.setDb(null); } } } return clientData; } /** * (3)クライアントを破棄する。アプリやデバイスプラグインがアンインストールされるときに実行する. * * @param clientId クライアントID * @throws AuthorizationException Authorization例外. */ public static void destroyClient(final String clientId) throws AuthorizationException { /* 引数チェック */ if (clientId == null) { throw new IllegalArgumentException("clientId is null."); } SQLiteDatabase db = null; SQLiteTokenManager sqliteTokenManager = null; SQLiteClientManager sqliteClientManager = null; /* DBを同時アクセスさせない */ synchronized (sLockForDbAccess) { try { /* DBオープン */ db = sDbHelper.getWritableDatabase(); db.beginTransaction(); /* TokenManagerにDBオブジェクトを設定 */ sqliteTokenManager = (SQLiteTokenManager) sTokenManager; sqliteTokenManager.setDb(db); /* ClientManagerにDBオブジェクトを設定 */ sqliteClientManager = (SQLiteClientManager) sClientManager; sqliteClientManager.setDb(db); /* クライアントデータ削除 */ removeClientData(clientId); /* コミット */ db.setTransactionSuccessful(); } catch (SQLiteException e) { throw new RuntimeException(e); } finally { if (db != null) { db.endTransaction(); db.close(); } /* TokenManagerのDBオブジェクトをクリア設定 */ if (sqliteTokenManager != null) { sqliteTokenManager.setDb(null); } /* ClientManagerのDBオブジェクトをクリア設定 */ if (sqliteClientManager != null) { sqliteClientManager.setDb(null); } } } sLogger.fine("destroyClient() - clientId:" + clientId); } /** * (4)アプリまたはデバイスプラグインから受け取ったsignatureが、Local * OAuthで生成したsignatureと一致するかチェックする. * * @param signature signature * @param clientId クライアントID * @param grantType グラントタイプ("authorization_code"が渡される) * @param serviceId サービスID(デバイスプラグインの場合のみ設定する。アプリの場合はnullを入れる) * @param scopes 要求されたスコープの配列 * @return true: 一致した / false: 一致しなかった * @throws AuthorizationException Authorization例外. */ public static boolean checkSignature(final String signature, final String clientId, final String grantType, final String serviceId, final String[] scopes) throws AuthorizationException { /* 引数チェック */ if (signature == null) { throw new IllegalArgumentException("signature is null."); } else if (clientId == null) { throw new IllegalArgumentException("clientId is null."); } else if (grantType == null) { throw new IllegalArgumentException("grantType is null."); } else if (scopes == null) { throw new IllegalArgumentException("scopes is null."); } boolean result = false; SQLiteDatabase db = null; SQLiteClientManager sqliteClientManager = null; /* DBを同時アクセスさせない */ synchronized (sLockForDbAccess) { try { /* DBオープン */ db = sDbHelper.getReadableDatabase(); /* ClientManagerにDBオブジェクトを設定 */ sqliteClientManager = (SQLiteClientManager) sClientManager; sqliteClientManager.setDb(db); /* LocalOAuthが保持しているクライアントシークレットを取得 */ Client client = sqliteClientManager.findById(clientId); if (client != null) { String clientSecret = String.copyValueOf(client.getClientSecret()); /* * LocalOAuthが保持しているclientSecretとリクエストのclient_id, grant_type, * scopesを結合して暗号化しsignature作成 */ String innerSignature = AuthSignature.generateSignature(clientId, grantType, serviceId, scopes, clientSecret); /* Signature一致判定 */ if (innerSignature.equals(signature)) { result = true; } else { String strScopes = ""; for (int i = 0; i < scopes.length; i++) { if (i > 0) { strScopes += ","; } strScopes += scopes[i]; } sLogger.warning("checkSignature() - signature not equal."); sLogger.warning(" - signature: " + signature); sLogger.warning(" - innerSignature:" + innerSignature); sLogger.warning(" - clientId:" + clientId); sLogger.warning(" - grantType:" + grantType); sLogger.warning(" - serviceId:" + serviceId); sLogger.warning(" - scopes:" + strScopes); sLogger.warning(" - clientSecret:" + clientSecret); } } else { sLogger.warning("client not found. clientId: " + clientId); } } catch (SQLiteException e) { throw new RuntimeException(e); } finally { if (db != null) { db.close(); } /* TokenManagerのDBオブジェクトをクリア設定 */ if (sqliteClientManager != null) { sqliteClientManager.setDb(null); } } } return result; } /** * (4)-2.Device Connect Managerから受け取ったアクセストークン受信用signatureが、アプリで生成したsignatureと一致するかチェックする. * @param signature signature * @param accessToken アクセストークン * @param clientSecret クライアントシークレット * @return true: 一致した / false: 一致しなかった */ public static boolean checkSignature(final String signature, final String accessToken, final String clientSecret) { /* 引数チェック */ if (signature == null) { throw new IllegalArgumentException("signature is null."); } else if (accessToken == null) { throw new IllegalArgumentException("accessToken is null."); } else if (clientSecret == null) { throw new IllegalArgumentException("clientSecret is null."); } /* Signature作成 */ String innerSignature = AuthSignature.generateSignature(accessToken, clientSecret); /* Signatureが一致するか */ boolean result = signature.equals(innerSignature); if (!result) { sLogger.warning("checkSignature() - signature not equal."); sLogger.warning(" - signature: " + signature); sLogger.warning(" - innerSignature:" + innerSignature); sLogger.warning(" - accessToken:" + accessToken); sLogger.warning(" - clientSecret:" + clientSecret); } return result; } /** * (5)アクセストークン発行承認確認画面表示. * <p> * - Activityはprocess指定を行わないのでDevice Connect Managerのスレッドとは別プロセスとなる。<br> * - Device Connect Managerのサービスととプロセス間通信が行えるように(0)onBind()でBindする。<br> * - Messenger, Handler はLocalOAuth内部に持つ。<br> * * - 状況別で動作が変わる。<br> * * - (a)有効なアクセストークンが存在しない。(失効中も含む) => * 承認確認画面を表示する。承認/拒否はBindされたServiceへMessageで通知される。<br> * * - (b)アクセストークンは存在するがスコープが不足している。 => * 承認確認画面を表示する。承認/拒否はBindされたServiceへMessageで通知される。<br> * * - (c)アクセストークンは存在しスコープも満たされている。 => 承認確認画面は表示しない。(Message通知されない)<br> * </p> * @param params パラメータ * @param listener アクセストークン発行リスナー(承認確認画面で承認/拒否ボタンが押されたら実行される) * @throws AuthorizationException Authorization例外. */ public static void confirmPublishAccessToken(final ConfirmAuthParams params, final PublishAccessTokenListener listener) throws AuthorizationException { /* 引数チェック */ if (params == null) { throw new IllegalArgumentException("confirmAuthParams is null."); } else if (listener == null) { throw new IllegalArgumentException("publishAccessTokenListener is null."); } else if (params.getContext() == null) { throw new IllegalArgumentException("Context is null."); } else if (params.getApplicationName() == null || params.getApplicationName().isEmpty()) { throw new IllegalArgumentException("ApplicationName is null."); } else if (params.getClientId() == null || params.getClientId().isEmpty()) { throw new IllegalArgumentException("ClientId is null."); } else if (params.getScopes() == null || params.getScopes().length <= 0) { throw new IllegalArgumentException("Scope is null."); } /* トークンの状態取得 */ boolean isExpiredAccessToken = false; /* true: 有効期限切れ / false: 有効期限内 */ boolean isIncludeScope = false; /* * true: 要求スコープが全て含まれている / false: * 一部または全部含まれていない */ Client client = null; Token token = null; /* * not null: アクセストークンあり / null: アクセストークンなし */ SQLiteDatabase db = null; SQLiteClientManager sqliteClientManager = null; SQLiteTokenManager sqliteTokenManager = null; /* DBを同時アクセスさせない */ synchronized (sLockForDbAccess) { try { /* DBオープン */ db = sDbHelper.getWritableDatabase(); db.beginTransaction(); /* ClientManagerにDBオブジェクトを設定 */ sqliteClientManager = (SQLiteClientManager) sClientManager; sqliteClientManager.setDb(db); /* TokenManagerにDBオブジェクトを設定 */ sqliteTokenManager = (SQLiteTokenManager) sTokenManager; sqliteTokenManager.setDb(db); /* クライアントをDBから読み込み */ client = getClient(params); /* トークンをDBから読み込み */ token = sTokenManager.findToken(client, SampleUser.USERNAME); /* コミット */ db.setTransactionSuccessful(); } catch (SQLiteException e) { throw new RuntimeException(e); } finally { if (db != null) { db.endTransaction(); db.close(); } /* ClientManagerのDBオブジェクトをクリア設定 */ if (sqliteClientManager != null) { sqliteClientManager.setDb(null); } /* TokenManagerのDBオブジェクトをクリア設定 */ if (sqliteClientManager != null) { sqliteClientManager.setDb(null); } } } if (token != null) { /* アクセストークンが存在するか確認(全スコープの有効期限が切れていたら有効期限切れとみなす) */ if (((ServerToken) token).isExpired()) { isExpiredAccessToken = true; } } /* (a), (b)なら承認確認画面を表示する */ if (token == null /* (a) */ || isExpiredAccessToken /* (a) */ || !isIncludeScope) { /* (b) */ /* カレントスレッドID取得 */ Thread thread = Thread.currentThread(); final long threadId = thread.getId(); /* ロケール取得(アンダーバーがついている場合("ja_JP"等)は、アンダーバーから後の文字列は削除する) */ String locale = Locale.getDefault().getLanguage(); String[] splitlocales = locale.split("_"); if (splitlocales != null && splitlocales.length > 1) { locale = splitlocales[0]; } /* デバイスプラグインの場合はdevicePlugin.xmlからロケールが一致する表示スコープ名を取得する */ Map<String, DevicePluginXmlProfile> supportProfiles = null; if (params.isForDevicePlugin()) { supportProfiles = DevicePluginXmlUtil.getSupportProfiles( params.getContext(), params.getContext().getPackageName()); } String[] scopes = params.getScopes(); String[] displayScopes = new String[scopes.length]; for (int i = 0; i < scopes.length; i++) { /* ローカライズされたプロファイル名取得する */ displayScopes[i] = ScopeUtil.getDisplayScope(params.getContext(), scopes[i], locale, supportProfiles); } /* リクエストデータを作成する */ ConfirmAuthRequest request = new ConfirmAuthRequest(threadId, params, listener, displayScopes); // キューにリクエストを追加 enqueueRequest(request); // ActivityがサービスがBindされていない場合には、 // Activityを起動する。 if (!sBound) { startConfirmAuthActivity(pickupRequest()); } } } /** * (6)OAuthクライアント情報からアクセストークンを取得する. * * @param packageInfo パッケージ情報 * @return not null: アクセストークンデータ / null:アクセストークンがない */ public static AccessTokenData findAccessToken(final PackageInfoOAuth packageInfo) { /* 引数チェック */ if (packageInfo == null) { throw new IllegalArgumentException("packageInfo is null."); } else if (packageInfo.getPackageName() == null) { throw new IllegalArgumentException("packageInfo.getPackageName() is null."); } AccessTokenData acccessTokenData = null; SQLiteDatabase db = null; SQLiteClientManager sqliteClientManager = null; SQLiteTokenManager sqliteTokenManager = null; /* DBを同時アクセスさせない */ synchronized (sLockForDbAccess) { try { /* DBオープン */ db = sDbHelper.getReadableDatabase(); /* ClientManagerにDBオブジェクトを設定 */ sqliteClientManager = (SQLiteClientManager) sClientManager; sqliteClientManager.setDb(db); /* TokenManagerにDBオブジェクトを設定 */ sqliteTokenManager = (SQLiteTokenManager) sTokenManager; sqliteTokenManager.setDb(db); /* パッケージ情報からクライアントデータを取得 */ Client client = sqliteClientManager.findByPackageInfo(packageInfo); if (client != null) { /* クライアントからトークンを取得する */ Token token = sqliteTokenManager.findToken(client, SampleUser.USERNAME); if (token != null) { String accessToken = token.getAccessToken(); long date = token.getRegistrationDate(); AccessTokenScope[] accessTokenScopes = scopesToAccessTokenScopes(token.getScope()); acccessTokenData = new AccessTokenData(accessToken, date, accessTokenScopes); } } } catch (SQLiteException e) { throw new RuntimeException(e); } finally { if (db != null) { db.close(); } /* ClientManagerのDBオブジェクトをクリア設定 */ if (sqliteClientManager != null) { sqliteClientManager.setDb(null); } /* TokenManagerのDBオブジェクトをクリア設定 */ if (sqliteTokenManager != null) { sqliteTokenManager.setDb(null); } } } return acccessTokenData; } /** * (7)アクセストークンを確認する. * * @param accessToken 確認するアクセストークン * @param scope このスコープがアクセストークンに含まれるかチェックする * @param specialScopes 承認許可されていなくてもアクセス可能なscopes(nullなら指定無し) * @return チェック結果 */ public static CheckAccessTokenResult checkAccessToken(final String accessToken, final String scope, final String[] specialScopes) { /* 引数チェック */ if (scope == null) { throw new IllegalArgumentException("scope is null."); } boolean isExistClientId = false; /* * true: アクセストークンを発行したクライアントIDあり / * false: アクセストークンを発行したクライアントIDなし. */ boolean isExistAccessToken = false; /* * true: アクセストークンあり / false: * アクセストークンなし */ boolean isExistScope = false; /* true: スコープあり / false: スコープなし */ boolean isNotExpired = false; /* true: 有効期限内 / false: 有効期限切れ */ // 無視するスコープが指定されていた場合 if (specialScopes != null && Arrays.asList(specialScopes).contains(scope)) { return new CheckAccessTokenResult(true, true, true, true); } // アクセストークンが設定されていない場合 if (accessToken == null) { return new CheckAccessTokenResult(false, false, false, false); } SQLiteDatabase db = null; SQLiteClientManager sqliteClientManager = null; SQLiteTokenManager sqliteTokenManager = null; /* DBを同時アクセスさせない */ synchronized (sLockForDbAccess) { try { /* DBオープン */ db = sDbHelper.getWritableDatabase(); db.beginTransaction(); /* ClientManagerにDBオブジェクトを設定 */ sqliteClientManager = (SQLiteClientManager) sClientManager; sqliteClientManager.setDb(db); /* TokenManagerにDBオブジェクトを設定 */ sqliteTokenManager = (SQLiteTokenManager) sTokenManager; sqliteTokenManager.setDb(db); /* アクセストークンを元にトークンを検索する */ SQLiteToken token = (SQLiteToken) sqliteTokenManager.findTokenByAccessToken(accessToken); if (token != null) { isExistAccessToken = true; /* アクセストークンあり */ Scope[] scopes = token.getScope(); for (Scope s : scopes) { /* token.scopeに"*"が含まれていたら、どんなスコープにもアクセスできる */ if (BuildConfig.DEBUG && s.getScope().equals("*")) { isExistScope = true; /* スコープあり */ isNotExpired = true; /* 有効期限 */ break; } if (s.getScope().equals(scope)) { isExistScope = true; /* スコープあり */ if (s.getExpirePeriod() == 0) { /* 有効期限0の場合は、トークン発行から1分以内の初回アクセスなら有効期限内とする */ long t = System.currentTimeMillis() - token.getRegistrationDate(); if (0 <= t && t <= (LocalOAuth2Settings.ACCESS_TOKEN_GRACE_TIME * LocalOAuth2Settings.MSEC) && token.isFirstAccess()) { isNotExpired = true; } } else if (s.getExpirePeriod() > 0) { /* 有効期限1以上の場合は、トークン発行からの経過時間が有効期限内かを判定して返す */ isNotExpired = !s.isExpired(); } else { /* 有効期限にマイナス値が設定されていたら、有効期限切れとみなす */ isNotExpired = false; } break; } } /* このトークンを発行したクライアントIDが存在するかチェック */ if (sqliteClientManager.findById(token.getClientId()) != null) { isExistClientId = true; } /* トークンのアクセス時間更新 */ SQLiteToken sqliteToken = (SQLiteToken) token; sqliteToken.dbUpdateTokenAccessTime(db); } /* コミット */ db.setTransactionSuccessful(); } catch (SQLiteException e) { throw new RuntimeException(e); } finally { if (db != null) { db.endTransaction(); db.close(); } /* TokenManagerのDBオブジェクトをクリア設定 */ if (sqliteTokenManager != null) { sqliteTokenManager.setDb(null); } /* ClientManagerのDBオブジェクトをクリア設定 */ if (sqliteClientManager != null) { sqliteClientManager.setDb(null); } } } CheckAccessTokenResult result = new CheckAccessTokenResult(isExistClientId, isExistAccessToken, isExistScope, isNotExpired); if (!result.checkResult()) { sLogger.warning("checkAccessToken() - error."); sLogger.warning(" - isExistClientId: " + isExistClientId); sLogger.warning(" - isExistAccessToken: " + isExistAccessToken); sLogger.warning(" - isExistScope:" + isExistScope); sLogger.warning(" - isNotExpired:" + isNotExpired); sLogger.warning(" - accessToken:" + accessToken); sLogger.warning(" - scope:" + scope); } return result; } /** * (8)Signatureを作成する. * * @param clientId クライアントID * @param grantType グラントタイプ * @param serviceId サービスID * @param scopes スコープ * @param clientSecret クライアントシークレット * @return not null: 作成したSignature / null: nullは返さない。 * @throws AuthorizationException Authorization例外. */ public static String createSignature(final String clientId, final String grantType, final String serviceId, final String[] scopes, final String clientSecret) throws AuthorizationException { /* 引数チェック */ if (clientId == null) { throw new IllegalArgumentException("clientId is null."); } else if (grantType == null) { throw new IllegalArgumentException("grantType is null."); } else if (scopes == null) { throw new IllegalArgumentException("scopes is null."); } else if (clientSecret == null) { throw new IllegalArgumentException("clientSecret is null."); } String signature = AuthSignature.generateSignature(clientId, grantType, serviceId, scopes, clientSecret); return signature; } /** * (8)-2.アクセストークンを返却する際に添付するSignatureを作成する. * * @param accessToken アクセストークン * @param clientId クライアントId * @return not null: 作成したSignature / null: nullは返さない。 * @throws AuthorizationException Authorization例外. */ public static String createSignature(final String accessToken, final String clientId) throws AuthorizationException { /* 引数チェック */ if (accessToken == null) { throw new IllegalArgumentException("accessToken is null."); } else if (clientId == null) { throw new IllegalArgumentException("clientId is null."); } /* clientIdからclientSecretを取得する */ String clientSecret = null; SQLiteDatabase db = null; SQLiteClientManager sqliteClientManager = null; /* DBを同時アクセスさせない */ synchronized (sLockForDbAccess) { try { /* DBオープン */ db = sDbHelper.getReadableDatabase(); /* ClientManagerにDBオブジェクトを設定 */ sqliteClientManager = (SQLiteClientManager) sClientManager; sqliteClientManager.setDb(db); /* クライアントIDを元にをクライアントデータを検索する */ Client client = (Client) sqliteClientManager.findById(clientId); if (client != null) { clientSecret = String.copyValueOf(client.getClientSecret()); } else { throw new AuthorizationException(AuthorizationException.CLIENT_NOT_FOUND); } } catch (SQLiteException e) { throw new RuntimeException(e); } finally { if (db != null) { db.close(); } /* TokenManagerのDBオブジェクトをクリア設定 */ if (sqliteClientManager != null) { sqliteClientManager.setDb(null); } } } /* Signature作成 */ String signature = AuthSignature.generateSignature(accessToken, clientSecret); return signature; } /** * (9)クライアントが発行したアクセストークンを破棄して利用できないようにする.<br> * クライアントはパッケージ名で指定する. * * @param packageInfo パッケージ名 */ public static void destroyAccessToken(final PackageInfoOAuth packageInfo) { /* 引数チェック */ if (packageInfo == null) { throw new IllegalArgumentException("packageInfo is null."); } else if (packageInfo.getPackageName() == null) { throw new IllegalArgumentException("packageInfo.getPackageName() is null."); } SQLiteDatabase db = null; SQLiteClientManager sqliteClientManager = null; SQLiteTokenManager sqliteTokenManager = null; /* DBを同時アクセスさせない */ synchronized (sLockForDbAccess) { try { /* DBオープン */ db = sDbHelper.getWritableDatabase(); db.beginTransaction(); /* ClientManagerにDBオブジェクトを設定 */ sqliteClientManager = (SQLiteClientManager) sClientManager; sqliteClientManager.setDb(db); /* TokenManagerにDBオブジェクトを設定 */ sqliteTokenManager = (SQLiteTokenManager) sTokenManager; sqliteTokenManager.setDb(db); Client client = sClientManager.findByPackageInfo(packageInfo); sTokenManager.revokeAllTokens(client); sLogger.fine("destroyAccessToken()"); sLogger.fine(" - clientId:" + client.getClientId()); sLogger.fine(" - packageName:" + packageInfo.getPackageName()); if (packageInfo.getServiceId() != null) { sLogger.fine(" - serviceId:" + packageInfo.getServiceId()); } /* コミット */ db.setTransactionSuccessful(); } catch (SQLiteException e) { throw new RuntimeException(e); } finally { if (db != null) { db.endTransaction(); db.close(); } /* ClientManagerのDBオブジェクトをクリア設定 */ if (sqliteClientManager != null) { sqliteClientManager.setDb(null); } /* TokenManagerのDBオブジェクトをクリア設定 */ if (sqliteTokenManager != null) { sqliteTokenManager.setDb(null); } } } } /** * (10)アクセストークンからクライアントパッケージ情報を取得する. * * @param accessToken アクセストークン * @return not null: クライアントパッケージ情報 / null:アクセストークンがないのでクライアントパッケージ情報が取得できない。 */ public static ClientPackageInfo findClientPackageInfoByAccessToken(final String accessToken) { /* 引数チェック */ if (accessToken == null) { throw new IllegalArgumentException("accessToken is null."); } ClientPackageInfo clientPackageInfo = null; /* DBオープン */ SQLiteDatabase db = null; SQLiteClientManager sqliteClientManager = null; SQLiteTokenManager sqliteTokenManager = null; /* DBを同時アクセスさせない */ synchronized (sLockForDbAccess) { try { /* DBオープン */ db = sDbHelper.getReadableDatabase(); /* ClientManagerにDBオブジェクトを設定 */ sqliteClientManager = (SQLiteClientManager) sClientManager; sqliteClientManager.setDb(db); /* TokenManagerにDBオブジェクトを設定 */ sqliteTokenManager = (SQLiteTokenManager) sTokenManager; sqliteTokenManager.setDb(db); SQLiteToken token = (SQLiteToken) sqliteTokenManager.findTokenByAccessToken(accessToken); if (token != null) { String clientId = token.getClientId(); if (clientId != null) { Client client = sqliteClientManager.findById(clientId); if (client != null) { clientPackageInfo = new ClientPackageInfo(client.getPackageInfo(), clientId); } } } } finally { if (db != null) { db.close(); } /* ClientManagerのDBオブジェクトをクリア設定 */ if (sqliteClientManager != null) { sqliteClientManager.setDb(null); } /* TokenManagerのDBオブジェクトをクリア設定 */ if (sqliteTokenManager != null) { sqliteTokenManager.setDb(null); } } } return clientPackageInfo; } /** * (13)アクセストークン一覧Activityを表示. * @param context コンテキスト */ public static void startAccessTokenListActivity(final android.content.Context context) { Intent intent = new Intent(); intent.setClass(context, AccessTokenListActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } /** * (13)-1.アクセストークン一覧を取得する(startAccessTokenListActivity()用). * @return not null: アクセストークンの配列 / null: アクセストークンなし */ public static SQLiteToken[] getAccessTokens() { SQLiteToken[] tokens = null; SQLiteDatabase db = null; SQLiteTokenManager sqliteTokenManager = null; /* DBを同時アクセスさせない */ synchronized (sLockForDbAccess) { try { /* DBオープン */ db = sDbHelper.getReadableDatabase(); /* ClientManagerにDBオブジェクトを設定 */ sqliteTokenManager = (SQLiteTokenManager) sTokenManager; sqliteTokenManager.setDb(db); /* LocalOAuthが保持しているクライアントシークレットを取得 */ tokens = (SQLiteToken[]) sqliteTokenManager.findTokens(SampleUser.USERNAME); } catch (SQLiteException e) { throw new RuntimeException(e); } finally { if (db != null) { db.close(); } /* TokenManagerのDBオブジェクトをクリア設定 */ if (sqliteTokenManager != null) { sqliteTokenManager.setDb(null); } } } return tokens; } /** * クライアントに対応するトークンデータを取得する. * <p> * トークンが存在しない場合にはnullを返却する。 * </p> * @param client クライアントデータ * @return トークンデータ */ public static SQLiteToken getAccessToken(final Client client) { SQLiteDatabase db = null; SQLiteTokenManager sqliteTokenManager = null; /* DBを同時アクセスさせない */ synchronized (sLockForDbAccess) { try { /* DBオープン */ db = sDbHelper.getReadableDatabase(); /* ClientManagerにDBオブジェクトを設定 */ sqliteTokenManager = (SQLiteTokenManager) sTokenManager; sqliteTokenManager.setDb(db); /* LocalOAuthが保持しているクライアントシークレットを取得 */ return (SQLiteToken) sqliteTokenManager.findToken(client, SampleUser.USERNAME); } catch (SQLiteException e) { throw new RuntimeException(e); } finally { if (db != null) { db.close(); } /* TokenManagerのDBオブジェクトをクリア設定 */ if (sqliteTokenManager != null) { sqliteTokenManager.setDb(null); } } } } /** * (13)-2.アクセストークンを破棄して利用できないようにする(startAccessTokenListActivity()用. * * @param tokenId トークンID */ public static void destroyAccessToken(final long tokenId) { SQLiteDatabase db = null; SQLiteTokenManager sqliteTokenManager = null; /* DBを同時アクセスさせない */ synchronized (sLockForDbAccess) { try { /* DBオープン */ db = sDbHelper.getWritableDatabase(); db.beginTransaction(); /* TokenManagerにDBオブジェクトを設定 */ sqliteTokenManager = (SQLiteTokenManager) sTokenManager; sqliteTokenManager.setDb(db); sqliteTokenManager.revokeToken(tokenId); /* コミット */ db.setTransactionSuccessful(); } catch (SQLiteException e) { throw new RuntimeException(e); } finally { if (db != null) { db.endTransaction(); db.close(); } /* TokenManagerのDBオブジェクトをクリア設定 */ if (sqliteTokenManager != null) { sqliteTokenManager.setDb(null); } } } } /** * (13)-3.DBのトークンデータを削除(startAccessTokenListActivity()用. * */ public static void destroyAllAccessToken() { SQLiteDatabase db = null; SQLiteTokenManager sqliteTokenManager = null; /* DBを同時アクセスさせない */ synchronized (sLockForDbAccess) { try { /* DBオープン */ db = sDbHelper.getWritableDatabase(); db.beginTransaction(); /* TokenManagerにDBオブジェクトを設定 */ sqliteTokenManager = (SQLiteTokenManager) sTokenManager; sqliteTokenManager.setDb(db); sqliteTokenManager.revokeAllTokens(SampleUser.USERNAME); /* コミット */ db.setTransactionSuccessful(); } catch (SQLiteException e) { throw new RuntimeException(e); } finally { if (db != null) { db.endTransaction(); db.close(); } /* TokenManagerのDBオブジェクトをクリア設定 */ if (sqliteTokenManager != null) { sqliteTokenManager.setDb(null); } } } } /** * (13)-4.クライアントIDが一致するクライアントデータを取得する(startAccessTokenListActivity()用). * @param clientId クライアントID * @return not null: クライアント / null: クライアントなし */ public static SQLiteClient findClientByClientId(final String clientId) { SQLiteClient client = null; SQLiteDatabase db = null; SQLiteClientManager sqliteClientManager = null; /* DBを同時アクセスさせない */ synchronized (sLockForDbAccess) { try { /* DBオープン */ db = sDbHelper.getReadableDatabase(); /* ClientManagerにDBオブジェクトを設定 */ sqliteClientManager = (SQLiteClientManager) sClientManager; sqliteClientManager.setDb(db); /* LocalOAuthが保持しているクライアントシークレットを取得 */ client = (SQLiteClient) sqliteClientManager.findById(clientId); } catch (SQLiteException e) { throw new RuntimeException(e); } finally { if (db != null) { db.close(); } /* TokenManagerのDBオブジェクトをクリア設定 */ if (sqliteClientManager != null) { sqliteClientManager.setDb(null); } } } return client; } /** * 長時間使用されていなかったclientIdをクリーンアップする(DBアクセスしたついでに有効クライアント数も取得する). * @return 有効クライアント数 */ private static int cleanupClient() { int clientCount = 0; SQLiteDatabase db = null; SQLiteClientManager sqliteClientManager = null; /* DBを同時アクセスさせない */ synchronized (sLockForDbAccess) { try { /* DBオープン */ db = sDbHelper.getWritableDatabase(); db.beginTransaction(); /* ClientManagerにDBオブジェクトを設定 */ sqliteClientManager = (SQLiteClientManager) sClientManager; sqliteClientManager.setDb(db); sqliteClientManager.cleanupClient(LocalOAuth2Settings.CLIENT_CLEANUP_TIME); /* 有効クライアント数を取得する */ clientCount = sqliteClientManager.countClients(); /* コミット */ db.setTransactionSuccessful(); } catch (SQLiteException e) { throw new RuntimeException(e); } finally { if (db != null) { db.endTransaction(); db.close(); } /* ClientManagerのDBオブジェクトをクリア設定 */ if (sqliteClientManager != null) { sqliteClientManager.setDb(null); } } } return clientCount; } /** Handler of incoming messages from clients. */ private static class ApprovalHandler extends Handler { @Override public void handleMessage(final Message msg) { switch (msg.what) { /* 承認確認画面Activityから受け取ったメッセージを処理する */ case MSG_CONFIRM_APPROVAL: /* arg1:スレッドID / arg2:承認(=1) 拒否(=0) */ /* Messageからデータ取得 */ long threadId = (long) msg.arg1; boolean isApproval = false; /* true: 承認 / false: 拒否 */ if (msg.arg2 == ConfirmAuthActivity.APPROVAL) { isApproval = true; } /* 承認確認画面を表示する直前に保存しておいたパラメータデータを取得する(キューからは削除される) */ ConfirmAuthRequest request = dequeueRequest(threadId, true); if (request != null) { PublishAccessTokenListener publishAccessTokenListener = request.getPublishAccessTokenListener(); ConfirmAuthParams params = request.getConfirmAuthParams(); /* 許可された */ if (isApproval) { AccessTokenData accessTokenData = null; AuthorizationException exception = null; SQLiteDatabase db = null; SQLiteClientManager sqliteClientManager = null; SQLiteTokenManager sqliteTokenManager = null; /* DBを同時アクセスさせない */ synchronized (sLockForDbAccess) { try { /* DBオープン */ db = sDbHelper.getWritableDatabase(); db.beginTransaction(); /* ClientManagerにDBオブジェクトを設定 */ sqliteClientManager = (SQLiteClientManager) sClientManager; sqliteClientManager.setDb(db); /* TokenManagerにDBオブジェクトを設定 */ sqliteTokenManager = (SQLiteTokenManager) sTokenManager; sqliteTokenManager.setDb(db); /* アクセストークン発行する前に古い無効なトークン(クライアントIDが削除されて残っていたトークン)をクリーンアップする */ sqliteTokenManager.cleanup(); /* アクセストークン発行 */ accessTokenData = publishAccessToken(params); /* コミット */ db.setTransactionSuccessful(); } catch (AuthorizationException e) { exception = e; } catch (SQLiteException e) { exception = new AuthorizationException(AuthorizationException.SQLITE_ERROR); } finally { if (db != null) { db.endTransaction(); db.close(); } /* ClientManagerのDBオブジェクトをクリア設定 */ if (sqliteClientManager != null) { sqliteClientManager.setDb(null); } /* TokenManagerのDBオブジェクトをクリア設定 */ if (sqliteTokenManager != null) { sqliteTokenManager.setDb(null); } } } if (exception == null) { /* リスナーを通じてアクセストークンを返す */ callPublishAccessTokenListener(params, accessTokenData, publishAccessTokenListener); } else { /* リスナーを通じて発生した例外を返す */ callExceptionListener(params, exception, publishAccessTokenListener); } } else { /* 拒否された */ /* リスナーを通じて拒否通知を返す */ callPublishAccessTokenListener(params, null, publishAccessTokenListener); } /* キューにリクエストが残っていれば、次のキューを取得してActivityを起動する */ final ConfirmAuthRequest nextRequest = pickupRequest(); if (nextRequest != null) { postDelayed(new Runnable() { @Override public void run() { startConfirmAuthActivity(nextRequest); } }, 2000); } } break; case MSG_CONFIRM_CHECK_THREADID: /* Messageからデータ取得 */ long checkThreadId = (long) msg.arg1; /* キューにthreadIdが存在するか判定(1:true / 0:false) */ int result = 0; if (dequeueRequest(checkThreadId, false) != null) { result = 1; } /* Activityに例外発生を通知する(Activityを閉じる) */ sendMessageId(msg.replyTo, MSG_CHECK_THREADID_RESULT, result, null); break; default: super.handleMessage(msg); } } } /** * アクセストークンデータを返却する. * * @param params 承認確認画面のパラメータ * @return アクセストークンデータ(アクセストークン, 有効期間(アクセストークン発行時間から使用可能な時間。単位:ミリ秒) を返す。 * @throws AuthorizationException Authorization例外. */ private static AccessTokenData publishAccessToken(final ConfirmAuthParams params) throws AuthorizationException { String clientId = params.getClientId(); Client client = sClientManager.findById(clientId); if (client != null) { /* AuthSessionを登録してセッションIDを取得 */ RedirectRepresentation redirectRepresentation = callAuthorizationServerResource(client, true, null); if (redirectRepresentation != null) { String sessionId = redirectRepresentation.getOptions().get(RedirectRepresentation.SESSION_ID) .toString(); /* ログイン処理 */ ResultRepresentation result = (ResultRepresentation) callLoginPageServerResource( SampleUser.LOCALOAUTH_USER, SampleUser.LOCALOAUTH_PASS); if (result.getResult()) { RedirectRepresentation result3 = callAuthorizationServerResource(client, false, sessionId); if (result3 != null) { /* デバイスプラグインならxmlファイルに有効期限が存在すれば取得して使用する */ Map<String, DevicePluginXmlProfile> supportProfiles = null; if (params.isForDevicePlugin()) { supportProfiles = DevicePluginXmlUtil.getSupportProfiles(params.getContext(), params.getContext().getPackageName()); } /* スコープを登録(Scope型に変換して有効期限を追加する) */ String[] scopes = params.getScopes(); ArrayList<Scope> settingScopes = new ArrayList<Scope>(); for (String scope : scopes) { /* デバイスプラグインならxmlファイルに有効期限が存在すれば取得して使用する(無ければデフォルト値) */ long expirePeriod = LocalOAuth2Settings.DEFAULT_TOKEN_EXPIRE_PERIOD; if (supportProfiles != null) { DevicePluginXmlProfile xmlProfile = supportProfiles.get(scope); if (xmlProfile != null) { expirePeriod = xmlProfile.getExpirePeriod(); } } Scope s = new Scope(scope, System.currentTimeMillis(), expirePeriod); settingScopes.add(s); } /* 認可コード取得 */ String applicationName = params.getApplicationName(); RedirectRepresentation result4 = (RedirectRepresentation) LocalOAuth2Main .callAuthPageServerResource(sessionId, settingScopes, applicationName); if (result4 != null) { Map<String, Object> options = result4.getOptions(); String authCode = (String) options.get(AuthPageServerResource.CODE); if (authCode != null) { /* アクセストークン取得 */ String accessToken = callAccessTokenServerResource(client, authCode, applicationName); /* トークンデータを参照 */ Token token = sTokenManager.findTokenByAccessToken(accessToken); /* アクセストークンデータを返す */ AccessTokenScope[] accessTokenScopes = scopesToAccessTokenScopes(token.getScope()); AccessTokenData acccessTokenData = new AccessTokenData(accessToken, token.getRegistrationDate(), accessTokenScopes); return acccessTokenData; } } } } } } else { throw new AuthorizationException(AuthorizationException.CLIENT_NOT_FOUND); } return null; } /** * リスナーを通じてアクセストークンを返す. * * @param params 承認確認画面パラメータ * @param accessTokenData アクセストークンデータ * @param publishAccessTokenListener アクセストークン発行リスナー */ private static void callPublishAccessTokenListener(final ConfirmAuthParams params, final AccessTokenData accessTokenData, final PublishAccessTokenListener publishAccessTokenListener) { if (publishAccessTokenListener != null) { /* リスナーを実行してアクセストークンデータを返す */ publishAccessTokenListener.onReceiveAccessToken(accessTokenData); } else { /* リスナーが登録されていないので通知できない */ throw new RuntimeException("publishAccessTokenListener is null."); } } /** * リスナーを通じてアクセストークンを返す. * * @param params 承認確認画面パラメータ * @param exception 例外 * @param publishAccessTokenListener アクセストークン発行リスナー */ private static void callExceptionListener(final ConfirmAuthParams params, final Exception exception, final PublishAccessTokenListener publishAccessTokenListener) { if (publishAccessTokenListener != null) { /* リスナーを実行して例外データを返す */ publishAccessTokenListener.onReceiveException(exception); } else { /* リスナーが登録されていないので通知できない */ throw new RuntimeException("publishAccessTokenListener is null."); } } /** * AuthorizationServerResourceを実行. * * @param client クライアント情報 * @param initialize 初期化フラグ(trueにするとContextを初期化する) * @param sessionId セッションID(引き継ぐセッションIDが存在すれば指定する、無ければnullを設定する) * @return not null: RedirectRepresentation型の戻り値を返す / null: エラー * @throws AuthorizationException Authorization例外. */ private static RedirectRepresentation callAuthorizationServerResource(final Client client, final boolean initialize, final String sessionId) throws AuthorizationException { /* AuthorizationServerResourceを初期化する */ if (initialize) { Context context = new Context(getLogger()); AuthorizationServerResource.init(context); } /* request, responseを初期化 */ Request request = new Request(); request.setOriginalRef(new Reference(DUMMY_ORIGINALREF)); Response response = new Response(request); request.setResourceRef(new Reference(DUMMY_REFERENCE)); /* セッションIDが指定されていたらRequestに設定する */ if (sessionId != null) { Series<Cookie> cookies = new Series<Cookie>(Cookie.class); cookies.add(AuthorizationBaseServerResource.ClientCookieID, sessionId); request.setCookies(cookies); } AuthorizationServerResource.init(request, response, sClientManager, sTokenManager); /* Formに設定する */ Form paramsA = new Form(); paramsA.add(AuthorizationServerResource.CLIENT_ID, client.getClientId()); paramsA.add(AuthorizationServerResource.REDIR_URI, DUMMY_REDIRECTURI); paramsA.add(AuthorizationServerResource.RESPONSE_TYPE, "code"); paramsA.add(AuthorizationServerResource.SCOPE, DUMMY_SCOPE1); /* requestAuthorizationを実行する */ Representation representationA = null; try { representationA = AuthorizationServerResource.requestAuthorization(paramsA); } catch (OAuthException e) { throw new AuthorizationException(e); } /* 正常終了(ログイン画面リダイレクト) */ if (representationA instanceof RedirectRepresentation) { return (RedirectRepresentation) representationA; } return null; } /** * LoginPageServerResourceを実行する. * * @param userId ユーザーID * @param password パスワード * @return 戻り値(ResultRepresentation) */ private static ResultRepresentation callLoginPageServerResource(final String userId, final String password) { /* 前の処理からセッションIDを引き継ぐ */ String sessionId = AuthorizationServerResource.getSessionId(); Series<Cookie> cookies = new Series<Cookie>(Cookie.class); cookies.add(AuthorizationBaseServerResource.ClientCookieID, sessionId); /* (B)の処理 */ LoginPageServerResource.initResult(); Request request = new Request(); Reference requestReference = new Reference(DUMMY_ORIGINALREF); requestReference.addQueryParameter(LoginPageServerResource.USER_ID, userId); requestReference.addQueryParameter(LoginPageServerResource.PASSWORD, password); requestReference.addQueryParameter(LoginPageServerResource.CONTINUE, RedirectRepresentation.RedirectProc.requestAuthorization.toString()); /* QueryParameterとは別の変数に値を入れているので、直接値を設定する */ ArrayList<String> userIds = new ArrayList<String>(); userIds.add(userId); ArrayList<String> passwords = new ArrayList<String>(); passwords.add(password); ArrayList<String> continues = new ArrayList<String>(); continues.add(RedirectRepresentation.RedirectProc.requestAuthorization.toString()); LoginPageServerResource.getQuery().put(LoginPageServerResource.USER_ID, userIds); LoginPageServerResource.getQuery().put(LoginPageServerResource.PASSWORD, passwords); LoginPageServerResource.getQuery().put(LoginPageServerResource.CONTINUE, continues); request.setCookies(cookies); request.setOriginalRef(requestReference); request.setResourceRef(requestReference); Response response = new Response(request); LoginPageServerResource.init(request, response); ResultRepresentation resultRepresentation = null; try { resultRepresentation = (ResultRepresentation) LoginPageServerResource.getPage(); } catch (OAuthException e) { resultRepresentation = new ResultRepresentation(); resultRepresentation.setResult(false); resultRepresentation.setError(e.getMessage(), e.getErrorDescription()); } return resultRepresentation; } /** * AuthPageServerResourceを実行する. * * @param sessionId セッションID * @param scopes スコープ * @param applicationName アプリケーション名 * @return 戻り値(RedirectRepresentation) */ private static RedirectRepresentation callAuthPageServerResource(final String sessionId, final ArrayList<Scope> scopes, final String applicationName) { Series<Cookie> cookies = new Series<Cookie>(Cookie.class); cookies.add(AuthorizationBaseServerResource.ClientCookieID, sessionId); /* scopesを文字列配列に変換する */ ArrayList<String> strScopes = ScopeUtil.scopesToStrings(scopes); ArrayList<String> actions = new ArrayList<String>(); actions.add(AuthPageServerResource.ACTION_ACCEPT); ArrayList<String> applicationNames = new ArrayList<String>(); applicationNames.add(applicationName); AuthPageServerResource.getQuery().put(AuthPageServerResource.SCOPE, strScopes); AuthPageServerResource.getQuery().put(AuthPageServerResource.GRANTED_SCOPE, new ArrayList<String>()); AuthPageServerResource.getQuery().put(AuthPageServerResource.ACTION, actions); AuthPageServerResource.getQuery().put(AuthPageServerResource.APPLICATION_NAME, applicationNames); Request request = new Request(); request.setCookies(cookies); Response response = new Response(request); AuthPageServerResource.init(request, response); RedirectRepresentation redirectRepresentation = null; try { Representation representation = AuthPageServerResource.showPage(); if (representation != null) { if (representation instanceof RedirectRepresentation) { redirectRepresentation = (RedirectRepresentation) representation; } } } catch (OAuthException e) { e.printStackTrace(); } return redirectRepresentation; } /** * 認可コードを渡してアクセストークンを取得する. * * @param client クライアント * @param authCode 認可コード(TokenManager.sessionsに存在するキー値を設定する) * @param applicationName アプリケーション名 * @return not null: アクセストークン / null: アクセストークン取得失敗 */ private static String callAccessTokenServerResource(final Client client, final String authCode, final String applicationName) { /* Request / Response */ Request request = new Request(); ClientInfo clientInfo = new ClientInfo(); org.restlet.security.User user = new org.restlet.security.User(client.getClientId()); clientInfo.setUser(user); request.setClientInfo(clientInfo); Response response = new Response(request); AccessTokenServerResource.init(request, response); /* 入力値(アプリケーション名はbase64エンコードする) */ String base64ApplicationName = Base64.encodeToString(applicationName.getBytes(), Base64.URL_SAFE|Base64.NO_WRAP); StringRepresentation input = new StringRepresentation("grant_type=authorization_code&code=" + authCode + "&" + AccessTokenServerResource.REDIR_URI + "=" + DUMMY_REDIRECTURI + "&" + AccessTokenServerResource.APPLICATION_NAME + "=" + base64ApplicationName); /* 処理実行 */ try { ResultRepresentation resultRepresentation = (ResultRepresentation) AccessTokenServerResource .requestToken(input); if (resultRepresentation.getResult()) { String accessToken = resultRepresentation.getText(); return accessToken; } } catch (JSONException e) { e.printStackTrace(); } catch (OAuthException e) { e.printStackTrace(); } return null; } /** * ユーザーデータ追加. * * @param user ユーザーID * @param pass パスワード */ private static void addUserData(final String user, final String pass) { sUserManager.addUser(user).setPassword(pass.toCharArray()); } /** * クライアントデータ追加. * * @param packageInfo パッケージ名 * @return クライアントデータ */ private static Client addClientData(final PackageInfoOAuth packageInfo) { String[] redirectURIs = {DUMMY_REDIRECTURI}; Map<String, Object> params = new HashMap<String, Object>(); Client client = sClientManager.createClient(packageInfo, ClientType.CONFIDENTIAL, redirectURIs, params); return client; } /** * クライアントデータ削除. * * @param clientId クライアントID */ private static void removeClientData(final String clientId) { Client client = sClientManager.findById(clientId); if (client != null) { sClientManager.deleteClient(clientId); } } /** * 指定したクライアントに紐づくトークンデータを削除する. * @param clientId トークンデータ */ private static void removeTokenData(final String clientId) { Client client = sClientManager.findById(clientId); if (client != null) { sTokenManager.revokeToken(client); } } /** * 承認確認画面リクエストをキューに追加する. * * @param request リクエスト */ private static void enqueueRequest(final ConfirmAuthRequest request) { synchronized (sLockForRequestQueue) { sRequestQueue.add(request); } } /** * キュー先頭の承認確認画面リクエストをキューから取得する. * * @return not null: 取得したリクエスト / null: キューにデータなし */ private static ConfirmAuthRequest pickupRequest() { ConfirmAuthRequest request = null; synchronized (sLockForRequestQueue) { int requestCount = sRequestQueue.size(); if (requestCount > 0) { request = sRequestQueue.get(0); } } return request; } /** * threadIdが一致する承認確認画面リクエストをキューから取得する。(キューから削除することも可能). * * @param threadId キーとなるスレッドID * @param isDeleteRequest true: スレッドIDが一致したリクエストを返すと同時にキューから削除する。 / false: 削除しない。 * @return not null: 取り出されたリクエスト / null: 該当するデータなし(存在しないthreadIdが渡された、またはキューにデータ無し) */ private static ConfirmAuthRequest dequeueRequest(final long threadId, final boolean isDeleteRequest) { ConfirmAuthRequest request = null; synchronized (sLockForRequestQueue) { /* スレッドIDが一致するリクエストデータを検索する */ int requestCount = sRequestQueue.size(); for (int i = 0; i < requestCount; i++) { ConfirmAuthRequest req = sRequestQueue.get(i); if (req.getThreadId() == threadId) { if (isDeleteRequest) { /* スレッドIDに対応するリクエストデータを取得し、キューから削除する */ request = sRequestQueue.remove(i); } else { /* スレッドIDに対応するリクエストデータを取得 */ request = sRequestQueue.get(i); } break; } } } return request; } /** * クライアントデータ取得(なければAuthorizatonExceptionをスロー). * * @param confirmAuthParams パラメータ * @return クライアントデータ * @throws AuthorizationException Authorization例外. */ private static Client getClient(final ConfirmAuthParams confirmAuthParams) throws AuthorizationException { Client client = sClientManager.findById(confirmAuthParams.getClientId()); if (client == null) { throw new AuthorizationException(AuthorizationException.CLIENT_NOT_FOUND); } return client; } /** * Scope[]からAccessTokenScope[]に変換して返す. * @param scopes Scope[]の値 * @return AccessTokenScope[]の値 */ private static AccessTokenScope[] scopesToAccessTokenScopes(final Scope[] scopes) { if (scopes != null && scopes.length > 0) { AccessTokenScope[] accessTokenScopes = new AccessTokenScope[scopes.length]; for (int i = 0; i < scopes.length; i++) { Scope scope = scopes[i]; accessTokenScopes[i] = new AccessTokenScope(scope.getScope(), scope.getExpirePeriod()); } return accessTokenScopes; } return null; } /** * メッセージIDを通知する. * @param replyTo 通知するMessenger * @param messageId 通知するメッセージID * @param arg1 arg1(nullなら指定なし) * @param arg2 arg2(nullなら指定なし) */ private static void sendMessageId(final Messenger replyTo, final int messageId, final Integer arg1, final Integer arg2) { if (replyTo != null) { int iArg1 = (arg1 != null) ? arg1 : 0; int iArg2 = (arg2 != null) ? arg2 : 0; Message sendMsg = Message.obtain(null, messageId, iArg1, iArg2); sendMsg.replyTo = sMessenger; try { replyTo.send(sendMsg); } catch (RemoteException e) { e.printStackTrace(); } } } /** * リクエストデータを使ってアクセストークン発行承認確認画面を起動する. * @param request リクエストデータ */ private static void startConfirmAuthActivity(final ConfirmAuthRequest request) { android.content.Context context = request.getConfirmAuthParams().getContext(); long threadId = request.getThreadId(); ConfirmAuthParams params = request.getConfirmAuthParams(); String[] displayScopes = request.getDisplayScopes(); /* Activity起動(許可・拒否の結果は、ApprovalHandlerへ送られる) */ Intent intent = new Intent(); intent.setClass(params.getContext(), ConfirmAuthActivity.class); intent.putExtra(ConfirmAuthActivity.EXTRA_THREADID, threadId); if (params.getServiceId() != null) { intent.putExtra(ConfirmAuthActivity.EXTRA_DEVICEID, params.getServiceId()); } intent.putExtra(ConfirmAuthActivity.EXTRA_APPLICATIONNAME, params.getApplicationName()); intent.putExtra(ConfirmAuthActivity.EXTRA_SCOPES, params.getScopes()); intent.putExtra(ConfirmAuthActivity.EXTRA_DISPLAY_SCOPES, displayScopes); intent.putExtra(ConfirmAuthActivity.EXTRA_IS_FOR_DEVICEPLUGIN, params.isForDevicePlugin()); if (!params.isForDevicePlugin()) { intent.putExtra(ConfirmAuthActivity.EXTRA_PACKAGE_NAME, context.getPackageName()); intent.putExtra(ConfirmAuthActivity.EXTRA_KEYWORD, params.getKeyword()); } intent.setFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } }