Customizing a Topic's ACL

You can customize the users who can subscribe and send messages to a topic by modifying the topic's ACL. This topic explains how you can do this.

Topic ACL entries

A topic ACL entry is composed of an action and a subject:

  • Action

    This item defines "what" the target user or group can execute.

    Action Representation in code * What the target user/group/thing can execute
    SUBSCRIBE_TO_TOPIC topicActionSubscribe Subscribe to the topic.
    SEND_MESSAGE_TO_TOPIC topicActionSend Send push messages to the topic.

    * These symbols are defined in the KiiACLAction enumeration and can be specified like KiiACLAction.topicActionSubscribe.

  • Subject

    This item defines "who" can execute.

    Subject Who can execute the designated action?
    KiiUser instance The specified user.
    KiiGroup instance The members of the specified group.
    KiiThing instance The specified thing.
    KiiAnyAuthenticatedUser Any authenticated users.
    KiiAnonymousUser Anonymous users.

    See Subject for the definition of the "Any authenticated users" and "Anonymous users".

Managing a topic's ACL

You can add and delete an ACL entry in a topic's ACL. You can also get a list of ACL entries.

Adding a topic ACL entry

See the following two samples which add ACL entries to the ACL of group-scope and user-scope topics.

Topic in a group scope

The first sample code will add the following three ACL entries to the ACL of the group-scope topic GroupTopic.

  • entry1: Permit the "subscribe to the topic" action to "all authenticated users"
  • entry2: Permit the "subscribe to the topic" action to the user _I_am_a_supervisor_.
  • entry3: Permit the "send push messages to the topic" action to the user _I_am_a_supervisor_.

Note that these changes correspond to the first and second scenarios presented in Changing a topic's ACL.

Swift:

  • // 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;
        }
      }];
    }];

Here is a brief explanation of what is happening in the sample code.

  1. Get the target topic GroupTopic from its URI.
  2. Create an ACL handler by calling the topicAcl() method.
  3. Call the KiiACLEntry(subject:andAction:) method to create ACL entries (entry1, entry2, and entry3).
  4. Call the put(_:) method to locally save each ACL entry.
  5. Call the save(_:) method to save the ACL entries to Kii Cloud.

When you are adding multiple ACL entries, the entry registration is handled one by one. If an error occurs before all entries are registered, some entries will remain unregistered. In such a situation, you can get a list of ACL entries succeeded and failed to register from the succeeded and failed in the sample code.

Topic in a user scope

The second sample code will add the following ACL entry to the ACL of the user-scope topic MyTODO.

  • Permit the "subscribe to the topic" action to the specified group members.

Note that this change corresponds to the third scenario presented in Changing a topic's ACL.

Swift:

  • // 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;
        }
      }];
    }];

The basic processing is the same as in the previous sample code. In this sample code, we specify a group as an ACL entry's subject so as to permit the action to all group members.

Deleting a topic ACL entry

To delete an ACL entry, create a KiiACLEntry instance with its third parameter grant set to false and save it. The ACL entry will be deleted from the server.

For example, you can delete the ACL entries created in the previous sample code by setting the grant property to false.

Note that the removeACLEntry(_:) method of the KiiACL just deletes an ACL entry from the local modification list. The method does not delete an ACL entry set on the server. In the above sample code, we first register a request to remove the KiiACLTopicActionSubscribe action from acl on the client and then execute the save(_:) method to reflect the request on the server. If you execute the removeACLEntry(_:) method, it would remove the request from the ACL modification list, but not from the ACL on the server.

Getting a topic's ACL

You can get the ACL set on a topic. Even if you have not set any ACL entries in the topic's ACL, you can get the default ACL of the topic.

Here is the sample code for getting a list of ACL entries as an NSArray.

Swift:

  • // 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. Create a KiiACL instance of the topic by executing the topicACL() method.
  2. Call the listACLEntries(_:) method to get the topic's ACL as an NSArray of the KiiACLEntry instances.
  3. Parse through each ACL entry in the NSArray.

Trying to set an existing ACL entry will give you an error. By checking existing ACL entries beforehand as shown in this sample code, you can find which ACL entries should be set.

Troubleshooting

  • I got an error when I run my application multiple times

    The ACL entry should be registered just once (e.g., in the initialization) and should not be registered again in the next run.

    If you try to save an existing ACL entry, you will get an error. If your application runs the same ACL entry registration flow every time, therefore, you will get an error because the application attempts to re-register an already existing ACL entry.

    When you are adding multiple ACL entries, the entry registration is handled one by one. If an error occurs before all entries are registered, some entries will remain unregistered. To recover from such a situation, you will first need to get a list of the existing ACL entries, remove the duplicating ACL entries from the ACL modification list, and then register remaining entries. See Getting a topic's ACL to learn how you can get the list of the existing ACL entries.

  • I cannot delete an ACL entry

    You cannot delete default ACL entries applied to scope owners and topic creators. See Cannot delete default ACL entries of scope owners and creators for more details.