Murayama blog.

プログラミング教育なブログ

AndroidでParse入門 - Twitter Users -

元ネタ

こちらを参考にTwitter Usersについてまとめます。
https://parse.com/docs/android_guide#twitterusers

前回の記事はこちら。
AndroidでParse入門 - Facebook Users - - Murayama blog.

Twitter Users

ParseはFacebookと同様にTwitter認証機能を提供しています。Parse SDKを使えば、ParseUserにTwitterアカウントをリンクすることができます。わずか数行のコードで"Twitterでログイン"を実装できます。また、データをParse上に保存することができます。

Setup

ParseでTwitterを利用する手順を以下に示します。

  1. (まだ作成していないなら)Twitterアプリをセットアップします。
  2. TwitterアプリのコンシューマーキーをParseの設定画面で登録します。
  3. Twitterアプリで登録したコールバックURLに妥当なURLを入力します。このURLはiOSAndroidアプリケーション上では利用しませんが、Twitterの認証を有効にするために必要となります。
  4. Application.onCreate()メソッドにて、Parse SDKを初期化します。
ParseTwitterUtils.initialize("YOUR CONSUMER KEY", "YOUR CONSUMER SECRET");

Twitterに関連する部分で問題が発生した場合は、Twitterの公式ドキュメントを参照してください。

ParseユーザがTwitterを使うには2つの方法があります。(1)Twitterユーザとしてログインし、ParseUserを作成する方法、(2)既存のParseUserをTwitterユーザにリンクさせる方法の2つです。

Login & Signup

ParseTwitterUtilsは、Twitterによるサインアップ、ログイン処理を提供します。login()メソッドを使うと次のようになります。

ParseTwitterUtils.logIn(this, new LogInCallback() {
  @Override
  public void done(ParseUser user, ParseException err) {
    if (user == null) {
      Log.d("MyApp", "Uh oh. The user cancelled the Twitter login.");
    } else if (user.isNew()) {
      Log.d("MyApp", "User signed up and logged in through Twitter!");
    } else {
      Log.d("MyApp", "User logged in through Twitter!");
    }
  }
});

このコードを実行すると次のようになります。

  1. ユーザにTwitterのログインダイアログが表示されます。
  2. Twitterによって認証が行われ、アプリケーションはコールバックを受け取ります。
  3. Parse SDKTwitterのデータを受け取り、それをParseUsetに保存します。Twitterのハンドルに基づき、新規ユーザの場合はそのユーザが作成されます。
  4. そのユーザを引数にLogInCallbackが呼ばれます。

Twitterのログインダイアログ、アクティビティを表示するために、現在のContext(多くの場合、Activity内で呼ばれるのでthis)を指定しなければなりません。

Linking

既存のParseUserとTwitterアカウントをリンクするには、次のように実装します。

if (!ParseTwitterUtils.isLinked(user)) {
  ParseTwitterUtils.link(user, this, new SaveCallback() {
    @Override
    public void done(ParseException ex) {
      if (ParseTwitterUtils.isLinked(user)) {
        Log.d("MyApp", "Woohoo, user logged in with Twitter!");
      }
    }
  });
}

リンクはログイン処理と似ています。違うところはログイン成功時に、既存のParseUserオブジェクトがTwitterによって更新されていることです。Twitterでのログインは、既存のアカウントにユーザを関連づけることになります。

ユーザとTwitterのリンクを削除するには、次のように実装します。

ParseTwitterUtils.unlinkInBackground(user, new SaveCallback() {
  @Override
  public void done(ParseException ex) {
    if (ex == null) {
      Log.d("MyApp", "The user is no longer associated with their Twitter account.");
    }
  }
});

Twitter API Calls

Parse SDKは、TwitterとリンクしたParseUserによって、Twitter REST APIを呼び出す単純な方法を提供します。ParseTwitterUtilsは、シングルトンのTwitterオブジェクトを返すので、それを使ってリクエストを送ることができます。*1

HttpClient client = new DefaultHttpClient();
HttpGet verifyGet = new HttpGet(
        "https://api.twitter.com/1/account/verify_credentials.json");
ParseTwitterUtils.getTwitter().signRequest(verifyGet);
HttpResponse response = client.execute(verifyGet);

*1:サンプルのTwitterREST APIが古くなってるのでご注意を。2013年12月現在、1.1にしないとエラーになります

AndroidでParse入門 - Facebook Users -

元ネタ

こちらを参考にFacebook Usersについてまとめます。
https://parse.com/docs/android_guide#fbusers

前回の記事はこちら。
AndroidでParse入門 - Roles - - Murayama blog.

Facebook Users

Parseを使えば簡単にFacebookと連携することができます。Parse SDKの内部ではFacebook SDKを利用しているので、ParseUserクラスを使えばFacebookアカウントと簡単にリンクすることができます。

Facebook統合機能を使えば、ParseUserクラスとFacebookユーザを関連付けることができます。わずか数行のコードで、"Facebookでログイン"を実装できます。またデータをParse上に保存することができます。

Setup

ParseでFacebookを利用する手順を以下に示します。

  1. (まだ作成していないなら)Facebookアプリをセットアップします。
  2. FacebookアプリのApplication IDをParseの設定画面で登録します。
  3. Facebookの"getting started with the Facebook SDK"ガイドに従って、Facebook SDKとリンクしたアプリを作ります。ステップ6まで進んだなら、Facebook SDKプロジェクトとリンクした後ストップして、Facebook app IDを設定します。ログイン時にParse上のユーザとFacebookアカウントを関連付ける方法は、Parseのガイドに従います。
  4. Application.onCreate()メソッドにParse SDKを初期化する以下のコードを実装します。
