トピックの ACL のカスタマイズ

トピックの ACL を変更して、トピックの購読やメッセージの送信を実行できるユーザーをカスタマイズすることができます。ここでは、アクセス権を変更するための実装方法を説明します。

トピックの ACL エントリー

トピックの ACL エントリーに指定可能な項目は以下のとおりです。

  • アクション(アクセス制御)

    対象のユーザーやグループが「何をできるか」を指定します。

    アクション コードでの指定 ※ 対象ユーザー/グループ/Thing ができること
    SUBSCRIBE_TO_TOPIC topicActionSubscribe トピックの講読。
    SEND_MESSAGE_TO_TOPIC topicActionSend トピックに対するプッシュメッセージの送信。

    ※ これらのシンボルは、列挙型 KiiACLAction で定義されています。KiiACLAction.topicActionSubscribe のように指定できます。

  • サブジェクト(対象)

    「誰が」実行できるようになるかを指定します。

    サブジェクト 誰が実行可能か
    KiiUser インスタンス 指定されたユーザー。
    KiiGroup インスタンス 指定されたグループのメンバー。
    KiiThing インスタンス 指定された Thing。
    KiiAnyAuthenticatedUser ログイン済みの全ユーザー。
    KiiAnonymousUser 匿名ユーザー。

    ログイン済みの全ユーザーと匿名ユーザーの定義については、サブジェクト をご覧ください。

トピック の ACL の管理

トピックの ACL にエントリーを追加または削除できます。ACL エントリーの一覧を取得することもできます。

トピックに ACL エントリーを追加する

以下にグループスコープのトピックとユーザースコープのトピックに ACL エントリーを追加するサンプルコードを示します。

グループスコープのトピック

1 つ目のサンプルコードは、グループスコープのトピック GroupTopic に対して、以下の 3 つの ACL エントリーを追加する例です。

  • entry1:「認証済みユーザー全員」に「トピックの購読」アクションを許可。
  • entry2_I_am_a_supervisor_ という名前のユーザーに「トピックの購読」アクションを許可。
  • entry3_I_am_a_supervisor_ という名前のユーザーに「トピックへのプッシュメッセージ送信」アクションを許可。

なお、これは アクセス権の変更例 で示したシナリオの 1 つ目と 2 つ目の例に対応します。

Swift 3:

  • // Instantiate a group.
    let group = KiiGroup(uri: groupUri)
    
    do{
      // Refresh the group.
      try group.refreshSynchronous()
    } catch let error as NSError {
      // Handle the error.
      return
    }
    
    // Instantiate a topic in the group scope.
    let topicName = "GroupTopic"
    let topic = group.topic(withName: topicName)
    
    // Get an ACL handler.
    let acl = topic.topicACL
    
    // Allow the authenticated users to subscribe to the topic.
    let entry1 = KiiACLEntry(subject: KiiAnyAuthenticatedUser.aclSubject(), andAction: KiiACLAction.topicActionSubscribe)!
    acl.put(entry1)
    
    // Find a user.
    let user : KiiUser?
    user = try? KiiUser.find(byUsernameSynchronous: "_I_am_a_supervisor_")
    if user == nil {
      // Handle the error.
      return
    }
    
    // Allow the user to subscribe to the topic.
    let entry2 = KiiACLEntry(subject: user, andAction: KiiACLAction.topicActionSubscribe)!
    acl.put(entry2)
    
    // Allow the user to send push messages to the topic.
    let entry3 = KiiACLEntry(subject: user, andAction: KiiACLAction.topicActionSend)!
    acl.put(entry3)
    
    var succeeded: NSArray?
    var failed: NSArray?
    
    do{
      // Save all the ACL entries.
      try acl.saveSynchronous(&succeeded, didFail: &failed)
    } catch let error as NSError {
      // Failed to update one ACL entry at least.
      // Check the error description and the succeeded and failed arrays.
      return
    }
  • // Instantiate a group.
    let group = KiiGroup(uri: groupUri)
    
    // Refresh the group.
    group.refresh { (group : KiiGroup?, error : Error?) -> Void in
      if (error != nil) {
        // Handle the error.
        return
      }
    
      // Instantiate a topic in the group scope.
      let topicName = "GroupTopic"
      let topic = group!.topic(withName: topicName)
    
      // Get an ACL handler.
      let acl = topic.topicACL
    
      // Allow the authenticated users to subscribe to the topic.
      let entry1 = KiiACLEntry(subject: KiiAnyAuthenticatedUser.aclSubject(), andAction: KiiACLAction.topicActionSubscribe)!
      acl.put(entry1)
    
      // Find a user.
      let user : KiiUser?
      user = try? KiiUser.find(byUsernameSynchronous: "_I_am_a_supervisor_")
      if user == nil {
        // Handle the error.
        return
      }
    
      // Allow the user to subscribe to the topic.
      let entry2 = KiiACLEntry(subject: user, andAction: KiiACLAction.topicActionSubscribe)!
      acl.put(entry2)
    
      // Allow the user to send push messages to the topic.
      let entry3 = KiiACLEntry(subject: user, andAction: KiiACLAction.topicActionSend)!
      acl.put(entry3)
    
      // Save all the ACL entries.
      acl.save { (acl : KiiACL , succeeded : [Any]?, failed : [Any]?, error : Error?) -> Void in
        if (error != nil) {
          // Failed to update one ACL entry at least.
          // Check the error description and the succeeded and failed arrays.
          return
        }
      }
    }

Objective-C:

  • NSError *error;
    
    // Instantiate a group.
    KiiGroup *group = [KiiGroup groupWithURI:groupUri];
    
    // Refresh the group.
    [group refreshSynchronous:&error];
    if (error != nil) {
      // Handle the error.
      return;
    }
    
    // Instantiate a topic in the group scope.
    NSString *topicname = @"GroupTopic";
    KiiTopic *topic = [group topicWithName:topicname];
    
    // Get the ACL handler.
    KiiACL *acl = [topic topicACL];
    
    // Allow the authenticated users to subscribe to the topic.
    KiiAnyAuthenticatedUser *authenticatedUser = [KiiAnyAuthenticatedUser aclSubject];
    KiiACLEntry *entry1 = [KiiACLEntry entryWithSubject:authenticatedUser
                                              andAction:KiiACLTopicActionSubscribe];
    [acl putACLEntry:entry1];
    
    // Find a user.
    KiiUser* user = [KiiUser findUserByUsernameSynchronous:@"_I_am_a_supervisor_"
                                                 withError:&error];
    
    // Allow the user to subscribe to the topic.
    KiiACLEntry *entry2 = [KiiACLEntry entryWithSubject:user
                                              andAction:KiiACLTopicActionSubscribe];
    [acl putACLEntry:entry2];
    
    // Allow the user to send push messages to the topic.
    KiiACLEntry *entry3 = [KiiACLEntry entryWithSubject:user
                                              andAction:KiiACLTopicActionSend];
    [acl putACLEntry:entry3];
    
    NSArray *succeeded, *failed;
    
    // Save all the ACL entries.
    [acl saveSynchronous:&error
              didSucceed:&succeeded
                 didFail:&failed];
    if (error != nil) {
      // Failed to update one ACL entry at least.
      // Check the error description and the succeeded and failed arrays.
      return;
    }
  • // Instantiate a group.
    KiiGroup *group = [KiiGroup groupWithURI:groupUri];
    
    // Refresh the group.
    [group refreshWithBlock:^(KiiGroup *group, NSError *error) {
      if (error != nil) {
        // Handle the error.
        return;
      }
    
      // Instantiate a topic in the group scope.
      NSString *topicname = @"GroupTopic";
      KiiTopic *topic = [group topicWithName:topicname];
    
      // Get the ACL handler.
      KiiACL *acl = [topic topicACL];
    
      // Allow the authenticated users to subscribe to the topic.
      KiiAnyAuthenticatedUser *authenticatedUser = [KiiAnyAuthenticatedUser aclSubject];
      KiiACLEntry *entry1 = [KiiACLEntry entryWithSubject:authenticatedUser
                                              andAction:KiiACLTopicActionSubscribe];
      [acl putACLEntry:entry1];
    
      // Find a user.
      KiiUser* user = [KiiUser findUserByUsernameSynchronous:@"_I_am_a_supervisor_"
                                                 withError:&error];
    
      // Allow the user to subscribe to the topic.
      KiiACLEntry *entry2 = [KiiACLEntry entryWithSubject:user
                                              andAction:KiiACLTopicActionSubscribe];
      [acl putACLEntry:entry2];
    
      // Allow the user to send push messages to the topic.
      KiiACLEntry *entry3 = [KiiACLEntry entryWithSubject:user
                                              andAction:KiiACLTopicActionSend];
      [acl putACLEntry:entry3];
    
      // Save all the ACL entries.
      [acl saveWithBlock:^(KiiACL *acl, NSArray *succeeded, NSArray *failed, NSError *error) {
        if (error != nil) {
          // Failed to update one ACL entry at least.
          // Check the error description and the succeeded and failed arrays.
          return;
        }
      }];
    }];

ここでは以下の処理が行われています。

  1. URI から目的のグループを取得し、そのグループ内の GroupTopic という名前のトピックを取得します。
  2. 変更対象のトピックの topicACL() メソッドを実行して ACL ハンドラーを作成します。
  3. KiiACLEntry(subject:andAction:) メソッドを実行して、冒頭に示した entry1entry3 の ACL エントリーを作成します。
  4. put(_:) メソッドを実行して、各 ACL エントリーをローカル保存します。
  5. save(_:) メソッドを実行して、ローカルの ACL エントリーを Kii Cloud に保存します。

複数の ACL エントリーを追加する場合、SDK はそれらを 1 件ずつ処理するため、一部の ACL エントリーの処理が失敗する可能性があります。ACL の設定に失敗したときは、サンプルコード上の succeededfailed によって、成功した ACL エントリーの一覧と成功した ACL エントリーの一覧を取得できます。

ユーザースコープのトピック

2 つ目のサンプルコードは、ユーザースコープのトピック MyTODO に対して、以下の 1 つの ACL エントリーを追加する例です。

  • URI で指定されたグループに、「トピックの購読」アクションを許可。

なお、これは アクセス権の変更例 で示したシナリオの 3 つ目の例に対応します。

Swift 3:

  • // Instantiate an existing topic in the user scope.
    let user = KiiUser.current()!
    let topicName = "MyTODO"
    let topic = user.topic(withName: topicName)
    
    // Get an ACL handler.
    let acl = topic.topicACL
    
    // Instantiate a group.
    let group = KiiGroup(uri: groupUri)
    
    do{
      // Refresh the group.
      try group.refreshSynchronous()
    
      // Allow the group members to subscribe to the topic.
      let entry = KiiACLEntry(subject: group, andAction: KiiACLAction.topicActionSubscribe)!
      acl.put(entry)
    
      var succeeded: NSArray?
      var failed: NSArray?
    
      // Save all the ACL entries.
      try acl.saveSynchronous(&succeeded, didFail: &failed)
    } catch let error as NSError {
      // Failed to update one ACL entry at least.
      // Check the error description and the succeeded and failed arrays.
      return
    }
  • // Instantiate an existing topic in the user scope.
    let user = KiiUser.current()!
    let topicName = "MyTODO"
    let topic = user.topic(withName: topicName)
    
    // Get an ACL handler.
    let acl = topic.topicACL
    
    // Instantiate a group.
    let group = KiiGroup(uri: groupUri)
    
    // Refresh the group.
    group.refresh { (group : KiiGroup?, error : Error?) -> Void in
      if (error != nil) {
        // Handle the error.
        return
      }
    
      // Allow the group members to subscribe to the topic.
      let entry = KiiACLEntry(subject: group, andAction: KiiACLAction.bucketActionQueryObjects)!
      acl.put(entry)
    
      // Save all the ACL entries.
      acl.save { (acl : KiiACL , succeeded : [Any]?, failed : [Any]?, error : Error?) -> Void in
        if (error != nil) {
          // Failed to update one ACL entry at least.
          // Check the error description and the succeeded and failed arrays.
          return
        }
      }
    }

