Panic on American ApparelCar in Snow, Part 5Car in Snow, Part 4Car in Snow, Part 3

fill the void

Posted
13 December 2009 @ 6pm

Tagged
development

2 Comments

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

  1. To start, create a new Xcode project for a PreferencePane, listed in “Standard Apple Plug-ins”.
  2. Add the following frameworks to the project: Security and SecurityInterface.
  3. 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
    
  4. 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

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

2 Comments

Posted by
Tristan Seifert
23 February 2010 @ 8am

This also works outside of Pereference Panes, like in a normal App if you add the frameworks!


Posted by
bdunagan
23 February 2010 @ 9pm

Absolutely true. I’ve seen it integrated into several apps.


Leave a Comment