ParseFacebookUtils.initialize("YOUR FACEBOOK APP ID");

FacebookAndroid SDKは、Facebookの公式Androidアプリをインストールしていると、デバイス上のログイン処理が拡張されます。Facebookアプリを通じて、デバイス上で認証を使ってFacebookへのダイレクトサインインができるようになります。公式アプリがインストールされていない場合、ダイアログベースの認証処理を実行します。Facebookはこの機能はシングルサインオンと呼んでいます。ActivityでonActivityResultメソッドをオーバーライドして、finishAuthentication()メソッドを呼び出す必要があります。

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  super.onActivityResult(requestCode, resultCode, data);
  ParseFacebookUtils.finishAuthentication(requestCode, resultCode, data);
}

既にonActivityResult()を実装している場合は、link()メソッドやlogIn()メソッドを使う際にrequestCodeの衝突を避けなければなりません。activityCodeパラメータを利用してコードがユニークになるように設定します。設定しない場合はデフォルトのactivityCodeが適用されます。

Facebookに関連する問題が発生したら、Facebook SDKの公式リソースにあるAndroidページを参照してみてください。

Parseは、Facebook SDK for Androidのv3.0に対応しています。

ParseユーザがFacebookを利用するには2つの方法があります。(1)ParseUserを生成してFacebookユーザとしてログインする、(2)既存のParseUserをFacebookにリンクする

Login & Signup

ParseFacebookUtilsを使えばParseUserをFacebookに対してログインしたり、サインアップしたりすることができます。logIn()メソッドを使う場合は次のようになります。

ParseFacebookUtils.logIn(this, new LogInCallback() {
  @Override
  public void done(ParseUser user, ParseException err) {
    if (user == null) {
      Log.d("MyApp", "Uh oh. The user cancelled the Facebook login.");
    } else if (user.isNew()) {
      Log.d("MyApp", "User signed up and logged in through Facebook!");
    } else {
      Log.d("MyApp", "User logged in through Facebook!");
    }
  }
});

このコードを実行すると次ようになります。

  1. FacebookログインダイアログかFacebookアプリによって生成されたプロンプトが表示されます。
  2. Facebookを使ってユーザ認証を行い、コールバックを受け付けます。
  3. Parse SDKFacebookデータを受け取り、ParseUserにそれを保存します。FacebookのIDに基づいて新しいユーザの場合は、そのユーザが生成されます。
  4. そのユーザを引数にLogInCallbackが呼ばれます。

FacebookのログインダイアログやActivityを表示するために、現在のActivity(Activity内でlogIn()を呼び出す場合はほとんどの場合はthis)を引数に指定する必要があります。

Facebookユーザに対してアプリの要求する読み込み権限を文字列のコレクションで指定することができます。文字列を独自で定義することもできますし、ParseFacebookUtils.Permissionsクラスによって定義することもできます。

ParseFacebookUtils.logIn(Arrays.asList("email", Permissions.Friends.ABOUT_ME),
        this, new LogInCallback() {
  @Override
  public void done(ParseUser user, ParseException err) {
    // Code to handle login.
  }
});

ParseUserとの統合に権限は必要ありません(nullやPermissionを指定しない場合はすべて許可されます)。ログインしたとき、ユーザはパーミッションを確認することになります。Parseの提供するドキュメントrequesting additional permissionsも参照してください。より詳細な情報が必要な場合は、Facebookデベロッパーガイドのpermission節を参照してください。

認証後、Facebookからどのようなデータを扱うかはアプリの要件次第です。詳しくはgraph query via Facebook's SDK.を参照してください。

Linking

既存のParseUserとFacebookアカウントを関連付けたい場合はlinkメソッドを使います。

if (!ParseFacebookUtils.isLinked(user)) {
  ParseFacebookUtils.link(user, this, new SaveCallback() {
    @Override
    public void done(ParseException ex) {
      if (ParseFacebookUtils.isLinked(user)) {
        Log.d("MyApp", "Woohoo, user logged in with Facebook!");
      }
    }
  });
}

ログインの処理とよく似たステップになります。ログインのときとの違いは、既存のParseUserがFacebookの情報で更新されていることです。Facebookでログインすることで、既存のアカウントに関連づけることができます。

Facebookとの関連を削除した場合は次のようになります。

ParseFacebookUtils.unlinkInBackground(user, new SaveCallback() {
  @Override
  public void done(ParseException ex) {
    if (ex == null) {
      Log.d("MyApp", "The user is no longer associated with their Facebook account.");
    }
  }
});

現在のスレッドで処理したいならunlinkメソッドを使ってください。

try {
  ParseFacebookUtils.unlink(user);
  Log.d("MyApp", "The user is no longer associated with their Facebook account.");
} catch (ParseException e) {
}

Requesting Permissions

Facebook SDK v3.0では、readとpublishパーミッションは個別にリクエストする必要があります。ParseFacebookUtils.logIn()メソッドとParseFacebookUtils.link()メソッドは、readパーミッションを許可するだけです。追加で—ミッションをリクエストするなら、ParseFacebookUtils.getSession().requestNewReadPermissions()メソッドや、ParseFacebookUtils.getSession().requestNewPublishPermissions()メソッドを使用してください。パーミッションのリクエストについて詳細な情報を知りたければ、Facebook's API documentation for these functionsを参照してください。

新たなパーミッションを取得できたら、ParseFacebookUtilities.saveLatestSessionData()メソッドを呼び出してください。そうすることで、ParseUserにセッショントークンの変更を保存し、ログインしているユーザにこのセッションデータが関連することが保証されます。*1

Facebook SDK and Parse

Facebook Android SDKは、Facebook APIに作用するたくさんのヘルパークラスを提供します。一般的に、ログインしたユーザとしてFacebookとやりとりするにはRequestクラスを使用します。詳しくはFacebook SDKのドキュメントを参照してください。

Parseのライブラリは、ユーザのセッションオブジェクトを管理します。ParseFacebookUtils.getSession()を呼べば、Requestに必要なセッションインスタンスにアクセスできます。

*1:みたいなことが書いてます。。

AndroidでParse入門 - Roles -

元ネタ

こちらを参考にRolesについてまとめます。
https://parse.com/docs/android_guide#roles

前回の記事はこちら。
AndroidでParse入門 - Users - - Murayama blog.

Roles

ユーザをベースにしたアプリの規模が大きくなってくると、ユーザ単位のACLより大きな粒度でアクセス管理をしたくなるかもしれません。このような要件に対して、Parseはロールベースのアクセス管理を提供しています。ロールは、Parse上のデータに対して、論理的なユーザグループ単位でのアクセス権を提供します。ロールは、ユーザオブジェクトや他のロールオブジェクトを包括したオブジェクトです。ロールに付与されている権限は、対象となるユーザに暗黙的に付与されます。同様にロール内に含まれるロールの権限も付与されます。

たとえば、コンテンツをキュレーションするようなアプリケーションにおいては、何人かのユーザは"Moderators"として、他のユーザの投稿したコンテンツを更新したり、削除したりといった役割が必要になるでしょう。また、"Administrators"という役割のユーザは、"Moderators"と同等の権限を保持し、さららにシステム設定を変更できるようにする必要があるでしょう。これらのロールにユーザを追加することで、新規のユーザに手動でリソースへの権限を付与することなく、"Moderators"や"Administrators"とすることができます。

Parseは、このようなロールオブジェクトを表現するためにParseRoleというクラスを提供しています。ParseRoleはParseObjectのサブクラスです。そのため、フレキシブルなスキーマ、自動永続化機能、Key-ValueインタフェースといったParseObjectと同等の機能を保持しています。ParseObjectのすべてのメソッドを利用することができます。ParseRoleクラスは、ロール管理に必要な特別なメソッドが追加されています。

Properties

ParseRoleにはいくつかのプロパティが追加されています。

  • name: ロールの名前です。この値は必須項目であり、ロールを生成するときに設定する必要があります。ロールの名前には、半角英数字、スペース、ハイフン(-)、アンダースコア(_)を利用できます。この名前は、objectIdを使わずにロールを識別する方法として使用できます。
  • users: 関連するユーザの集合です。ロールに含まれる権限が適用される対象となります。
  • roles: 関連するロールの集合です。ロールに含まれる権限が適用される対象となります。

Security for Role Objects

ParseRoleは、Parse上の他のオブジェクトと同等のセキュリティスキーム(ACL)を使います。ただし、明示的にACLを設定する必要があります。一般的に、非常に高い権限(マスターユーザや管理者のような)を持ったユーザだけがロールを作成したり、更新したりすべきでしょう。そのような方針に従ってACLを定義する必要があります。ユーザにParseRoleへの書き込み権限を与えれば、そのユーザは、ロールに他のユーザを追加したり、ロールを削除したりすることもできるということを覚えておいてください。

ParseRoleを作成してみましょう。次のようになります。

// By specifying no write privileges for the ACL, we can ensure the role cannot be altered.
ParseACL roleACL = new ParseACL();
roleACL.setPublicReadAccess(true);
ParseRole role = new ParseRole("Administrator", roleACL);
role.saveInBackground();

ユーザやロールを追加できます。ParseRoleのusersプロパティ、rolesプロパティを通じて権限を適用することができます。

ParseRole role = new ParseRole(roleName, roleACL);
for (ParseUser user : usersToAddToRole) {
  role.getUsers().add(user)
}
for (ParseRole childRole : rolesToAddToRole) {
  role.getRoles().add(childRole);
}
role.saveInBackground();

ロールを変更する権限を持つユーザによって変更されることがあるので、ACLを割り当てるときには細心の注意を払ってください。

Security for Other Objects

アプリケーションで利用するロールを作成できたので、ロールに対して、ユーザに付与する権限をACLとして定義することができます。個々のParseObjectにはParseACLを設定できます。ParseACLで、対象オブジェクトに対して、読み込み権限、書き込み権限を付与するロールやユーザを定義することができます。

オブジェクトに読み込み権限、書き込み権限を与えるのは簡単です。ParseRoleを使う場合は次のようになります。

ParseRole moderators = /* Query for some ParseRole */;
ParseObject wallPost = new ParseObject("WallPost");
ParseACL postACL = new ParseACL();
postACL.setRoleWriteAccess(moderators);
wallPost.setACL(postACL);
wallPost.saveInBackground();

ACLにロールの名前を指定することで、Roleの問い合わせを減らすことができます。

ParseObject wallPost = new ParseObject("WallPost");
ParseACL postACL = new ParseACL();
postACL.setRoleWriteAccess("Moderators", true);
wallPost.setACL(postACL);
wallPost.save();


ロールベースのParseACLもまた、アプリケーションのデフォルトACLとして使用できます。書き込み権限を持つユーザだけにデータを保護することができます。アプリケーションに"Moderators"ロールをデフォルトACLに適用するには次のようになります。

ParseACL defaultACL = new ParseACL();
// Everybody can read objects created by this user
defaultACL.setPublicReadAccess(true);
// Moderators can also modify these objects
defaultACL.setRoleWriteAccess("Moderators");
// And the user can read and modify its own objects
ParseACL.setDefaultACL(defaultACL, true);

Role Hierarchy

ここまでで見てきたように、1つのロールに他のロールを含むことができます。つまり、2つのロールに親子関係を定義することができます。この結果、親のロールに付与された権限は、暗黙的にすべての子ロールに引き継がれることになります。

このような関連は、フォーラムのようなユーザコンテンツを管理するアプリケーションでよく見られます。ユーザのサブセットとして"Administrators"を定義し、"Administrators"には、アプリケーションの設定を調整したり、新しいフォーラムを作成したり、グローバルメッセージを設定したりといったハイレベルなアクセス制御を実装します。他のユーザのサブセットとして"Moderators"を定義し、コンテンツの品質を適切に保つための役割を定義します。ここで、"Administrator"の権限を持つユーザはまた"Moderator"の権限も持つべきでしょう。このような関連を定義するには、"Moderators"ロールの子ロールとして "Administrators"を作成します。次のようになるでしょう。

ParseRole administrators = /* Your "Administrators" role */;
ParseRole moderators = /* Your "Moderators" role */;
moderators.getRoles().add(administrators);
moderators.saveInBackground();

AndroidでParse入門 - Users -

元ネタ

こちらを参考にUsersについてまとめます。
https://parse.com/docs/android_guide#users

前回の記事はこちら。
AndroidでParse入門 - Analytics - - Murayama blog.

Users

多くのアプリは、セキュアな方法でユーザアカウント管理する必要があります。ParseはParseUserという特別なユーザクラスを提供しています。これにより、ユーザーアカウント管理に必要な多くの機能が自動的に施されることになります。

このクラスを使用することで、あなたのアプリにユーザアカウント管理機能を追加することができます。

ParseUserクラスはParseObjectクラスのサブクラスです。そのため、フレキシブルなスキーマ、自動永続化、Key-ValueインタフェースといったParseObjectの機能を継承しています。ParseObjectクラスのすべてのメソッドはParseUserクラスにも存在しています。ParseUserクラスには、ユーザアカウント管理のための特別な機能が追加されているのです。

Properties

ParseUserクラスにはいくつかのプロパティが追加されています。

  • username: ユーザの名前です(必須)
  • password: ユーザのパスワードです(必須)
  • email: ユーザのメールアドレスです(任意)

これらは、ユーザ管理の様々なユースケースで必要になるものです。usernameやemailはsetterメソッドでセットします。putメソッドを使う必要がないことに気をつけてください。

Signing Up

アプリで最初にやることはサインアップ処理でしょう。次のコードは典型的なサインアップ処理を示しています。

ParseUser user = new ParseUser();
user.setUsername("my name");
user.setPassword("my pass");
user.setEmail("email@example.com");
 
// other fields can be set just like with ParseObject
user.put("phone", "650-253-0000");
 
user.signUpInBackground(new SignUpCallback() {
  public void done(ParseException e) {
    if (e == null) {
      // Hooray! Let them use the app now.
    } else {
      // Sign up didn't succeed. Look at the ParseException
      // to figure out what went wrong
    }
  }
});

この呼び出しは、非同期でParseアプリ内に新しいユーザを生成します。また、usernameとemailがユニークであるかというチェックも働きます。クラウド上にはパスワードのハッシュ値のみが格納されます。パスワードを平文でやりとりすることはありませんし、パスワードを平文でクライアントに送ることもありません。

signUpInBackgroundメソッドを使用したことに注目してください。saveInBackgroundメソッドは使用していません。新規のParseUserは、signUpInBackground(あるいはsignUp)メソッドによって生成されます。その後の更新処理には、saveメソッドを利用することができます。

サインアップ処理は、signUpInBackgroundメソッド以外にも種類があります。エラーハンドリングを受け取ったり、同期処理したりといったものです。通常は、可能な限り非同期処理で行うことを推奨しています。なぜなら、アプリのUIスレッドをロックしないからです。メソッドの使い方についての詳細を知りたい場合は、API docsを参照してください。

サインアップ処理に失敗した場合は、エラーオブジェクトを調べます。よくあるケースは、usernameやemailが他のユーザと重複するケースです。このような場合はユーザに異なるユーザ名の入力を促すようにします。

メールアドレスをユーザ名として使うことも可能です。ユーザにはメールアドレスの入力を依頼します。その際、usernameプロパティを利用するようにします。このようなケースでもParseUserは問題なく動作します。リセットパスワードのセクションで、どのように処理されるかがわかるでしょう。

Logging In

サインアップしたユーザは、自身のアカウントを使ってログインすることができます。次のようにlogInInBackgroundメソッドを使用します。

ParseUser.logInInBackground("Jerry", "showmethemoney", new LogInCallback() {
  public void done(ParseUser user, ParseException e) {
    if (user != null) {
      // Hooray! The user is logged in.
    } else {
      // Signup failed. Look at the ParseException to see what happened.
    }
  }
});

Verifying Emails

アプリケーションの設定でメールアドレスの検証を有効にすると、メールアドレスの検証処理を追加できます。メールアドレスを検証するには、ParseUserオブジェクトにemailVerifiedキーを追加します。ParseUserのメールアドレスが変更されると、emailVerifiedにはfalseが設定されます。Parseは、メールアドレスとユーザがリンクするとemailVerifiedにtrueを設定します。

emailVerifiedには3つの状態が存在します。

  • true - Parseから送信されるメールによって、メールアドレスを確認できた場合。 ParseUserオブジェクトはアカウント生成後すぐ、trueとなるわけではありません。
  • false - 最後にフェッチされた時に、メールアドレスの確認がされていない場合。emailVerifiedがfalseの場合、ParseUserオブジェクトのfetch()

呼び出しを考えてみてください。

  • missing - メールアドレスの検証がOFFのときに生成されたParseUserか、メールアドレスを保持しないPerseUserです。

Current User

アプリケーションの至る所でユーザーのログを残すのは面倒です。キャッシュされたcurrentUserオブジェクトを使うことでこの問題を解消できます。

signupやloginメソッドを利用すると、ユーザはディスクにキャッシュされます。これにより、セッションとしてこのキャッシュを利用するができますし、ユーザがログインしていると見なすこともできます。

ParseUser currentUser = ParseUser.getCurrentUser();
if (currentUser != null) {
  // do stuff with the user
} else {
  // show the signup or login screen
}

ログアウトすれば、現行ユーザはクリアされます。

ParseUser.logOut();
ParseUser currentUser = ParseUser.getCurrentUser(); // this will now be null

Anonymous Users

個々のユーザにデータやオブジェクトを関連づけることは大事ですが、時には、ユーザ名やパスワードを使用せずにユーザを扱いたいこともあるでしょう。Anonymousユーザは、ユーザ名やパスワードなしで生成できるユーザです。他のParseUserと同じように振る舞うことができます。Anonymousユーザはログアウトする破棄されるので、そのデータにはアクセスできなくなります。ParseAnonymousUtilsを使うとAnonymousユーザを生成できます。

ParseAnonymousUtils.logIn(new LogInCallback() {
  @Override
  public void done(ParseUser user, ParseException e) {
    if (e != null) {
      Log.d("MyApp", "Anonymous login failed.");
    } else {
      Log.d("MyApp", "Anonymous user logged in.");
    }
  }
});

Anonymousユーザは、signUpメソッドを使用してユーザ名とパスワードを設定することで、通常のユーザに変更することもできます。他にもFacebookTwitterのようなサービスと関連づけることもできます。変更後のユーザーであっても、それまで保持していたデータを継続して利用することができます。

if (ParseAnonymousUtils.isLinked(ParseUser.getCurrentUser())) {
  enableSignUpButton();
} else {
  enableLogOutButton();
}

Anonymousユーザは、ネットワーク要求なしで生成できるので、アプリケーションを起動してすぐに利用することができます。アプリケーション起動時に、自動Anonymousユーザ生成を有効にすると、ParseUser.getCurrentUser()メソッドはnullを返さないようになります。ユーザは、ユーザオブジェクトや関連のあるオブジェクトが保存されたときに、初めてクラウド上に生成されます。それまでobjectIdはnullとなっています。自動Anonymousユーザ生成を有効にすれば、簡単にデータを関連づけることができます。たとえば、Application.onCreate()メソッドの中で次のように実装します。

ParseUser.enableAutomaticUser();
ParseUser.getCurrentUser().increment("RunCount");
ParseUser.getCurrentUser().saveInBackground();

Security For User Objects

ParseUserクラスはデフォルトでセキュアな実装となっています。ParseUser上に保存されたデータは、ユーザ本人にしか更新できません。また、デフォルトでは他のユーザから読み取ることもできません。そして、認証済みのParseUserオブジェクトの場合は、他のユーザからは読み込みのみ許可しますが、本人であれば更新することも可能です。

具体的には、loginやsignUpメソッドのような認証メソッドによって取得したParseUserオブジェクトの場合を除いて、saveメソッドやdeleteメソッドを呼び出すことはできません。これにより、ユーザ本人だけが自身のデータを更新できるようになっています。

以下はこのセキュリティポリシーを示しています。

ParseUser user = ParseUser.logIn("my_username", "my_password");
user.setUsername("my_new_username"); // attempt to change username
user.saveInBackground(); // This succeeds, since the user was authenticated on the device
 
// Get the user from a non-authenticated manner
ParseQuery<ParseUser> query = ParseUser.getQuery();
query.getInBackground(user.getObjectId(), new GetCallback<ParseUser>() {
  public void done(ParseUser object, ParseException e) {
    object.setUsername("another_username");
 
    // This will throw an exception, since the ParseUser is not authenticated
    object.saveInBackground();
  }
});

ParseUserが認証済みかどうか確認したいなら、isAuthenticated()メソッドを利用します。メソッドによって取得したParseUserは、常に認証済みのものが返ります。認証メソッドを通じて取得したParseUserオブジェクトについては、isAuthenticated()メソッドを呼び出す必要はありません。

Security for Other Objects

ParseUserと同等のセキュリティモデルが他のオブジェクトにも適用されています。あるオブジェクトに対して、特定のユーザだけに読み込みを許可したり、書き込みを許可したり制御することができます。このようなセキュリティをサポートするために個々のオブジェクトはACL(Access Control List)を保持しています。ACLはParseACLクラスによって実装されています。

ParseACLを使う最もシンプルな方法は、あるオブジェクトの読み込み・書き込みをシングルユーザに限定する方法です。このようなオブジェクトを作成するには、まずParseUserとしてログインしておく必要があります。そして、new ParseACL(user)によってParseACLオブジェクトを生成します。結果として、このオブジェクトは特定のユーザだけによる限定的なアクセスが可能となります。オブジェクトのACLは、他のプロパティと同様にオブジェクトの保存時に更新されます。たとえば、プライベートなメモ(PraivateNote)へのアクセスを限定する場合は次のようになります。

ParseObject privateNote = new ParseObject("Note");
privateNote.put("content", "This note is private!");
privateNote.setACL(new ParseACL(ParseUser.getCurrentUser()));
privateNote.saveInBackground();

このノートは、現在のユーザのみアクセスできます。また、他のデバイスでサインインしてもアクセス可能となります。これは複数のデバイスで横断的にユーザ情報を扱う事ができるので役に立ちます。たとえば、TODOリストの管理などに適しているでしょう。

