概要
DataWedge には、DataWedge Intent API へのアクセスを制御し、承認されたアプリのみが DataWedge を構成できるようにするための管理者向けのオプションがあります。DataWedge Intent API はカテゴリ化されているため、管理者は、カテゴリに基づいて特定のアプリに DataWedge API へのアクセス権を付与できます。デフォルトでは、DataWedge は、既存のアプリケーションに影響しないように任意のインテント API を受け入れます。
以前は、デバイス上の任意のアプリケーションで DataWedge パラメータを構成し、DataWedge API を介して構成やステータス通知などの情報を受信できていました。このため、悪意のあるアプリケーションが他のアプリケーション用の DataWedge プロファイルを構成し、それを独自に利を得るために活用できるという潜在的なセキュリティ リスクがありました。たとえば、悪意のあるアプリが IP 出力プラグインの宛先 URI を変更して、データ配信を別のアドレスに転送できていました。
デバイスには、MX バージョン 10.1.1 以降が必要です。
DataWedge Intent API へのアクセスの保護に関するビデオ デモ:
アクセスの制御
DataWedge Intent API へのアクセスを制御するプロセス:
- MX の AccessMgr CSP を使用して、承認済みアプリをホワイトリストに登録します。
- インテント API カテゴリを指定して、MX の DataWedgeMgr CSP を介したインテント API アクセスを制限します。
- EMDK を使用して、AccessMgr からアプリ トークンを取得します。
- DataWedge Intent にトークンを含めて API にアクセスします。
詳細については、以降のセクションで説明します。
1.承認済みアプリをホワイトリストに登録
DataWedge 11.3 以降の場合:
DataWedge Intent API へのアクセスを制御する最初の手順では、管理者が、Zebra Device Manager (ZDM) から AccessMgr CSP を使用してアプリをホワイトリストに登録します。ホワイトリストを使用することで、リストに指定されているアプリのみを実行でき、承認済みのホワイトリストに登録されていないアプリの使用が制限されます。StageNow を使用して、アプリをホワイトリストに登録するための構成を展開します。以下のパラメータを設定します。
- 動作モード: "ホワイトリストなしのシングル ユーザー"
- サービス アクセス アクション: "発信者に呼び出しサービスを許可"
- サービス ID: <API カテゴリの委任範囲>
- 呼び出し元パッケージ名の指定: <Com.company.appname など、アプリのパッケージ名を入力>
- 呼び出し元の署名: <アプリ証明書を含む署名ファイルを選択>
各インテント API カテゴリの委任範囲:
| インテント API カテゴリ | 委任範囲 |
|---|---|
| クエリ API | delegation_scope_datawedge_query_api |
| ランタイム API | delegation_scope_datawedge_control_api |
| 通知 API | delegation_scope_datawedge_notification_api |
| 構成 API | delegation_scope_datawedge_config_api |
DataWedge 11.2 以前の場合:
DataWedge Intent API へのアクセスを制御する最初の手順では、管理者が、MX の AccessMgr CSP を使用してアプリをホワイトリストに登録します。ホワイトリストを使用することで、リストに指定されているアプリのみを実行でき、承認済みのホワイトリストに登録されていないアプリの使用が制限されます。StageNow または EMDK Profile Manager (Android または Xamarin) を使用して構成を展開し、アプリをホワイトリストに登録します。以下のパラメータを設定します。
- 動作モード: "ホワイトリストなしのシングル ユーザー"
- サービス アクセス アクション: "発信者に呼び出しサービスを許可"
- サービス ID: com.symbol.datawedge.api
- 呼び出し元パッケージ名の指定: <Com.company.appname など、アプリのパッケージ名を入力>
- 呼び出し元の署名: <アプリ証明書を含む署名ファイルを選択>
2.DataWedge Intent API を制限
デフォルトでは、すべての DataWedge API は非制御モードになっているため、どのアプリケーションもすべての DataWedge API にアクセスできます。管理者は、StageNow を介して DataWedgeMgr CSP を使用して DataWedge API カテゴリを制御/非制御モードにし、特定の API カテゴリへのアクセスを制限できます。DataWedgeMgr CSP の「インテント API へのアクセス制御」を参照してください。
注:
• DataWedge 8.1 の場合は、DataWedge 8.1.61 以降が必要です。
• DataWedge 8.2 の場合は、DataWedge 8.2.62 以降が必要です。
| インテント API カテゴリ | 制御モード |
|---|---|
| 構成 API | 非制御 制御 |
| 通知 API | 非制御 制御 |
| クエリ API | 非制御 制御 |
| ランタイム API | 非制御 制御 |
非制御 – すべてのアプリから API にアクセスできます。デフォルト設定です。
制御 – API アクセスを制限します。管理者のみが AccessMgr CSP を使用して、API にアクセスするアプリをホワイトリストに登録できます。
DataWedge API は、次の 4 つのタイプに分類されます。
- 構成 API - 構成で実行されるアクションに関連する API
- 通知 API - スキャナ、プロファイル、または構成のステータスの取得に関連する API
- クエリ API - 情報を取得したり、スキャナを列挙したりする API
- ランタイム API - 実行時に機能を変更できる API
管理者は、DataWedgeMgr CSP を使用して保護するカテゴリを指定できます。デフォルトでは、すべての API カテゴリは非保護モードになっています。
| インテント API カテゴリ | インテント API |
|---|---|
| 構成 API | 構成の取得 無効アプリ リストの取得 プロファイルの複製 プロファイルの作成 プロファイルの削除 プロファイル名の変更 構成の設定 無効なプロファイルを無視の設定 無効アプリ リストの設定 構成の復元 構成のインポート |
| 通知 API | プロファイルの切り替え (通知の登録/登録解除) スキャナ ステータス (通知の登録/登録解除) 構成変更 (通知の登録/登録解除) |
| クエリ API | スキャナの列挙 アクティブ プロファイルの取得 DataWedge ステータスの取得 バージョン情報の取得 スキャナ ステータスの取得 プロファイル リストの取得 無効なプロファイルを無視の取得 |
| ランタイム API | DataWedge の有効化/無効化 スキャナ入力プラグインの有効化/無効化 スキャナ パラメータの切り替え SimulScan パラメータの切り替え ソフト スキャン トリガ ソフト RFID トリガ プロファイルの切り替え デフォルト プロファイルの設定 デフォルト プロファイルのリセット スキャナの切り替え レポート オプションの設定 |
「DataWedge API」を参照してください。
3.トークンを取得
DataWedge 11.3 以降の場合:
ホワイトリストに登録されたアプリケーションのみが、DataWedge サービス ID/委任範囲を呼び出してトークンを生成できます。アプリケーションは、ZDM コンテンツ プロバイダを使用して、必要な委任を渡すことによってトークンを生成する必要があります。
トークンを生成するには:
Android マニフェストに次の権限を追加します。
ZDM 読み取り権限:
<uses-permission android:name="com.zebra.devicemanager.provider.READ_PERMISSION"/>照会権限:
<queries><package android:name="com.zebra.devicemanager"/></queries>
ZDM コンテンツ プロバイダにアクセスします。サンプル コード:
static final Uri AUTHORITY_URI = Uri.parse("content://com.zebra.devicemanager.zdmcontentprovider"); Uri ACQUIRE_TOKEN_URI = Uri.withAppendedPath(AUTHORITY_URI, "AcquireToken"); public static final String COLUMN_QUERY_RESULT = "query_result"; public String acquireToken(String delegation_scope) { String token = ""; try { Cursor cursor = mContext.getContentResolver().query(ACQUIRE_TOKEN_URI, (String[])null, "delegation_scope=?", new String[]{delegation_scope}, (String)null); if (cursor != null && cursor.getCount() > 0) { cursor.moveToFirst(); token = cursor.getString(cursor.getColumnIndex(COLUMN_QUERY_RESULT)); cursor.close(); } } catch (Exception var3) { if (var3 instanceof SecurityException) { Log.e("BroadcastProtection", "Invalid Token/Caller"); } else { Log.e("BroadcastProtection", "Unknown Caller to acquire token"); } }
}return token;
DataWedge 11.2 以前の場合:
ホワイトリストに登録されたアプリケーションのみが、DataWedge サービス ID を呼び出してトークンを生成できます。アプリケーションでは、EMDK ProfileManager API を使用して AccessMgr CSP からトークンを取得する必要があります。
トークンを生成するには、EMDK Profile Manager を使用してプロファイルを作成します。
- EMDK Profile Manager (Android または Xamarin) を開きます。
- MX 10.1 以降を使用して新しいプロファイルを作成し、RequestToken などの名前を指定します。
- [使用可能な機能] リストで、[Access Manager] を選択して機能として追加します。
- Access Manager が [選択された機能] の下に表示されます。
- Access Manager のパラメータ セクションで、以下を設定/入力します。
- サービス アクセス アクション: “Request Token”
- サービス ID: com.symbol.datawedge.api
EMDK Profile Manager で生成された EMDKConfig.xml のサンプル:
<?xml version="1.0" encoding="UTF-8"?><!--This is an auto generated document. Changes to this document may cause incorrect behavior.--><wap-provisioningdoc><characteristic type="ProfileInfo"><parm name="created_wizard_version" value="7.3.2"/></characteristic><characteristic type="Profile"><parm name="ProfileName" value="RequestToken"/><parm name="ModifiedDate" value="2020-03-23 20:01:24"/><parm name="TargetSystemVersion" value="9.1"/><characteristic type="AccessMgr" version="8.3"><parm name="emdk_name" value=""/><parm name="ServiceAccessAction" value="7"/><parm name="ServiceIdentifier" value="com.symbol.datawedge.api"/></characteristic></characteristic></wap-provisioningdoc>
EMDK の ProcessProfile API を使用して、安全にアクセスできるように、目的の DataWedge API インテントに渡されるトークンを生成します。トークンを生成するためのサンプル コードを以下に示します。
import android.content.Context; import android.os.AsyncTask; import android.text.TextUtils; import android.util.Xml; import com.symbol.emdk.EMDKManager; import com.symbol.emdk.EMDKResults; import com.symbol.emdk.ProfileManager; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.StringReader; public class TokenOperations implements EMDKManager.EMDKListener { static Object syncObj = new Object(); Context mContext; // Provides the error type for characteristic-error private String errorType = ""; // Provides the parm name for parm-error private String parmName = ""; // Provides error description private String errorDescription = ""; // Provides error string with type/name + description private String errorString = ""; private String mToken = ""; //Assign the profile name used in EMDKConfig.xml private String profileName = ""; //Declare a variable to store ProfileManager object private ProfileManager profileManager = null; //Declare a variable to store EMDKManager object private EMDKManager emdkManager = null; public TokenOperations(Context context) { this.mContext = context; } public String getErrorDescription() { return errorDescription; } public String generateToken() { EMDKResults results = EMDKManager.getEMDKManager(mContext, this); //Check the return status of EMDKManager object creation. if(results.statusCode == EMDKResults.STATUS_CODE.SUCCESS) { //EMDKManager object creation success }else { //EMDKManager object creation failed } try { synchronized (syncObj) { syncObj.wait(); } } catch (InterruptedException e) { e.printStackTrace(); } if (emdkManager != null) { emdkManager.release(); emdkManager = null; } return mToken; } @Override public void onOpened(EMDKManager emdkManager) { this.emdkManager = emdkManager; //Get the ProfileManager object to process the profiles profileManager = (ProfileManager) emdkManager.getInstance(EMDKManager.FEATURE_TYPE.PROFILE); profileName = "RequestToken"; new ProcessProfileTask().execute(""); } @Override public void onClosed() { //This callback will be issued when the EMDK closes unexpectedly. if (emdkManager != null) { emdkManager.release(); emdkManager = null; } } private void parseXML(XmlPullParser myParser) { int event; try { // Retrieve error details if parm-error/characteristic-error in the response XML event = myParser.getEventType(); while (event != XmlPullParser.END_DOCUMENT) { String name = myParser.getName(); switch (event) { case XmlPullParser.START_TAG: if (name.equals("parm-error")) { parmName = myParser.getAttributeValue(null, "name"); errorDescription = myParser.getAttributeValue(null, "desc"); errorString = " (Name: " + parmName + ", Error Description: " + errorDescription + ")"; return; } else if (name.equals("parm")) { String parmName = myParser.getAttributeValue(null, "name"); if(parmName.equalsIgnoreCase("ServiceAccessToken")) { //Retrieved Token will be saved to a variable mToken = myParser.getAttributeValue(null, "value"); return; } } break; case XmlPullParser.END_TAG: break; } event = myParser.next(); } } catch (Exception e) { e.printStackTrace(); } } private class ProcessProfileTask extends AsyncTask<String, Void, EMDKResults> { @Override protected EMDKResults doInBackground(String... params) { parmName = ""; errorDescription = ""; errorType = ""; mToken = ""; EMDKResults resultsReset = profileManager.processProfile(profileName, ProfileManager.PROFILE_FLAG.RESET, params); EMDKResults results = profileManager.processProfile(profileName, ProfileManager.PROFILE_FLAG.SET, params); return results; } @Override protected void onPostExecute(EMDKResults results) { super.onPostExecute(results); String resultString = ""; //Check the return status of processProfile if(results.statusCode == EMDKResults.STATUS_CODE.CHECK_XML) { // Get XML response as a String String statusXMLResponse = results.getStatusString(); try { // Create instance of XML Pull Parser to parse the response XmlPullParser parser = Xml.newPullParser(); // Provide the string response to the String Reader that reads // for the parser parser.setInput(new StringReader(statusXMLResponse)); // Call method to parse the response parseXML(parser); if (TextUtils.isEmpty(parmName) && TextUtils.isEmpty(errorType) && TextUtils.isEmpty(errorDescription) ) { resultString = "Profile update success."; if(!TextUtils.isEmpty(mToken)) { resultString = ""; } } else { resultString = "Profile update failed." + errorString; } synchronized (syncObj) { syncObj.notify(); } } catch (XmlPullParserException e) { resultString = e.getMessage(); } } } } }
4.API にアクセス
トークンが生成されたら、それを、DataWedge に送信する呼び出し元アプリケーションのパッケージ名とともにインテント API 呼び出しに含める必要があります。DataWedge でインテントを受信すると、そのトークンが特定のアプリケーションに対して有効かどうかが確認されます。トークンが有効な場合、DataWedge でそのインテントが処理されます。トークンが無効な場合は、以下のいずれかのエラー コードが発生します。
- メイン エラー コード:
APPLICATION_NOT_AUTHORIZED - サブ エラー コード:
TOKEN_EXPIRED: トークンの有効期限が切れています。アプリケーションは、新しいトークンを取得する必要があります。MX_NOT_INITIALIZED: MX が初期化されていません。これは通常、リブート直後に発生します。FAILED_TO_VERIFY_APPLICATION: 呼び出し元のアプリケーションを確認できません (トークンが無効である、アプリケーションがホワイトリストに登録されていないなど)。
以下の複数の API 用のソース コード サンプルが用意されています。
例 1: 構成の取得
Intent i = new Intent(); i.setAction("com.symbol.datawedge.api.ACTION"); i.setPackage("com.symbol.datawedge"); i.putExtra("APPLICATION_PACKAGE", getPackageName()); i.putExtra("TOKEN", "291f7f7c-1479-4813-9639-2d4ab31e37b9"); i.putExtra("com.symbol.datawedge.api.GET_CONFIG", "Profile0 (default)"); this.sendBroadcast(i);
例 2: DataWedge の有効化/無効化
Intent i = new Intent(); i.setAction("com.symbol.datawedge.api.ACTION"); i.setPackage("com.symbol.datawedge"); i.putExtra("APPLICATION_PACKAGE", getPackageName()); i.putExtra("TOKEN", "<token here>");); i.putExtra("SEND_RESULT","LAST_RESULT"); i.putExtra("com.symbol.datawedge.api.ENABLE_DATAWEDGE", true); sendBroadcast(i);
例 3: 構成の設定
Bundle bMain = new Bundle(); bMain.putString("PROFILE_NAME", "Profile009"); bMain.putString("PROFILE_ENABLED", "true"); bMain.putString("CONFIG_MODE", "CREATE_IF_NOT_EXIST"); Bundle bConfig = new Bundle(); bConfig.putString("PLUGIN_NAME","BARCODE"); bConfig.putString("RESET_CONFIG","true"); Bundle paramList = new Bundle(); paramList.putString("scanner_selection","auto"); paramList.putString("decoder_code11", "true"); bConfig.putBundle("PARAM_LIST", paramList); bMain.putBundle("PLUGIN_CONFIG", bConfig); Intent i = new Intent(); i.setAction("com.symbol.datawedge.api.ACTION"); i.setPackage("com.symbol.datawedge"); i.putExtra("APPLICATION_PACKAGE", getPackageName()); i.putExtra("TOKEN", "<token here>");); i.putExtra("com.symbol.datawedge.api.SET_CONFIG", bMain); i.putExtra("SEND_RESULT","LAST_RESULT"); i.putExtra("COMMAND_IDENTIFIER", "SET_CONFIG"); this.sendBroadcast(i);
使用上の注意
- DataWedge API が「制御」に設定されており、デバイスが再起動したときに、ホワイトリストに登録されたアプリケーションから「制御」グループにインテント API を送信すると、エラーが返される場合があります。MX フレームワークが初期化を完了しなかったため、リブート完了後しばらく待ってからインテントを送信して、このエラーを回避してください。
- トークンは、以下の状況で期限切れになります。
- デバイスの日付または時刻が、トークンのタイムスタンプより前に設定されている。
- トークンを生成してからデバイスのクロックで 24 時間が経過した。
- 自動時刻更新により、デバイスの時刻が戻ったか、24 時間を超えて進んだ。
- インテント API が保護モードになっている場合、DataWedge を使用する既存の Zebra アプリが期待どおりに機能しないことがあります。たとえば、ランタイム API が保護モードになっている場合、エンタープライズ キーボード バージョン 4.0 以前ではエンタープライズ キーボードのスキャン ボタンは機能しません。
- DataWedge 11.3 以降の場合:
- デバイスが MX と ZDM の両方を使用している場合、ZDM が優先オプションになります。
- ZDM がデバイスに存在する場合、EMDK プロファイル マネージャを介して MX を使用して取得したトークンは使用できません。
関連ガイド: