Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
639 views
in Technique[技术] by (71.8m points)

macos - How can I programmatically obtain (Objective-C or Swift) the list of groups a user is member of, for all the users on the Mac?

I found this lovely code in this question that helps me get the list of users.

#import <OpenDirectory/OpenDirectory.h>
ODSession *s = [ODSession defaultSession];
ODNode *root = [ODNode nodeWithSession:s name:@"/Local/Default" error:nil];
ODQuery *q = [ODQuery queryWithNode:root forRecordTypes:kODRecordTypeUsers attribute:nil matchType:0 queryValues:nil returnAttributes:nil maximumResults:0 error:nil];

NSArray *results = [q resultsAllowingPartial:NO error:nil];
for (ODRecord *r in results) {
    NSLog(@"%@", [r recordName]);
}

Now in Terminal/Shell, I could simply:

id -G <username or userID>

to immediately get the list of groups that user is member of, e.g.

>id -G 501 
20 12 61 79 80 81 98 702 701 33 100 204 250 395 398 399 400

However, is looks silly to go on and spawn an NSTask running "id", then collect the output and parse, just because I don't know how to use the OpenDirectory APIs...

So I dug a little deeper, and saw I could specify one or more "attributes" in the returnAttributes: parameter of the ODQuery... and then I went through the long list of attributes and found only one that made sense in CFOpenDirectoryConstants.h:

/*!
    @const      kODAttributeTypeGroup
    @abstract   List of groups.
    @discussion List of groups.
*/
CF_EXPORT
const ODAttributeType kODAttributeTypeGroup;

However, when I add it to the "standard" or "nativeOnly" or "All" sets of returnedAttributes, or even specify it alone - the result ODRecords I receive do NOT contain that attribute.

I try to obtain it using [r valueForAttribute: kODAttributeTypeGroup]; and also when I print [r description] where I can see all the attributes returned - alas it's not there.

I tried other attributes too, which were "close to the subject" like

kODAttributeTypeGroupMembers, kODAttributeTypeGroupMembers, kODAttributeTypeNetGroups, kODAttributeTypeGroupMembership and others - but both they are more expected on "Group" records of OpenDirectory, and anyway they're not returned.

So my question: how do I persuade OD to retrieve the groups for each of the users? Any hint (or reference to helpful documentation) will be greatly appreciated. Thanks!


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

If you change your loop as follows

        for (ODRecord *r in results) {
            NSLog(@"%@", [r recordName]);
            
            // Query groups
            ODQuery * gq = [ODQuery queryWithNode:root
                           forRecordTypes:kODRecordTypeGroups
                            attribute:kODAttributeTypeGroupMembership
                            matchType:kODMatchContains
                          queryValues:r.recordName
                         returnAttributes:nil
                           maximumResults:0
                            error:NULL];
            NSArray * gr = [gq resultsAllowingPartial:NO
                                error:NULL];
            
            for ( ODRecord * g in gr ) {
                
                NSLog(@"%@",g.recordName);
                
            }
        }

Update

If you want something similar to id you need to query each group individually. Here is an example, just forced into your structure.

        for (ODRecord *r in results) {
            NSLog(@"%@", [r recordName]);
            
            // Query groups
            ODQuery * gq = [ODQuery queryWithNode:root
                           forRecordTypes:kODRecordTypeGroups
                            attribute:nil
                            matchType:0
                          queryValues:nil
                         returnAttributes:nil
                           maximumResults:0
                            error:NULL];
            NSArray * gr = [gq resultsAllowingPartial:NO
                                error:NULL];
            
            for ( ODRecord * g in gr ) {
                
                if ( [g isMemberRecord:r
                         error:NULL] ) {

                    NSLog(@"%@",g.recordName);

                }
            }
        }

This illustrates how to do it. What is wrong with it is that the groups are retrieved every time. You could e.g. easily cache that.

It is important to take note of both queries. In the first I query for group membership and in the second I query for groups and use each group to test for membership. It seems the first is more direct and the second stronger in the sense that it is more complete / accurate. I think this is more accurate because of nesting in groups.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...