パーミッションは、ユーザ単位で付与することができます。パーミッションを指定するにはParseACLのsetReadAccess、setWriteAccessを使用します。たとえば、あなたは伝えたいメッセージを、何人かのユーザグループに送りたい場合、対象のユーザ一人ひとりに読み込み・削除権限を付与します。

arseObject groupMessage = new ParseObject("Message");
ParseACL groupACL = new ParseACL();
     
// userList is an Iterable<ParseUser> with the users we are sending this message to.
for (ParseUser user : userList) {
  groupACL.setReadAccess(user, true);
  groupACL.setWriteAccess(user, true);  
}
 
groupMessage.setACL(groupACL);
groupMessage.saveInBackground();

すべてのユーザに一括でパーミッションを与えたいなら、setPublicReadAccess、setPublicWriteAccessメソッドを使用します。これは、メッセージボードへのコメントを投稿するようなパターンを可能とします。たとえば、投稿データの更新は本人のみに限定し、他のユーザには閲覧のみを許可する場合は次のようになります。

ParseObject publicPost = new ParseObject("Post");
ParseACL postACL = new ParseACL(ParseUser.getCurrentUser());
postACL.setPublicReadAccess(true);
publicPost.setACL(postACL);
publicPost.saveInBackground();

ユーザのデータがデフォルトでセキュアなものとするために、生成されたすべてのParseObjectに対してACLを施すこともできます。

ParseACL.setDefaultACL(defaultACL, true);

上記のコードの第2引数は、現在のユーザに対して、オブジェクト生成時の読み書きを許可するかどうかを設定します。この設定をしなければ、ユーザがログインしたり、ログアウトしたりするたびに、デフォルトACLをリセットする必要があります。そうすることで現在のユーザに対して適切なアクセス権が付与されます。この設定をすれば、ユーザが明示的に異なるアクセス権を付与するまで現在のユーザの変更を無視することができます。*1

デフォルトACLは、アクセスパターンに従うことでアプリを作成することが簡単になります。Twitterのようなアプリケーションの場合、ユーザのコンテンツは読み込みを公開します。次の様なACLになるでしょう。

ParseACL defaultACL = new ParseACL();
defaultACL.setPublicReadAccess(true);
ParseACL.setDefaultACL(defaultACL, true);

Dropboxのようなアプリケーションでは、ユーザが明示的に権限を設定をしない限り、ユーザ本人にのみアクセスを許可すべきでしょう。デフォルトACLを使用することで、現在のユーザにのみアクセスを許可することができます。

ParseACL.setDefaultACL(new ParseACL(), true);

Parseにデータを記録するが、ユーザにそのデータへのアクセスを提供しないアプリケーションでは、限定的なACLを提供することで、現在のユーザのアクセスを拒否することができます。

ParseACL.setDefaultACL(new ParseACL(), false);

書き込み権限を持たずにオブジェクトを削除するような禁止される操作は、ParseException.OBJECT_NOT_FOUNDという結果になります。
セキュリティの観点では、IDが存在しないのか、それともアクセス権がないのか、ユーザにエラーの原因を特定させないことは大切です。

Resetting Passwords

システムに登録したパスワードを忘れてしまうのはよくあることでしょう。Parseのライブラリは、パスワードをリセットするセキュアな方法を提供しています。

パスワードをリセットする手順を見てみましょう。まずはユーザのメールアドレスを確認します。次のようになるでしょう。

ParseUser.requestPasswordResetInBackground("myemail@example.com",
                                           new RequestPasswordResetCallback() {
  public void done(ParseException e) {
    if (e == null) {
      // An email was successfully sent with reset instructions.
    } else {
      // Something went wrong. Look at the ParseException to see what's up.
    }
  }
});

入力されたメールアドレスと、emailフィールド、あるいはusernameフィールドにマッチするユーザを探します。該当したユーザには、パスワードリセット通知がメールで届きます。このように、ユーザのユーザ名(username)としてメールアドレスを使うのか、メールアドレスはemailフィールドとして別で管理するのかを選択することができます。

パスワードリセットの手順は以下のとおりです。

  1. ユーザは、パスワードのリセット通知をメールで要求します。
  2. Parseは、指定されたアドレスにメールを送ります。そのメールにはリセット用のリンクが記載されています。
  3. ユーザがリセットリンクをクリックすると、Parseの特別なページにつながります。そこで新しいパスワードを設定することができます。
  4. ユーザは新しいパスワードを入力します。パスワードは入力された値でリセットされます。

注意:メールの本文の中では、アプリケーション生成時に指定したアプリケーション名が引用されます。

Querying

ユーザ用のクエリは、専用のクエリを使う必要があります。

ParseQuery<ParseUser> query = ParseUser.getQuery();
query.whereEqualTo("gender", "female");
query.findInBackground(new FindCallback<ParseUser>() {
  public void done(List<ParseUser> objects, ParseException e) {
    if (e == null) {
        // The query was successful.
    } else {
        // Something went wrong.
    }
  }
});

ID指定でParseUserを取得したい場合はget()メソッドを使用します。

Associations

ParseUserに関連を持たせることができます。たとえば、ブログアプリケーションを作る場合を見てみましょう。ユーザの新しい投稿を保存し、すべての記事を取得する場合は次のようになります。

ParseUser user = ParseUser.getCurrentUser();
 
// Make a new post
ParseObject post = new ParseObject("Post");
post.put("title", "My New Post");
post.put("body", "This is some great content.");
post.put("user", user);
post.saveInBackground();
 
// Find all posts by the current user
ParseQuery<ParseObject> query = ParseQuery.getQuery("Post");
query.whereEqualTo("user", user);
query.findInBackground(new FindCallback<ParseObject>() { ... });

