Finding and Monitoring Identities
One of the most common uses of the Identity Services API is to look for an identity stored in an identity authority. You may also need to observe changes to users and groups that occur outside the scope of your application. For example, if another application removes a user from a group your application is using, you want to be notified of this change. In the Core Services Identity API, the CSIdentityQuery
class provides synchronous and asynchronous access to find and monitor identities from an identity authority’s database. In the Collaboration framework, these methods are part of the CBIdentity
class.
This chapter explains how to search for identities using both CSIdentityQuery
objects and CBIdentity
objects.
Find an Identity
You can find an identity using either the Objective-C based Collaboration framework or the Core Services Identity API.
Using the Collaboration Framework
To find a user or group with the Collaboration framework, use one of the CBIdentity class factory methods. There are three methods that allow you to search based on different properties of an identity:
If you want to search by full names, short names, or aliases, use the
identityWithName:authority:
method.If you want to search by UUID, use the
identityWithUUIDString:authority:
method.If you want to search using a persistent reference, use the
identityWithPersistentReference:
method. (For more information about persistent references, see Loading an ACL).
To complete your search, pass a search term and an identity authority object to either the identityWithName:authority:
and the identityWithUUIDString:authority:
methods. There are a number of class factory methods in CBIdentityAuthority
that allow you to create an identity authority object based on the identity authorities you want to search. Listing 3-1 shows how to search for all identities named “David Ortiz” in a local identity authority.
Listing 3-1 Finding an identity in Objective-C
CBIdentityAuthority *localAuthority = |
[CBIdentityAuthority localIdentityAuthority]; |
CBIdentity *user = |
[CBIdentity identityWithName:@"David Ortiz" authority:localAuthority]; |
You can also search specifically for a user identity or a group identity by using the CBUserIdentity
and CBGroupIdentity
classes, respectively. By default, the CBIdentity
class factory methods search for a user identities first, and if none are located then it looks for group identities.
Using the Core Services Identity API
To find a user or group with the Core Services Identity API you need to create a CSIdentityQuery
object. A CSIdentityQuery
object contains methods to search the identities database. It is important to use the appropriate method to create the identity query object based on how you want to search the database. The following methods are provided for you:
If you want to search by full names, short names, or aliases, use the
CSIdentityQueryCreateForName
method.If you want to search by UUID, use the
CSIdentityQueryCreateForUUID
method.If you want to search by POSIX ID, use the
CSIdentityQueryCreateForPosixID
method.If you want to search by reference data (generated by the
CSIdentityCreatePersistentReference
method), use theCSIdentityQueryCreateForPersistentReference
method.If you want to search for the current user’s identity, use the
CSIdentityQueryCreateForCurrentUser
method.
There are two ways to execute the search, synchronously and asynchronously. It is highly recommended that you run any process that could block as a result of network delays asynchronously.
Search Identities Synchronously
To perform a CSIdentityQuery
search synchronously, call the method CSIdentityQueryExecute
on your identity query object. The method returns only when it has completed the search. If the query is executed successfully, CSIdentityQueryExecute
returns TRUE
; otherwise, it returns FALSE
. Assuming the query was successful, run the CSIdentityQueryCopyResults
method to return an array of identity objects. When you have finished retrieving the identities, make sure to release the CSIdentityQuery
object. Listing 3-2 shows an example of this.
Listing 3-2 Finding identities synchronously
CSIdentityQueryRef query; |
CFErrorRef error; |
CFArrayRef identityArray; |
// create the identity query based on name |
query = CSIdentityQueryCreateForName(kCFAllocatorDefault, |
CFSTR("David"), |
kCSIdentityQueryStringBeginsWith, |
kCSIdentityClassUser, |
CSGetDefaultIdentityAuthority()); |
// execute the query |
if (CSIdentityQueryExecute(query, kCSIdentityQueryGenerateUpdateEvents, &error)) |
{ |
// retrieve the results of the identity query |
identityArray = CSIdentityQueryCopyResults(query); |
// do something with identityArray |
} |
CFRelease(query); |
Search Identities Asynchronously
Performing an identity query asynchronously is similar to performing a query synchronously but differs in an important way. With a synchronous query, you execute the query, wait for it to complete, and then ask for the results. In contrast, with an asynchronous query, you start the query and your callback function will be passed the results as they become available. The process for setting up asynchronous callbacks is similar in theory and in practice to other Core Services callbacks.
To search for an identity asynchronously requires two main steps: setting up a callback function and adding the identity query object to a run loop. First, set up your callback function. The callback function must be a void
function and must accept five arguments:
CSIdentityQueryRef query
, the identity query objectCSIdentityQueryEvent event
, the event that caused the callback function to be runCFArrayRef identities
, the results of the query as an array identitiesCFErrorRef error
, the error that occurred as a result of the identity query, if applicablevoid *info
, any data placed in theCSIdentityQueryClientContext
, to be sent to the callback function
A callback function might look like Listing 3-3.
Listing 3-3 Identity query callback function
void myIdentityQueryCallback (CSIdentityQueryRef query, |
CSIdentityQueryEvent event, |
CFArrayRef identities, |
CFErrorRef error, |
void *info) { |
// See what event triggered the callback |
switch ( event ) { |
case kCSIdentityQueryEventResultsAdded: |
// An identity was added to the list of results |
break; |
case kCSIdentityQueryEventSearchPhaseFinished: |
// The query was completed |
break; |
} |
} |
To add the identity query object to a run loop, first create the object. Then create a CSIdentityQueryClientContext
structure. In the CSIdentityQueryClientContext
structure, define the name of the callback function to be run. With the CSIdentityQueryClientContext
structure set up, call the CSIdentityQueryExecuteAsynchronously
method to add the query to a run loop.
CSIdentityQueryExecuteAsynchronously
requires five arguments: the identity query object to be executed, the execution options (from CSIdentityQueryFlags
), a pointer to your CSIdentityQueryClientContext
structure, the run loop on which to schedule callbacks, and the run loop mode. If the query is added to the run loop, the function returns TRUE
and any errors as a result of the query are sent to your callback function. See Listing 3-4 to see how this looks in code.
Listing 3-4 Adding an identity query object to a run loop
CSIdentityQueryRef query; |
CSIdentityQueryClientContext queryclient = |
{0, NULL, NULL, NULL, NULL, myIdentityQueryCallback}; |
// create the identity query based on name |
query = CSIdentityQueryCreateForName(kCFAllocatorDefault, |
CFSTR("David"), |
kCSIdentityQueryStringBeginsWith, |
kCSIdentityClassUser, |
CSGetDefaultIdentityAuthority()); |
// add the identity query object to the current run loop |
if (!CSIdentityQueryExecuteAsynchronously(query, |
kCSIdentityQueryGenerateUpdateEvents, |
&queryclient, |
CFRunLoopGetCurrent(), |
kCFRunLoopCommonModes)) |
{ |
// query was not added to the run loop |
} |
When an event is triggered based on your query, your callback function is run. The event that causes your callback function to run is passed to the callback function along with an array of identities. If identities from the query are added, removed, or modified, the array contains only those identities that have been affected. If the search is completed, then the array is NULL
. In this case, use CSIdentityQueryCopyResults
to get the full list of identities.
After the query has completed, you need to remove the identity query object from the run loop. To do this, call the CSIdentityQueryStop
method and pass it the identity query object. Then, release the identity query object. This should look like the code in Listing 3-5.
Listing 3-5 Invalidating an identity query object
CSIdentityQueryStop(query); |
CFRelease(query); |
Continually Monitor Identities
Monitoring an identity is very similar to searching for one. If you are searching for an identity asynchronously, then all you need to do is to not call CSIdentityQueryStop
when the query is completed. As long as your identity query object is registered on the run loop, it continues to notify you when the contents of your query change. So if you are searching for all Identities with the name “Chris”, and a new user is created with the name “Chris Jones” after your original search finished, your callback function will be notified of this new user. When you are done monitoring the identities, make sure to call CSIdentityQueryStop
.
To monitor identities synchronously, you need to poll an identity query object. Each identity query object can only be executed once, so after running CSIdentityQueryExecute
and checking the results with CSIdentityQueryCopyResults
, you will need to create an identical identity query object to execute again. Each time you run CSIdentityQueryCopyResults
it will return an array with the full results of your query, not just what has changed. This is another reason why it is recommended that you search for and monitor identities asynchronously, rather than synchronously.
Copyright © 2012 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2012-02-16