System Preferences Pane Lock

Many panes in System Preferences require I authenticate myself to change settings. Apple has a consistent UI for this: the lock icon. Next to it is a little snippet of text telling me why it’s useful: “Click the lock to make changes.” I click it and authenticate, and it opens, enabling various buttons and fields in that pane. “Click the lock to prevent further changes.” I click it again, and it locks, disabling those buttons and fields. This UI and underlying authentication process are included in Cocoa: SFAuthorizationView.
This tutorial creates a simple prefpane with a button and the lock UI, building on two earlier posts about root-level operations and debugging prefpanes. Clicking the button creates an empty file at /var/log/test.txt, an action that requires root-level privilege. The button is grayed out if the lock is locked; it enables when I unlock the lock. The source code for this project is available on Google Code.

Xcode
- To start, create a new Xcode project for a PreferencePane, listed in “Standard Apple Plug-ins”.
- Add the following frameworks to the project: Security and SecurityInterface.
- In the header file, import the SecurityInterface framework and add two IBOutlets:
#import <PreferencePanes/PreferencePanes.h> #import <SecurityInterface/SFAuthorizationView.h> @interface BDAuthorizePrefPanePref : NSPreferencePane { IBOutlet SFAuthorizationView *authView; IBOutlet NSButton *touchButton; } - (BOOL)isUnlocked; - (IBAction)clickTouch:(id)sender; @end - In the source file, setup the security interface and handle its actions:
#import "BDAuthorizePrefPanePref.h" @implementation BDAuthorizePrefPanePref - (void) mainViewDidLoad { // Setup security. AuthorizationItem items = {kAuthorizationRightExecute, 0, NULL, 0}; AuthorizationRights rights = {1, &items}; [authView setAuthorizationRights:&rights]; authView.delegate = self; [authView updateStatus:nil]; [touchButton setEnabled:[self isUnlocked]]; } - (BOOL)isUnlocked { return [authView authorizationState] == SFAuthorizationViewUnlockedState; } - (IBAction)clickTouch:(id)sender { // Collect arguments into an array. NSMutableArray *args = [NSMutableArray array]; [args addObject:@"-c"]; [args addObject:@" touch /var/log/test.txt"]; // Convert array into void-* array. const char **argv = (const char **)malloc(sizeof(char *) * [args count] + 1); int argvIndex = 0; for (NSString *string in args) { argv[argvIndex] = [string UTF8String]; argvIndex++; } argv[argvIndex] = nil; OSErr processError = AuthorizationExecuteWithPrivileges([[authView authorization] authorizationRef], [@"/bin/sh" UTF8String], kAuthorizationFlagDefaults, (char *const *)argv, nil); free(argv); if (processError != errAuthorizationSuccess) NSLog(@"Error: %d", processError); } // // SFAuthorization delegates // - (void)authorizationViewDidAuthorize:(SFAuthorizationView *)view { [touchButton setEnabled:[self isUnlocked]]; } - (void)authorizationViewDidDeauthorize:(SFAuthorizationView *)view { [touchButton setEnabled:[self isUnlocked]]; } @end
Interface Builder

- Open the xib file in the Resources folder of the new project.
- Change the NSFileOwner’s class to the main controller’s class.
- Resize the window to W:440,H:120.
- Drop an NSView into the content view of the window.
- Resize the view to X:20,Y:20,W:400,:H:40.
- Change the view’s class to SFAuthorizationView.
- Place an NSButton above the security view and link its action to clickTouch:.
- Link up the outlets for the button and the security view.








2 Comments