Users in the Data Browser

ユーザクラスはParseUserオブジェクトを格納する専用のクラスです。データブラウザで確認すると、小さな人形のアイコンが表示されるようになっています。

*1:この辺は訳が怪しいので原文を読まれた方が良いです。。

AndroidでParse入門 - Analytics -

元ネタ

こちらを参考にAnalyticsについてまとめます。
https://parse.com/docs/android_guide#analytics


前回の記事はこちら。
AndroidでParse入門 - Files - - Murayama blog.

Analytics

Parseはあなたのアプリが刻々と動く中で、その状況を取得するフックを多数用意しています。アプリがいつ、どのような頻度で利用されているかを知る事は重要です。

このセクションでは、Parseのアナリティクスバックエンドを利用して、あなたのアプリを診断する方法を紹介します。開発者はParseを使ってデータを保存したり取得したりすることで、Parseの評価指標を利用することができます。

クライアントサイドロジックを実装することなく、ダッシュボード上で、APIリクエストのリアルタイムグラフとその内訳(デバイスタイプ、Parseクラス名、REST verb)を表示することができます。興味のあるデータに素早くアクセスするためのグラフフィルターを保存することもできます。

App-Open / Push Analytics

最初のアナリティクスフックは、アプリケーションの起動をトラックするものです。メインアクティビティのonCreateメソッドに次の1行を追加します。あなたのアプリが起動されるたびに、データが収集されるでしょう。

ParseAnalytics.trackAppOpened(getIntent());

統計グラフの内訳は、アプリケーションのダッシュボード上で確認できます。

さらに、アナリティクスでは、プッシュノーティフィケーションの通知と開封率を知ることもできます。ノーティフィケーションペイロードやプッシュ関連のコールバックについて、詳しくはPushガイドのTracking Pushes and App Opens subsectionを参照してください。

Custom Analytics

ParseAnalyticsは、フリーフォームのイベントトラッキングもサポートしています。これには文字列のKey、Valueを利用できます。これらの追加ディメンションは、ダッシュボード上で、カスタムイベントのセグメンテーションとして利用できます。

あなたのアプリが、アパートのリストのための検索機能を提供しているなら、メタデータを追加して、その機能がどのような頻度で利用されているか知りたいでしょう。

Map<String, String> dimensions = new HashMap<String, String>();
// Define ranges to bucket data points into meaningful segments
dimensions.put("priceRange", "1000-1500");
// Did the user filter the query?
dimensions.put("source", "craigslist");
// Do searches happen more often on weekdays or weekends?
dimensions.put("dayType", "weekday");
// Send the dimensions to Parse along with the 'search' event
ParseAnalytics.trackEvent("search", dimensions);

ParseAnalyticsは、軽量のエラートラッカーとして利用することもできます。次のようにすると、エラーの発生頻度を確認したり、エラーコードで内訳を確認したりすることができます。

Map<String, String> dimensions = new HashMap<String, String>();
dimensions.put('code', Integer.toString(error.getCode()));
ParseAnalytics.trackEvent('error', dimensions);

現状のParseAnalytics.trackEvent()呼び出しにおいては、最大8ディメンションしか保存できないので注意してください。

AndroidでParse入門 - Files -

元ネタ

こちらを参考にFilesについてまとめます。
https://parse.com/docs/android_guide#files

前回の記事はこちら。
AndroidでParse入門 - Subclasses - - Murayama blog.

The ParseFile

ParseFileを使えば、PerseObjectには収まり切らないファイルであってもクラウド上に保存することができます。画像ファイルはその代表的なものですが、ドキュメント、ビデオ、ミュージックファイル、その他のバイナリデータも該当します(最大10MBまで)。

ParseFileを取得するのは簡単です。byte[]でデータを用意して、それからParseFileを作成します。例えばStringデータを格納する場合は次のようになります。

byte[] data = "Working at Parse is great!".getBytes();
ParseFile file = new ParseFile("resume.txt", data);

ここではファイル名をresume.txtとしています。気をつけることが2つあります。

  • ファイル名は衝突しません。個々のファイルには、アップロード時にユニークな識別子が割り振られます。そのため、resume.txtを複数回アップロードしても問題は発生しません。
  • ファイル名に含まれる拡張子は重要です。Parseは、拡張子をもとにファイルをどう扱うべきか決定します。たとえば、PNGイメージを保存するのであれば拡張しは.pngとするようにしてください。


次に、クラウド上に保存する方法を見てみましょう。ParseObjcectの場合と同様に、いくつかの種類のsaveメソッドが提供されています。また、コールバックやエラーハンドリングを組み合わせて使うことができます。

file.saveInBackground();


保存が完了すると、他のデータと同じようにParseFileをPerseObjectに関連付けることができます。

ParseObject jobApplication = new ParseObject("JobApplication");
jobApplication.put("applicantName", "Joe Smith");
jobApplication.put("applicantResumeFile", file);
jobApplication.saveInBackground();


getData関連のメソッドを呼び出せば、データを取り出すことができます。次の例では、他のJobApplicationオブジェクトからファイルを取り出します。

ParseFile applicantResume = (ParseFile)anotherApplication.get("applicantResumeFile");
applicantResume.getDataInBackground(new GetDataCallback() {
  public void done(byte[] data, ParseException e) {
    if (e == null) {
      // data has the bytes for the resume
    } else {
      // something went wrong
    }
  }
});

ParseObjectの場合と同様、バックグラウンドで取得するgetDataメソッドを使うことが多いでしょう。

Progress

ParseFileは、ファイルのアップロード、ダウンロードの進捗を簡単に表示できます。これには、saveInBackgroundメソッドやgetDataInBackgroundメソッドの引数にProgressCallbackを渡します。例を観てみましょう。