Objective-C:

  • NSError *error;
    
    // Instantiate an existing topic in the user scope.
    KiiUser* user = [KiiUser currentUser];
    NSString *topicname = @"MyTODO";
    KiiTopic *topic = [user topicWithName:topicname];
    
    // Get the ACL handler.
    KiiACL *acl = [topic topicACL];
    
    // Instantiate a group.
    KiiGroup *group = [KiiGroup groupWithURI:groupUri];
    
    // Refresh the group.
    [group refreshSynchronous:&error];
    if (error != nil) {
      // Handle the error.
      return;
    }
    
    // Allow the group members to subscribe to the topic.
    KiiACLEntry *entry = [KiiACLEntry entryWithSubject:group
                                             andAction:KiiACLTopicActionSubscribe];
    [acl putACLEntry:entry];
    
    NSArray *succeeded, *failed;
    
    // Save all the ACL entries.
    [acl saveSynchronous:&error
              didSucceed:&succeeded
                 didFail:&failed];
    if (error != nil) {
      // Failed to update one ACL entry at least.
      // Check the error description and the succeeded and failed arrays.
      return;
    }
  • // Instantiate an existing topic in the user scope.
    KiiUser* user = [KiiUser currentUser];
    NSString *topicname = @"MyTODO";
    KiiTopic *topic = [user topicWithName:topicname];
    
    // Get the ACL handler.
    KiiACL *acl = [topic topicACL];
    
    // Instantiate a group.
    KiiGroup *group = [KiiGroup groupWithURI:groupUri];
    
    // Refresh the group.
    [group refreshWithBlock:^(KiiGroup *group, NSError *error) {
      if (error != nil) {
        // Handle the error.
        return;
      }
    
      // Allow the group members to subscribe to the topic.
      KiiACLEntry *entry = [KiiACLEntry entryWithSubject:group
                                               andAction:KiiACLTopicActionSubscribe];
      [acl putACLEntry:entry];
    
      // Save all the ACL entries.
      [acl saveWithBlock:^(KiiACL *acl, NSArray *succeeded, NSArray *failed, NSError *error) {
        if (error != nil) {
          // Failed to update one ACL entry at least.
          // Check the error description and the succeeded and failed arrays.
          return;
        }
      }];
    }];

基本的な処理は、先ほどのサンプルコードと同様です。こちらの例では、ACL エントリーのサブジェクトとしてユーザーグループを指定しています。これにより、指定したアクション(トピックの購読)が、このユーザーグループのメンバー全員に許可されます。

トピックの ACL エントリーを削除する

設定されている ACL エントリーを削除するには、KiiACLEntrygrant を false にしたエントリーを作成して保存します。サーバー上の ACL から指定した ACL エントリーが削除されます。

サンプルコードはグループスコープのトピック、ユーザースコープのトピックとも、上記の ACL エントリーの追加の場合と同様で、grant プロパティを false にする点だけが異なります。

KiiACLremoveACLEntry(_:) メソッドは、クライアント側で準備中の「ACL の 変更リスト」のエントリーを削除するもので、サーバー上の ACL エントリーを削除するものではない点にご注意ください。ACL 設定のコードでは、クライアント上の acl に対して KiiACLTopicActionSubscribe を伴う ACL エントリーの削除要求を登録してから、save でサーバーに反映しています。removeACLEntry(_:) メソッドは、この ACL エントリーを変更リストから削除するためのもので、サーバー上の ACL を直接操作するためのものではありません。

トピックの ACL を取得する

トピックに設定されている ACL を取得できます。ACL エントリーを明示的に設定していない場合でも、デフォルトの ACL を取得することができます。

以下のように、ACL は ACL エントリーの NSArray として取得できます。

Swift 3:

  • // Instantiate an existing topic in the user scope.
    let topic = KiiUser.current()!.topic(withName: "MyTODO")
    
    // Get the ACL handler.
    let acl = topic.topicACL
    
    do{
      // Get the ACL.
      let aclList = try acl.listACLEntriesSynchronous() as! [KiiACLEntry]
      for entry in aclList{
        let action = entry.action
        let subject = entry.subject
        // Check the ACL entry.
      }
    }catch(let error as NSError){
      // Handle the error.
      return
    }
  • // Instantiate an existing topic in the user scope.
    let topic = KiiUser.current()!.topic(withName: "MyTODO")
    
    // Get the ACL handler.
    let acl = topic.topicACL
    
    // Get the ACL.
    acl.listACLEntries { (retAcl : KiiACL, result : [Any]?, error : Error?) -> Void in
      if (error != nil) {
        // Handle the error.
        return
      }
      let aclList = result as! [KiiACLEntry]
    
      for entry in aclList{
        let action = entry.action
        let subject = entry.subject
        // Check the ACL entry.
      }
    }

Objective-C:

  • // Instantiate an existing topic in the user scope.
    KiiTopic *topic = [[KiiUser currentUser] topicWithName:@"MyTODO"];
    
    // Get the ACL handler.
    KiiACL *acl = [topic topicACL];
    
    NSError *error = nil;
    
    // Get the ACL.
    NSArray *aclList = [acl listACLEntriesSynchronous:&error];
    if (error != nil) {
      // Handle the error.
      return;
    }
    for (KiiACLEntry *entry in aclList) {
      KiiACLAction action = entry.action;
      id subject = entry.subject;
      // Check the ACL entry.
    }
  • // Instantiate an existing topic in the user scope.
    KiiTopic *topic = [[KiiUser currentUser] topicWithName:@"MyTODO"];
    
    // Get the ACL handler.
    KiiACL *acl = [topic topicACL];
    
    // Get the ACL.
    [acl listACLEntriesWithBlock:^(KiiACL *acl, NSArray *aclList, NSError *error) {
      if (error != nil) {
        // Handle the error.
        return;
      }
      for (KiiACLEntry *entry in aclList) {
        KiiACLAction action = entry.action;
        id subject = entry.subject;
        // Check the ACL entry.
      }
    }];
  1. topicACL() メソッドをコールして KiiACL のインスタンスを生成します。
  2. listACLEntries(_:) メソッドをコールして、登録されている ACL を KiiACLEntry インスタンスの NSArray として取得します。
  3. NSArray の各エントリーを確認することで目的の処理を実行します。

ACL 設定の際、設定済みの ACL エントリーをさらに追加しようとするとエラーとなります。ここに示す方法で ACL を事前にチェックすることによって、必要な ACL エントリーの変更差分を作成できます。

期待どおりに動作しない場合

  • 複数回実行するとエラーになる

    ACL エントリーは初期化処理等で 1 回だけ書き換え、次回の実行時には再設定しないような実装が必要です。

    保存しようとしている ACL エントリーがすでに設定されていた場合、エラーになります。特に、同じ登録処理を複数回実行すると、登録しようとしている ACL エントリーがすでに存在することになるため、エラーになる点に注意が必要です。

    なお、複数の ACL エントリーは 1 件ずつサーバーに反映するため、途中でエラーが発生すると不完全な形で ACL エントリーが保存されます。このような状況から回復するには、既存の ACL をサーバーから一旦取得し、重複している ACL エントリーを削除してから登録するような処理が必要です。取得方法の詳細は トピックの ACL を取得する をご覧ください。

  • ACL エントリーを削除できない

    トピック作成者やスコープオーナーにデフォルトで許可されるアクションの ACL エントリーは削除できません。詳細は スコープオーナーや作成者の ACL エントリーは削除できません をご覧ください。