byte[] data = "Working at Parse is great!".getBytes();
ParseFile file = new ParseFile("resume.txt", data);
 
file.saveInBackground(new SaveCallback() {
  public void done(ParseException e) {
    // Handle success or failure here ...
  }
}, new ProgressCallback() {
  public void done(Integer percentDone) {
    // Update your progress spinner here. percentDone will be between 0 and 100.
  }
});

REST APIを使えば、オブジェクトによって参照されているファイルを削除することができます。ファイルの削除を許可するためにはマスターキーが必要になります。

ファイルがアプリ内のどのオブジェクトからも参照されていない場合は、REST APIを使って削除することはできません。ただし、アプリの設定ページ(Setting page)で、未使用のファイルを削除することができます。URLプロパティを介した場合、参照されていないファイルへのアクセスができないことに気をつけてください。設定ページでの削除は、オブジェクトに関連付けられているファイルに影響はありません。

AndroidでParse入門 - Subclasses -

元ネタ

こちらを参考にSubclassesについてまとめます。
https://parse.com/docs/android_guide#subclasses

前回の記事はこちら。
AndroidでParse入門 - Queries - - Murayama blog.

Subclasses

Parseは、なるべく速く動かせるように設計されています。ParseObjectとget()メソッドを使えば、すべてのデータにアクセスすることができます。一方、熟練したコードベースにおいては、サブクラス化を行うことで、簡潔さ、拡張性、オートコンプリートといったアドバンテージが生まれます。サブクラス化を行うことは任意ですが、コードは次のように置き換わるでしょう。

ParseObjectを使う場合

ParseObject shield = new ParseObject("Armor");
shield.put("displayName", "Wooden Shield");
shield.put("fireproof", false);
shield.put("rupees", 50);

サブクラス化した場合

Armor shield = new Armor();
shield.setDisplayName("Wooden Shield");
shield.setFireproof(false);
shield.setRupees(50);

Subclassing ParseObject

ParseObjectを継承したサブクラスを作成するには

  1. ParseObjectを継承したサブクラスを宣言します。
  2. @ParseClassNameアノテーションを付与します。アノテーションで指定された文字列は、ParseObjectのコンストラクタに渡されます。以降の機能でクラス名を指定する必要がなくなります。
  3. publicな引数なしのコンストラクタを定義します。コンストラクタ内でParseObjectのフィールドをカスタマイズしてはいけません。
  4. Applicationクラス内で、Parse.initialize()を呼び出す前にParseObject.registerSubclass(YourClass.class)を呼び出します。

ParseObjectを継承したArmorクラスを登録する例を以下に示します。

// Armor.java
import com.parse.ParseObject;
import com.parse.ParseClassName;
 
@ParseClassName("Armor")
public class Armor extends ParseObject {
}
 
// App.java
import com.parse.Parse;
import android.app.Application;
 
public class App extends Application {
  @Override
  public void onCreate() {
    super.onCreate();
 
    ParseObject.registerSubclass(Armor.class);
    Parse.initialize(this, PARSE_APPLICATION_ID, PARSE_CLIENT_KEY);
  }
}

Accessors, Mutators, and Methods

ParseObjectのサブクラスには、ロジックをカプセル化するためのメソッドを追加します。ビジネスロジック、ストレージ/トランスミッションロジックといったような層の分離ではなく、対象についてのロジックを一カ所で管理することができます。

ParseObjcetのフィールドに対して、アクセサやミューテータを追加します。これは通常どおり、getterやsetterを定義するのですが、その内部ではget()、put()を使った実装になります。次のコードは、ArmorクラスにdisplayNameを定義したものです。

// Armor.java
@ParseClassName("Armor")
public class Armor extends ParseObject {
  public String getDisplayName() {
    return getString("displayName");
  }
  public void setDisplayName(String value) {
    put("displayName", value);
  }
}


displayNameを取得したい場合はarmor.getDisplayName()を使います。displayNameを設定したい場合は、armor.setDisplayName("Wooden Sword")を使います。また、IDEによってオートコンプリートも可能となるでしょう。タイポもコンパイル時に気づくようになります。

アクセサやミューテータは型に合わせて、getInt(), getParseFile()、getMap()を使用するようにしてください。

アクセサやミューテータだけでなく、より複雑なロジックを実装する必要がある場合は独自のメソッドを定義すると良いでしょう。

public void takeDamage(int amount) {
  // Decrease the armor's durability and determine whether it has broken
  increment("durability", -amount);
  if (getDurability() < 0) {
    setBroken(true);
  }
}

Initializing Subclasses

定義したサブクラスからオブジェクトを生成するには、コンストラクタを使用します。publicなデフォルトコンストラクタを定義する必要があり、コンストラクタの中では、ParseObjectのフィールドをカスタマイズしてはいけません。ParseSDKによって、サブクラスのインスタンスを作る際に型制約に従う必要があります。


既存のオブジェクトの参照を作るには、ParseObject.createWithoutData()を使います。

Armor armorReference = ParseObject.createWithoutData(Armor.class, armor.getObjectId());

Queries

ParseObject.getQuery()を使えば、特定のサブクラスのオブジェクトを検索するクエリを取得できます。次のコードは、ユーザが購入可能なarmorsを検索するものです。

ParseQuery<Armor> query = ParseObject.getQuery(Armor.class);
query.whereLessThanOrEqualTo("rupees", ParseUser.getCurrentUser().get("rupees"));
query.findInBackground(new FindCallback<Armor>() {
  @Override
  public void done(List<Armor> results, ParseException e) {
    for (Armor a : results) {
      // ...
    }
  }
});