From: Justin Anderson Date: Wed, 1 Oct 2008 14:12:31 +0000 (+0000) Subject: A working KIM-based KerberosAgent to handle all of KIM's built-in GUI prompting. X-Git-Tag: krb5-1.7-alpha1~378 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=8294739d8cbc7b91ae11c0fb8fbb3c918bb90348;p=krb5.git A working KIM-based KerberosAgent to handle all of KIM's built-in GUI prompting. A few outstanding issues remain, like the ability to add duplicate entries to the list of favorite identities. ticket: 6055 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@20793 dc483132-0cff-0310-8789-dd5450dbe970 --- diff --git a/src/kim/agent/mac/AuthenticationController.h b/src/kim/agent/mac/AuthenticationController.h new file mode 100644 index 000000000..5fcb612c0 --- /dev/null +++ b/src/kim/agent/mac/AuthenticationController.h @@ -0,0 +1,76 @@ +/* + * Copyright 2008 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#import + +@class IPCClient; +@class BadgedImageView; + +@interface AuthenticationController : NSWindowController { + IPCClient *associatedClient; + + IBOutlet NSView *containerView; + IBOutlet NSView *identityView; + IBOutlet NSView *passwordView; + IBOutlet NSView *samView; + IBOutlet NSView *changePasswordView; + IBOutlet NSView *expiredPasswordView; + IBOutlet NSView *errorView; + + IBOutlet BadgedImageView *enterBadge; + IBOutlet BadgedImageView *passwordBadge; + IBOutlet BadgedImageView *samBadge; + IBOutlet BadgedImageView *changePasswordBadge; + IBOutlet BadgedImageView *errorBadge; + + // Controls that need to be made key + IBOutlet NSTextField *usernameField; + IBOutlet NSTextField *passwordField; + IBOutlet NSTextField *samPromptField; + IBOutlet NSTextField *oldPasswordField; + + // Other controls of interest + IBOutlet NSButton *rememberPasswordInKeychainCheckBox; + + IBOutlet NSObjectController *glueController; +} + +@property (readwrite, retain) IPCClient *associatedClient; + +- (void) setContent: (NSMutableDictionary *) newContent; + +- (void) showEnterIdentity; +- (void) showAuthPrompt; +- (void) showEnterPassword; +- (void) showSAM; +- (void) showChangePassword; +- (void) showError; + +- (IBAction) cancel: (id) sender; +- (IBAction) enterIdentity: (id) sender; +- (IBAction) answerAuthPrompt: (id) sender; +- (IBAction) changePassword: (id) sender; +- (IBAction) showedError: (id) sender; + +@end diff --git a/src/kim/agent/mac/AuthenticationController.m b/src/kim/agent/mac/AuthenticationController.m new file mode 100644 index 000000000..6a9b668bb --- /dev/null +++ b/src/kim/agent/mac/AuthenticationController.m @@ -0,0 +1,383 @@ +/* + * Copyright 2008 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#import "AuthenticationController.h" +#import "IPCClient.h" +#import "BadgedImageView.h" + +// to get kim_prompt_type enum +#import + +/* + * glueController KVC mapping is as follows: + * name = client app name + * path = client app bundle path + * title = suggested label for prompt field + * message = desired large text message + * description = longer, detailed, small text message + * username = 'user' part of 'user@REALM.ORG' + * realm = 'REALM.ORG' part of 'user@REALM.ORG' + * realm_history = past realms the user has entered + * prompt_response = auth prompt response + * allow_save_password = whether or not to show the 'save password in keychain' checkbox + * should_save_password = whether or not to save the password in the keychain + * old_password = for change password dialog + * new_password = " + * verify_password = " + */ + +#define client_name_keypath @"content.name" +#define client_path_keypath @"content.path" + +#define identity_string_keypath @"content.identity_string" +#define title_keypath @"content.title" +#define message_keypath @"content.message" +#define description_keypath @"content.description" + +#define username_keypath @"content.username" +#define realm_keypath @"content.realm" +#define realm_history_keypath @"content.realm_history" + +#define prompt_response_keypath @"content.prompt_response" +#define allow_save_password_keypath @"content.allow_save" +#define should_save_password_keypath @"content.save_response" + +#define password_expired_keypath @"content.expired" +#define old_password_keypath @"content.old_password" +#define new_password_keypath @"content.new_password" +#define verify_password_keypath @"content.verify_password" + +#define enable_identity_ok_keypath @"content.isPrincipalValid" +#define enable_prompt_ok_keypath @"content.isPromptValid" +#define change_password_ok_keypath @"content.isChangePasswordValid" + +#define valid_lifetime_keypath @"content.valid_lifetime" +#define renewal_lifetime_keypath @"content.renewal_lifetime" +#define renewable_keypath @"content.renewable" +#define addressless_keypath @"content.addressless" +#define forwardable_keypath @"content.forwardable" + +#define max_valid_lifetime_keypath @"content.max_valid_lifetime" +#define min_valid_lifetime_keypath @"content.min_valid_lifetime" +#define max_renewal_lifetime_keypath @"content.max_renewal_lifetime" +#define min_renewal_lifetime_keypath @"content.min_renewal_lifetime" + + +#define ACKVOContext @"authenticationController" + +// localization keys and tables + +#define ACLocalizationTable @"AuthenticationController" + +#define ACAppPrincReqKey @"AuthControllerApplicationPrincipalRequest" +#define ACPrincReqKey @"AuthControllerPrincipalRequest" +#define ACAppPasswordReqKey @"AuthControllerApplicationPasswordRequest" +#define ACPasswordReqKey @"AuthControllerPasswordRequest" +#define ACPasswordChangeExpired @"ChangePasswordPasswordExpired" +#define ACPasswordChangeApp @"ChangePasswordApplicationRequest" +#define ACPasswordChangePrinc @"ChangePasswordPrincipalRequest" + +@implementation AuthenticationController + +@synthesize associatedClient; + +- (id) init +{ + return [self initWithWindowNibName: @"Authentication"]; +} + +- (void) awakeFromNib +{ + [glueController addObserver:self + forKeyPath:username_keypath + options:NSKeyValueObservingOptionNew + context:ACKVOContext]; + [glueController addObserver:self + forKeyPath:realm_keypath + options:NSKeyValueObservingOptionNew + context:ACKVOContext]; + [glueController addObserver:self + forKeyPath:prompt_response_keypath + options:NSKeyValueObservingOptionNew + context:ACKVOContext]; + [glueController addObserver:self + forKeyPath:old_password_keypath + options:NSKeyValueObservingOptionNew + context:ACKVOContext]; + [glueController addObserver:self + forKeyPath:new_password_keypath + options:NSKeyValueObservingOptionNew + context:ACKVOContext]; + [glueController addObserver:self + forKeyPath:verify_password_keypath + options:NSKeyValueObservingOptionNew + context:ACKVOContext]; + + [[self window] center]; + // We need to float over the loginwindow and SecurityAgent so use its hardcoded level. + [[self window] setLevel:2003]; +} + +- (void) dealloc +{ + [glueController removeObserver:self forKeyPath:username_keypath]; + [glueController removeObserver:self forKeyPath:realm_keypath]; + [glueController removeObserver:self forKeyPath:prompt_response_keypath]; + [super dealloc]; +} + +- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + if ([(NSString *) context isEqualToString:ACKVOContext]) { + if ([keyPath isEqualToString:username_keypath] || [keyPath isEqualToString:realm_keypath]) { + BOOL valid = [KIMUtilities validatePrincipalWithName:[glueController valueForKeyPath:username_keypath] + realm:[glueController valueForKeyPath:realm_keypath]]; + [glueController setValue:[NSNumber numberWithBool:valid] + forKeyPath:enable_identity_ok_keypath]; + } + else if ([keyPath isEqualToString:prompt_response_keypath]) { + BOOL valid = ([[glueController valueForKeyPath:prompt_response_keypath] length] > 0); + [glueController setValue:[NSNumber numberWithBool:valid] + forKeyPath:enable_prompt_ok_keypath]; + } + else if ([keyPath isEqualToString:old_password_keypath] || + [keyPath isEqualToString:new_password_keypath] || + [keyPath isEqualToString:verify_password_keypath]) { + NSString *oldString = [glueController valueForKeyPath:old_password_keypath]; + NSString *newString = [glueController valueForKeyPath:new_password_keypath]; + NSString *verifyString = [glueController valueForKeyPath:verify_password_keypath]; + BOOL valid = ([oldString length] > 0 && + [newString length] > 0 && + [verifyString length] > 0 && + [newString isEqualToString:verifyString]); + [glueController setValue:[NSNumber numberWithBool:valid] + forKeyPath:change_password_ok_keypath]; + } + } + else { + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; + } +} + +- (void) setContent: (NSMutableDictionary *) newContent +{ + [self window]; // wake up the nib connections + [glueController setContent:newContent]; +} + +- (void) showEnterIdentity +{ + NSString *key = (associatedClient.name) ? ACAppPrincReqKey : ACPrincReqKey; + NSString *message = [NSString stringWithFormat: + NSLocalizedStringFromTable(key, ACLocalizationTable, NULL), + associatedClient.name]; + + // wake up the nib connections and adjust window size + [self window]; + // set up controls with info from associatedClient + [[containerView subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)]; + [containerView addSubview:identityView]; + [enterBadge setBadgePath:associatedClient.path]; + [glueController setValue:message + forKeyPath:message_keypath]; + [self showWindow:nil]; + [[self window] makeFirstResponder:usernameField]; +} + +- (void) showAuthPrompt +{ + uint32_t type = [[glueController valueForKeyPath:@"content.prompt_type"] unsignedIntegerValue]; + + switch (type) { + case kim_prompt_type_password : + [self showEnterPassword]; break; + case kim_prompt_type_preauth : + default : + [self showSAM]; break; + } +} + +- (void) showEnterPassword +{ + CGFloat shrinkBy; + NSRect frame; + NSString *key = nil; + NSString *message = nil; + + [self window]; + + if ([associatedClient.name isEqualToString:[[NSBundle mainBundle] bundlePath]]) { + key = ACPasswordReqKey; + message = [NSString stringWithFormat: + NSLocalizedStringFromTable(key, ACLocalizationTable, NULL), + [glueController valueForKeyPath:identity_string_keypath]]; + } else { + key = ACAppPasswordReqKey; + message = [NSString stringWithFormat: + NSLocalizedStringFromTable(key, ACLocalizationTable, NULL), + associatedClient.name, + [glueController valueForKeyPath:identity_string_keypath]]; + } + [glueController setValue:message + forKeyPath:message_keypath]; + + // wake up the nib connections and adjust window size + // set up controls with info from associatedClient + [[containerView subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)]; + [containerView addSubview:passwordView]; + // set badge + [passwordBadge setBadgePath:associatedClient.path]; + + // adjust for checkbox visibility + if (![[glueController valueForKeyPath:allow_save_password_keypath] boolValue]) { + shrinkBy = ([passwordField frame].origin.y - + [rememberPasswordInKeychainCheckBox frame].origin.y); + frame = [[self window] frame]; + frame.origin.y += shrinkBy; + frame.size.height -= shrinkBy; + [[self window] setFrame:frame display:NO animate:NO]; + } + + [self showWindow:nil]; + [[self window] makeFirstResponder:passwordField]; +} + +- (void) showSAM +{ + // wake up the nib connections and adjust window size + [self window]; + // set up controls with info from associatedClient + [[containerView subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)]; + [containerView addSubview:samView]; + // set badge + [samBadge setBadgePath:associatedClient.path]; + + [glueController setValue:[NSNumber numberWithBool:NO] + forKeyPath:allow_save_password_keypath]; + + [self showWindow:nil]; + [[self window] makeFirstResponder:samPromptField]; +} + +- (void) showChangePassword +{ + NSString *key = ([glueController valueForKeyPath:password_expired_keypath]) ? ACAppPrincReqKey : ACPrincReqKey; + NSString *message = [NSString stringWithFormat: + NSLocalizedStringFromTable(key, ACLocalizationTable, NULL), + associatedClient.name]; + + + BOOL expired = [[glueController valueForKeyPath:password_expired_keypath] boolValue]; + BOOL calledBySelf = [associatedClient.path isEqualToString:[[NSBundle mainBundle] bundlePath]]; + + if (calledBySelf) { + key = ACPasswordChangePrinc; + message = [NSString stringWithFormat: + NSLocalizedStringFromTable(key, ACLocalizationTable, NULL), + [glueController valueForKeyPath:identity_string_keypath]]; + // Please change the Kerberos password for \"%@\" + } else if (!expired) { + key = ACPasswordChangeApp; + message = [NSString stringWithFormat: + NSLocalizedStringFromTable(key, ACLocalizationTable, NULL), + associatedClient.name, + [glueController valueForKeyPath:identity_string_keypath]]; + // %@ requires that you change the Kerberos password for \"%@\" + } else { + key = ACPasswordChangeExpired; + message = NSLocalizedStringFromTable(key, ACLocalizationTable, NULL); + // Your password has expired, would you like to change it? + } + [glueController setValue:message forKeyPath:message_keypath]; + + // wake up the nib connections and adjust window size + [self window]; + // set up controls with info from associatedClient + [[containerView subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)]; + [containerView addSubview:changePasswordView]; + // set badge + [changePasswordBadge setBadgePath:associatedClient.path]; + + [self showWindow:nil]; + [[self window] makeFirstResponder:oldPasswordField]; +} + +- (void) showError +{ + // wake up the nib connections and adjust window size + [self window]; + // set up controls with info from associatedClient + [[containerView subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)]; + [containerView addSubview:errorView]; + // set badge + [errorBadge setBadgePath:associatedClient.path]; + + [self showWindow:nil]; +} + +- (IBAction) cancel: (id) sender +{ + [associatedClient didCancel]; + [self close]; +} + +- (IBAction) enterIdentity: (id) sender +{ + NSString *usernameString = [glueController valueForKeyPath:username_keypath]; + NSString *realmString = [glueController valueForKeyPath:realm_keypath]; + NSString *identityString = [NSString stringWithFormat:@"%@@%@", usernameString, realmString]; + + // the principal must already be valid to get this far + [associatedClient didEnterIdentity:identityString]; +} + +- (IBAction) answerAuthPrompt: (id) sender +{ + NSString *responseString = [glueController valueForKeyPath:prompt_response_keypath]; + NSNumber *saveResponse = [glueController valueForKeyPath:should_save_password_keypath]; + + if (!saveResponse) { + saveResponse = [NSNumber numberWithBool:NO]; + } + [associatedClient didPromptForAuth:responseString + saveResponse:saveResponse]; +} + +- (IBAction) changePassword: (id) sender +{ + NSString *oldString = [glueController valueForKeyPath:old_password_keypath]; + NSString *newString = [glueController valueForKeyPath:new_password_keypath]; + NSString *verifyString = [glueController valueForKeyPath:verify_password_keypath]; + + [associatedClient didChangePassword:oldString + newPassword:newString + verifyPassword:verifyString]; +} + +- (IBAction) showedError: (id) sender +{ + [associatedClient didHandleError]; +} + +@end diff --git a/src/kim/agent/mac/BadgedImageView.h b/src/kim/agent/mac/BadgedImageView.h index 714c13ef5..4fba86727 100644 --- a/src/kim/agent/mac/BadgedImageView.h +++ b/src/kim/agent/mac/BadgedImageView.h @@ -33,6 +33,7 @@ - (id) initWithFrame: (NSRect) frameRect; - (void) dealloc; - (void) setBadgeImage: (NSImage *) image; +- (void) setBadgePath: (NSString *) path; - (void) drawRect: (NSRect) rect; @end diff --git a/src/kim/agent/mac/BadgedImageView.m b/src/kim/agent/mac/BadgedImageView.m index b408130e3..167130aca 100644 --- a/src/kim/agent/mac/BadgedImageView.m +++ b/src/kim/agent/mac/BadgedImageView.m @@ -60,6 +60,14 @@ // --------------------------------------------------------------------------- +- (void) setBadgePath: (NSString *) path +{ + NSImage *newImage = [[NSWorkspace sharedWorkspace] iconForFile: path]; + [self setBadgeImage: newImage]; +} + +// --------------------------------------------------------------------------- + - (void) drawRect: (NSRect) rect { float frameSize = [self frame].size.width; diff --git a/src/kim/agent/mac/IPCClient.h b/src/kim/agent/mac/IPCClient.h new file mode 100644 index 000000000..531f7cbe4 --- /dev/null +++ b/src/kim/agent/mac/IPCClient.h @@ -0,0 +1,51 @@ +// +// IPCClient.h +// Kerberos5 +// +// Created by Justin Anderson on 9/28/08. +// Copyright 2008 MIT. All rights reserved. +// + +#import +#import "KIMUtilities.h" + +@class SelectIdentityController; +@class AuthenticationController; + +@interface IPCClient : NSObject { + mach_port_t port; + NSString *name; + NSString *path; + NSInteger state; + NSMutableDictionary *currentInfo; + + SelectIdentityController *selectController; + AuthenticationController *authController; +} + +@property (assign) mach_port_t port; +@property (readwrite, retain) NSString *name; +@property (readwrite, retain) NSString *path; +@property (assign) NSInteger state; +@property (readwrite, retain) NSMutableDictionary *currentInfo; +@property (readonly, retain) SelectIdentityController *selectController; +@property (readonly, retain) AuthenticationController *authController; + +- (void) cleanup; + +- (kim_error) selectIdentity: (NSDictionary *) info; +- (kim_error) enterIdentity: (NSDictionary *) info; +- (kim_error) promptForAuth: (NSDictionary *) info; +- (kim_error) changePassword: (NSDictionary *) info; +- (kim_error) handleError: (NSDictionary *) info; + +- (void) didCancel; +- (void) didSelectIdentity: (NSString *) identityString; +- (void) didEnterIdentity: (NSString *) identityString; +- (void) didPromptForAuth: (NSString *) responseString saveResponse: (NSNumber *) saveResponse; +- (void) didChangePassword: (NSString *) oldPassword + newPassword: (NSString *) newPassword + verifyPassword: (NSString *) verifyPassword; +- (void) didHandleError; + +@end diff --git a/src/kim/agent/mac/IPCClient.m b/src/kim/agent/mac/IPCClient.m new file mode 100644 index 000000000..4cb10fc27 --- /dev/null +++ b/src/kim/agent/mac/IPCClient.m @@ -0,0 +1,189 @@ +// +// IPCClient.m +// Kerberos5 +// +// Created by Justin Anderson on 9/28/08. +// Copyright 2008 MIT. All rights reserved. +// + +#import "IPCClient.h" +#import "SelectIdentityController.h" +#import "AuthenticationController.h" +#import "KerberosAgentListener.h" + +enum krb_agent_client_state { + ipc_client_state_idle, + ipc_client_state_init, + ipc_client_state_enter, + ipc_client_state_select, + ipc_client_state_auth_prompt, + ipc_client_state_change_password, + ipc_client_state_handle_error, + ipc_client_state_fini, +}; + +@interface IPCClient () + +@property (readwrite, retain) SelectIdentityController *selectController; +@property (readwrite, retain) AuthenticationController *authController; + +@end + + +@implementation IPCClient + +@synthesize port; +@synthesize name; +@synthesize path; +@synthesize state; +@synthesize currentInfo; +@synthesize selectController; +@synthesize authController; + +- (BOOL) isEqual: (IPCClient *) otherClient +{ + return (self.port == otherClient.port); +} + +- (NSUInteger) hash +{ + return self.port; +} + +- (id) init +{ + self = [super init]; + if (self != nil) { + self.state = ipc_client_state_init; + self.selectController = [[[SelectIdentityController alloc] init] autorelease]; + self.authController = [[[AuthenticationController alloc] init] autorelease]; + self.selectController.associatedClient = self; + self.authController.associatedClient = self; + } + return self; +} + +- (void) cleanup +{ + [self.selectController close]; + [self.authController close]; +} + +- (void) didCancel +{ + kim_error err = KIM_USER_CANCELED_ERR; + if (self.state == ipc_client_state_select) { + [KerberosAgentListener didSelectIdentity:self.currentInfo error:err]; + } + else if (self.state == ipc_client_state_enter) { + [KerberosAgentListener didEnterIdentity:self.currentInfo error:err]; + } + else if (self.state == ipc_client_state_select) { + [KerberosAgentListener didSelectIdentity:self.currentInfo error:err]; + } + else if (self.state == ipc_client_state_auth_prompt) { + [KerberosAgentListener didPromptForAuth:self.currentInfo error:err]; + } + else if (self.state == ipc_client_state_change_password) { + [KerberosAgentListener didChangePassword:self.currentInfo error:err]; + } + [self.selectController close]; + [self.authController close]; + self.state = ipc_client_state_idle; +} + +- (kim_error) selectIdentity: (NSDictionary *) info +{ + self.currentInfo = [[info mutableCopy] autorelease]; + self.state = ipc_client_state_select; + + [self.selectController showWindow:nil]; + + return 0; +} + +- (void) didSelectIdentity: (NSString *) identityString +{ + [self.currentInfo setObject:identityString forKey:@"identity_string"]; + + [KerberosAgentListener didSelectIdentity:self.currentInfo error:0]; + + // clean up state + self.currentInfo = nil; + self.state = ipc_client_state_idle; +} + +- (kim_error) enterIdentity: (NSDictionary *) info +{ + self.currentInfo = [[info mutableCopy] autorelease]; + self.state = ipc_client_state_enter; + + [self.authController setContent:self.currentInfo]; + [self.authController showEnterIdentity]; + + return 0; +} + +- (void) didEnterIdentity: (NSString *) identityString +{ + [self.currentInfo setObject:identityString forKey:@"identity_string"]; + [KerberosAgentListener didEnterIdentity:self.currentInfo error:0]; +} + +- (kim_error) promptForAuth: (NSDictionary *) info +{ + self.currentInfo = [[info mutableCopy] autorelease]; + self.state = ipc_client_state_auth_prompt; + + [self.authController setContent:self.currentInfo]; + [self.authController showAuthPrompt]; + + return 0; +} + +- (void) didPromptForAuth: (NSString *) responseString saveResponse: (NSNumber *) saveResponse +{ + [self.currentInfo setObject:responseString forKey:@"prompt_response"]; + [self.currentInfo setObject:saveResponse forKey:@"save_response"]; + [KerberosAgentListener didPromptForAuth:self.currentInfo error:0]; +} + +- (kim_error) changePassword: (NSDictionary *) info +{ + self.currentInfo = [[info mutableCopy] autorelease]; + self.state = ipc_client_state_change_password; + + [self.authController setContent:self.currentInfo]; + [self.authController showChangePassword]; + + return 0; +} + +- (void) didChangePassword: (NSString *) oldPassword + newPassword: (NSString *) newPassword + verifyPassword: (NSString *) verifyPassword +{ + [self.currentInfo setObject:oldPassword forKey:@"old_password"]; + [self.currentInfo setObject:newPassword forKey:@"new_password"]; + [self.currentInfo setObject:verifyPassword forKey:@"verify_password"]; + [KerberosAgentListener didChangePassword:self.currentInfo error:0]; +} + + +- (kim_error) handleError: (NSDictionary *) info +{ + self.currentInfo = [[info mutableCopy] autorelease]; + self.state = ipc_client_state_handle_error; + + [self.authController setContent:self.currentInfo]; + [self.authController showError]; + + return 0; +} + +- (void) didHandleError +{ + [KerberosAgentListener didHandleError:self.currentInfo error:0]; +} + +@end diff --git a/src/kim/agent/mac/Identities.m b/src/kim/agent/mac/Identities.m index 54a4947dc..5dcd32939 100644 --- a/src/kim/agent/mac/Identities.m +++ b/src/kim/agent/mac/Identities.m @@ -204,7 +204,7 @@ self.favorite = true; } else { kim_string_create_for_last_error(&error_string, err); - NSLog(@"%s failed with %s", _cmd, error_string); + NSLog(@"%s failed with %s", __FUNCTION__, error_string); } return (err != KIM_NO_ERROR); } @@ -237,7 +237,7 @@ self.favorite = false; } else { kim_string_create_for_last_error(&error_string, err); - NSLog(@"%s failed with %s", _cmd, error_string); + NSLog(@"%s failed with %s", __FUNCTION__, error_string); } return (err != KIM_NO_ERROR); } @@ -695,12 +695,13 @@ err = cc_context_wait_for_change (context); if (!err) { - NSLog (@"%s thread noticed update", __FUNCTION__); + // NSLog (@"%s thread noticed update", __FUNCTION__); } else { - NSLog (@"%s thread got error %d (%s)", __FUNCTION__, err, [NSString stringForLastKIMError:err]); + // NSLog (@"%s thread got error %d (%s)", __FUNCTION__, err, [KIMUtilities stringForLastKIMError:err]); err = 0; /* The server quit unexpectedly -- just try again */ } - + + //NSLog(@"waited %@", [[NSThread currentThread] description]); [(Identities *) [connection rootProxy] update]; sleep (1); } @@ -708,7 +709,7 @@ if (context) { cc_context_release (context); } } - NSLog (@"%s thread exiting", __FUNCTION__); +// NSLog (@"%s thread exiting", __FUNCTION__); [pool release]; } @@ -856,7 +857,7 @@ } // Build list of identities with existing ccaches - +// NSLog(@"updating %@", [[NSThread currentThread] description]); while (!err) { kim_ccache ccache = NULL; kim_identity kimIdentity = NULL; diff --git a/src/kim/agent/mac/KIMUtilities.h b/src/kim/agent/mac/KIMUtilities.h index 68774952c..9b735b3e2 100644 --- a/src/kim/agent/mac/KIMUtilities.h +++ b/src/kim/agent/mac/KIMUtilities.h @@ -11,11 +11,14 @@ #define log_kim_error_to_console(err)\ {\ -NSLog(@"%s got error %@", _cmd, [NSString stringForLastKIMError:err]);\ +NSLog(@"%s got error %@", _cmd, [KIMUtilities stringForLastKIMError:err]);\ } while (0); -@interface NSString (KIMUtilities) +@interface KIMUtilities : NSObject + (NSString *) stringForLastKIMError: (kim_error) in_err; ++ (BOOL) validatePrincipalWithName: (NSString *) name + realm: (NSString *) realm; + @end diff --git a/src/kim/agent/mac/KIMUtilities.m b/src/kim/agent/mac/KIMUtilities.m index 07c85e8e8..1326c137f 100644 --- a/src/kim/agent/mac/KIMUtilities.m +++ b/src/kim/agent/mac/KIMUtilities.m @@ -8,7 +8,7 @@ #import "KIMUtilities.h" -@implementation NSString (KIMUtilities) +@implementation KIMUtilities + (NSString *) stringForLastKIMError: (kim_error) in_err { @@ -25,4 +25,27 @@ return result; } ++ (BOOL) validatePrincipalWithName: (NSString *) name + realm: (NSString *) realm +{ + kim_error err = KIM_NO_ERROR; + kim_identity identity = NULL; + NSString *principal = nil; + + if (!name || !realm || [name length] == 0) { + err = KIM_BAD_PRINCIPAL_STRING_ERR; + } + if (!err) { + principal = [[NSString alloc] initWithFormat:@"%@@%@", name, realm]; + err = kim_identity_create_from_string(&identity, [principal UTF8String]); + [principal release]; + } + if (!identity) { + err = KIM_BAD_PRINCIPAL_STRING_ERR; + } + kim_identity_free(&identity); + + return (err == KIM_NO_ERROR); +} + @end diff --git a/src/kim/agent/mac/KerberosAgent-Info.plist b/src/kim/agent/mac/KerberosAgent-Info.plist index d3379ce0d..6f851e91d 100644 --- a/src/kim/agent/mac/KerberosAgent-Info.plist +++ b/src/kim/agent/mac/KerberosAgent-Info.plist @@ -22,5 +22,7 @@ MainMenu NSPrincipalClass NSApplication + LSUIElement + diff --git a/src/kim/agent/mac/KerberosAgentController.h b/src/kim/agent/mac/KerberosAgentController.h index 42992dbf3..876961163 100644 --- a/src/kim/agent/mac/KerberosAgentController.h +++ b/src/kim/agent/mac/KerberosAgentController.h @@ -24,9 +24,29 @@ #import +@class IPCClient; +@class AuthenticationController; @interface KerberosAgentController : NSObject { - + NSMutableArray *clients; + NSTimer *autoQuitTimer; } +@property (readwrite, retain) NSMutableArray *clients; + +- (void) quitIfIdle: (NSTimer *) timer; + +- (IPCClient *)clientForPort:(mach_port_t)client_port; +- (IPCClient *)clientForInfo:(NSDictionary *)info; + +- (IBAction) fakeANewClient: (id) sender; + +- (void) addClient: (NSDictionary *) info; +- (void) enterIdentity: (NSDictionary *) info; +- (void) selectIdentity: (NSDictionary *) info; +- (void) promptForAuth: (NSDictionary *) info; +- (void) changePassword: (NSDictionary *) info; +- (void) handleError: (NSDictionary *) info; +- (void) removeClient: (NSDictionary *) info; + @end diff --git a/src/kim/agent/mac/KerberosAgentController.m b/src/kim/agent/mac/KerberosAgentController.m index 20b74015a..5e973ea5e 100644 --- a/src/kim/agent/mac/KerberosAgentController.m +++ b/src/kim/agent/mac/KerberosAgentController.m @@ -24,20 +24,206 @@ #import "KerberosAgentController.h" #import "SelectIdentityController.h" +#import "AuthenticationController.h" +#import "KerberosAgentListener.h" +#import "IPCClient.h" +#import "ServerDemux.h" +#define SECONDS_BEFORE_AUTO_QUIT_ON_START 5 +#define SECONDS_BEFORE_AUTO_QUIT_ON_NO_CLIENTS 1 @implementation KerberosAgentController +@synthesize clients; + // --------------------------------------------------------------------------- - (void) applicationDidFinishLaunching: (NSNotification *) notification { - SelectIdentityController *controller = [[SelectIdentityController alloc] init]; - int result = [controller runWindow]; - if (result != 0) { - NSLog(@"SelectIdentityController -runWindow result was %d", result); + self.clients = [NSMutableArray array]; + [KerberosAgentListener startListening]; + + [NSApp activateIgnoringOtherApps:YES]; + + autoQuitTimer = [NSTimer scheduledTimerWithTimeInterval:SECONDS_BEFORE_AUTO_QUIT_ON_START + target:self + selector:@selector(quitIfIdle:) + userInfo:nil + repeats:NO]; +} + +- (void) dealloc +{ + self.clients = nil; + [autoQuitTimer invalidate]; + [autoQuitTimer release]; + + [super dealloc]; +} + +- (void) quitIfIdle: (NSTimer *) timer +{ + if ([self.clients count] == 0) { + NSLog(@"No active clients. Terminating."); + [NSApp terminate:nil]; } + autoQuitTimer = nil; +} + +- (IPCClient *)clientForPort:(mach_port_t)client_port +{ + IPCClient *aClient = nil; + + for (aClient in self.clients) { + if (aClient.port == client_port) { + break; + } + } + + return aClient; +} +- (IPCClient *)clientForInfo:(NSDictionary *)info +{ + mach_port_t client_port = [[info objectForKey:@"client_port"] integerValue]; + return [self clientForPort:client_port]; +} + +- (IBAction) fakeANewClient: (id) sender +{ + IPCClient *aClient = [[IPCClient alloc] init]; + aClient.port = 1; + aClient.name = @"Barry"; + aClient.path = [[NSBundle mainBundle] bundlePath]; + [self.clients addObject:aClient]; + [aClient release]; +} + +#pragma mark Client actions + +// init +- (void) addClient: (NSDictionary *) info +{ + int32_t err = 0; + IPCClient *aClient = [self clientForInfo:info]; + + if (aClient) { + // already registered + err = KIM_IDENTITY_ALREADY_IN_LIST_ERR; + } else { + aClient = [[IPCClient alloc] init]; + aClient.port = [[info objectForKey:@"client_port"] integerValue]; + aClient.name = [info objectForKey:@"name"]; + aClient.path = [info objectForKey:@"path"]; + [self.clients addObject:aClient]; + [aClient release]; + } + + [autoQuitTimer invalidate]; + + [KerberosAgentListener didAddClient:info error:err]; + [info release]; +} + +// enter +- (void) enterIdentity: (NSDictionary *) info +{ + kim_error err = KIM_NO_ERROR; + IPCClient *aClient = nil; + + // get client object for matching info, creating if it doesn't exist + aClient = [self clientForInfo:info]; + if (!aClient) { err = KIM_IDENTITY_NOT_IN_LIST_ERR; } + else { + err = [aClient enterIdentity:info]; + } + if (err) { + [KerberosAgentListener didEnterIdentity:info error:err]; + } +} + +// select +- (void) selectIdentity: (NSDictionary *) info +{ + kim_error err = KIM_NO_ERROR; + IPCClient *aClient = nil; + + // get client object for matching info, creating if it doesn't exist + aClient = [self clientForInfo:info]; + if (!aClient) { err = KIM_IDENTITY_NOT_IN_LIST_ERR; } + else { + err = [aClient selectIdentity:info]; + } + if (err) { + [KerberosAgentListener didSelectIdentity:info error:err]; + } +} + +// auth +- (void) promptForAuth: (NSDictionary *) info +{ + kim_error err = KIM_NO_ERROR; + IPCClient *aClient = nil; + + aClient = [self clientForInfo:info]; + if (!aClient) { err = KIM_IDENTITY_NOT_IN_LIST_ERR; } + else { + err = [aClient promptForAuth:info]; + } +} + +// change password +- (void) changePassword: (NSDictionary *) info +{ + kim_error err = KIM_NO_ERROR; + IPCClient *aClient = nil; + + aClient = [self clientForInfo:info]; + if (!aClient) { err = KIM_IDENTITY_NOT_IN_LIST_ERR; } + else { + err = [aClient changePassword:info]; + } +} + +// error +- (void) handleError: (NSDictionary *) info +{ + kim_error err = KIM_NO_ERROR; + IPCClient *aClient = nil; + + aClient = [self clientForInfo:info]; + if (!aClient) { err = KIM_IDENTITY_NOT_IN_LIST_ERR; } + else { + err = [aClient handleError:info]; + } +} + +// fini +- (void) removeClient: (NSDictionary *) info +{ + kim_error err = KIM_NO_ERROR; + IPCClient *aClient = [self clientForInfo:info]; + + if (!aClient) { + err = KIM_IDENTITY_NOT_IN_LIST_ERR; + } else { + // close all windows associated with it + [aClient cleanup]; + [self.clients removeObject:aClient]; + if ([self.clients count] == 0) { + // the client removes itself after select identity, + // but might come back shortly afterward in need of an auth prompt + autoQuitTimer = [NSTimer scheduledTimerWithTimeInterval:SECONDS_BEFORE_AUTO_QUIT_ON_NO_CLIENTS + target:self + selector:@selector(quitIfIdle:) + userInfo:nil + repeats:NO]; + } + } + + // called after user finishes prompt + [KerberosAgentListener didRemoveClient:info error:err]; + [info release]; } @end diff --git a/src/kim/agent/mac/KerberosAgentListener.h b/src/kim/agent/mac/KerberosAgentListener.h new file mode 100644 index 000000000..f58ce3324 --- /dev/null +++ b/src/kim/agent/mac/KerberosAgentListener.h @@ -0,0 +1,91 @@ +// +// KerberosAgentListener.h +// Kerberos5 +// +// Created by Justin Anderson on 9/28/08. +// Copyright 2008 MIT. All rights reserved. +// + +#import +#import + +#define kCheckinMessage 100 + +@interface KerberosAgentListener : NSObject { + NSThread *thread; +} + +@property (readwrite, retain) NSThread *thread; + ++ (KerberosAgentListener *) sharedListener; + ++ (void) startListening; + +- (void) threadMain; + ++ (void) addClientWithPort: (mach_port_t) client_port + replyPort: (mach_port_t) reply_port + name: (kim_string) name + path: (kim_string) path; + +// contains reply_port ++ (void) didAddClient: (NSDictionary *) info + error: (int32_t) error; + ++ (void) enterIdentityWithClientPort: (mach_port_t) client_port + replyPort: (mach_port_t) reply_port; + +// contains reply_port, kim_identity ++ (void) didEnterIdentity: (NSDictionary *) info + error: (int32_t) error; + ++ (void) selectIdentityWithClientPort: (mach_port_t) client_port + replyPort: (mach_port_t) reply_port + hints: (kim_selection_hints) hints; + +// contains reply_port, kim_identity ++ (void) didSelectIdentity: (NSDictionary *) info + error: (int32_t) error; + ++ (void) promptForAuthWithClientPort: (mach_port_t) client_port + replyPort: (mach_port_t) reply_port + identity: (kim_string) identity_string + promptType: (uint32_t) prompt_type + allowSave: (kim_boolean) allow_save + hideReply: (kim_boolean) hide_reply + title: (kim_string) title + message: (kim_string) message + description: (kim_string) description; + +// contains reply_port, (string) prompt_response ++ (void) didPromptForAuth: (NSDictionary *) info + error: (int32_t) error; + ++ (void) changePasswordWithClientPort: (mach_port_t) client_port + replyPort: (mach_port_t) reply_port + identity: (kim_string) identity_string + expired: (kim_boolean) expired; + +// contains reply_port, old password, new password, verify password ++ (void) didChangePassword: (NSDictionary *) info + error: (int32_t) error; + ++ (void) handleErrorWithClientPort: (mach_port_t) client_port + replyPort: (mach_port_t) reply_port + identity: (kim_string) identity_string + error: (kim_error) error + message: (kim_string) message + description: (kim_string) description; + +// contains reply_port ++ (void) didHandleError: (NSDictionary *) info + error: (int32_t) error; + + ++ (void) removeClientMatchingPort: (mach_port_t) client_port + replyPort: (mach_port_t) reply_port; + ++ (void) didRemoveClient: (NSDictionary *)info + error: (int32_t) error; + +@end diff --git a/src/kim/agent/mac/KerberosAgentListener.m b/src/kim/agent/mac/KerberosAgentListener.m new file mode 100644 index 000000000..f76599726 --- /dev/null +++ b/src/kim/agent/mac/KerberosAgentListener.m @@ -0,0 +1,316 @@ +// +// KerberosAgentListener.m +// Kerberos5 +// +// Created by Justin Anderson on 9/28/08. +// Copyright 2008 MIT. All rights reserved. +// + +#import "KerberosAgentListener.h" +#import "KIMUtilities.h" +#import "ServerDemux.h" +#import "IPCClient.h" + +@implementation KerberosAgentListener + +@synthesize thread; + +- (id) init +{ + self = [super init]; + if (self != nil) { + self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadMain) object:nil]; + } + return self; +} + +static KerberosAgentListener *sharedListener = nil; + ++ (KerberosAgentListener *) sharedListener +{ + @synchronized(self) { + if (sharedListener == nil) { + [[self alloc] init]; // assignment not done here + } + } + return sharedListener; +} + ++ (id)allocWithZone:(NSZone *)zone +{ + @synchronized(self) { + if (sharedListener == nil) { + sharedListener = [super allocWithZone:zone]; + return sharedListener; // assignment and return on first allocation + } + } + return nil; //on subsequent allocation attempts return nil +} + +- (id)copyWithZone:(NSZone *)zone +{ + return self; +} + +- (id)retain +{ + return self; +} + +- (unsigned)retainCount +{ + return UINT_MAX; //denotes an object that cannot be released +} + +- (void)release +{ + //do nothing +} + +- (id)autorelease +{ + return self; +} + +#pragma mark Thread management + +// called from main thread to start listen thread ++ (void) startListening +{ +// NSLog(@"%s %@ thread", __FUNCTION__, ([NSThread isMainThread]) ? @"main" : @"not main"); + + [[KerberosAgentListener sharedListener].thread start]; +} + +- (void) threadMain +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + int32_t err = 0; + +// NSLog(@"%s starting up", __FUNCTION__); + + while (!err && ![self.thread isCancelled]) { + err = kim_agent_listen_loop(); + if (!err) { +// NSLog (@"%s loop resetting %@", __FUNCTION__, [[NSThread currentThread] description]); + } else { + NSLog (@"%s got error %d (%@) %@", __FUNCTION__, err, [KIMUtilities stringForLastKIMError:err], [[NSThread currentThread] description]); + err = 0; /* The server quit unexpectedly -- just try again */ + } + sleep(10); + } + + [pool release]; +} + +#pragma mark IPC handlers + ++ (void) addClientWithPort: (mach_port_t) client_port + replyPort: (mach_port_t) reply_port + name: (kim_string) name + path: (kim_string) path +{ + NSDictionary *info = [[NSDictionary alloc] initWithObjectsAndKeys: + [NSNumber numberWithInteger:client_port], @"client_port", + [NSNumber numberWithInteger:reply_port], @"reply_port", + [NSString stringWithUTF8String:name], @"name", + [NSString stringWithUTF8String:path], @"path", + nil]; + [[NSApp delegate] performSelectorOnMainThread:@selector(addClient:) + withObject:info + waitUntilDone:NO]; +} + ++ (void) didAddClient: (NSDictionary *) info + error: (kim_error) error +{ + kim_error err = KIM_NO_ERROR; + mach_port_t reply_port = [[info objectForKey:@"reply_port"] integerValue]; + err = kim_handle_reply_init(reply_port, error); +} + ++ (void) enterIdentityWithClientPort: (mach_port_t) client_port + replyPort: (mach_port_t) reply_port +{ + NSDictionary *info = [[NSDictionary alloc] initWithObjectsAndKeys: + [NSNumber numberWithInteger:client_port], @"client_port", + [NSNumber numberWithInteger:reply_port], @"reply_port", + nil]; + [[NSApp delegate] performSelectorOnMainThread:@selector(enterIdentity:) + withObject:info + waitUntilDone:NO]; +} + +// contains reply_port, identity_string ++ (void) didEnterIdentity: (NSDictionary *) info + error: (kim_error) error +{ + kim_error err = KIM_NO_ERROR; + mach_port_t reply_port = [[info objectForKey:@"reply_port"] integerValue]; + kim_identity identity = NULL; + + if (!err) { + err = kim_identity_create_from_string(&identity, [[info objectForKey:@"identity_string"] UTF8String]); + } + + err = kim_handle_reply_enter_identity(reply_port, identity, error); +} + ++ (void) selectIdentityWithClientPort: (mach_port_t) client_port + replyPort: (mach_port_t) reply_port + hints: (kim_selection_hints) hints +{ + kim_error err = KIM_NO_ERROR; + kim_selection_hints new_hints = NULL; + NSDictionary *info = nil; + + // new hints will be freed by main thread + err = kim_selection_hints_copy(&new_hints, hints); + + info = [[NSDictionary alloc] initWithObjectsAndKeys: + [NSNumber numberWithInteger:client_port], @"client_port", + [NSNumber numberWithInteger:reply_port], @"reply_port", + [NSValue valueWithPointer:new_hints], @"hints", + nil]; + + [[NSApp delegate] performSelectorOnMainThread:@selector(selectIdentity:) + withObject:info + waitUntilDone:NO]; +} + +// contains reply_port, identity_string ++ (void) didSelectIdentity: (NSDictionary *) info + error: (int32_t) error +{ + kim_error err = KIM_NO_ERROR; + NSNumber *portNumber = [info objectForKey:@"reply_port"]; + NSString *identityString = [info objectForKey:@"identity_string"]; + mach_port_t reply_port = [portNumber integerValue]; + kim_identity identity = NULL; + kim_identity_create_from_string(&identity, (identityString) ? [identityString UTF8String] : ""); + + err = kim_handle_reply_select_identity(reply_port, identity, error); +} + ++ (void) promptForAuthWithClientPort: (mach_port_t) client_port + replyPort: (mach_port_t) reply_port + identity: (kim_string) identity_string + promptType: (uint32_t) prompt_type + allowSave: (kim_boolean) allow_save + hideReply: (kim_boolean) hide_reply + title: (kim_string) title + message: (kim_string) message + description: (kim_string) description +{ + NSDictionary *info = [[NSDictionary alloc] initWithObjectsAndKeys: + [NSNumber numberWithInteger:client_port], @"client_port", + [NSNumber numberWithInteger:reply_port], @"reply_port", + [NSString stringWithUTF8String:identity_string], @"identity_string", + [NSNumber numberWithUnsignedInt:prompt_type], @"prompt_type", + [NSNumber numberWithBool:allow_save], @"allow_save", + [NSNumber numberWithBool:hide_reply], @"hide_reply", + [NSString stringWithUTF8String:title], @"title", + [NSString stringWithUTF8String:message], @"message", + [NSString stringWithUTF8String:description], @"description", + nil]; + [[NSApp delegate] performSelectorOnMainThread:@selector(promptForAuth:) + withObject:info + waitUntilDone:NO]; +} + +// contains reply_port, (string) prompt_response ++ (void) didPromptForAuth: (NSDictionary *) info + error: (int32_t) error +{ + kim_error err = KIM_NO_ERROR; + mach_port_t reply_port = [[info objectForKey:@"reply_port"] integerValue]; + kim_string prompt_response = [[info objectForKey:@"prompt_response"] UTF8String]; + kim_boolean save_response = [[info objectForKey:@"save_response"] boolValue]; + + err = kim_handle_reply_auth_prompt(reply_port, prompt_response, save_response, error); +} + ++ (void) changePasswordWithClientPort: (mach_port_t) client_port + replyPort: (mach_port_t) reply_port + identity: (kim_string) identity_string + expired: (kim_boolean) expired +{ + NSDictionary *info = [[NSDictionary alloc] initWithObjectsAndKeys: + [NSNumber numberWithInteger:client_port], @"client_port", + [NSNumber numberWithInteger:reply_port], @"reply_port", + [NSString stringWithUTF8String:identity_string], @"identity_string", + [NSNumber numberWithBool:expired], @"expired", + nil]; + [[NSApp delegate] performSelectorOnMainThread:@selector(changePassword:) + withObject:info + waitUntilDone:NO]; +} + +// contains reply_port, old password, new password, verify password ++ (void) didChangePassword: (NSDictionary *) info + error: (int32_t) error +{ + kim_error err = KIM_NO_ERROR; + mach_port_t reply_port = [[info objectForKey:@"reply_port"] integerValue]; + kim_string old_pw = [[info objectForKey:@"old_password"] UTF8String]; + kim_string new_pw = [[info objectForKey:@"new_password"] UTF8String]; + kim_string verify_pw = [[info objectForKey:@"verify_password"] UTF8String]; + + err = kim_handle_reply_change_password(reply_port, old_pw, new_pw, verify_pw, error); +} + ++ (void) handleErrorWithClientPort: (mach_port_t) client_port + replyPort: (mach_port_t) reply_port + identity: (kim_string) identity_string + error: (kim_error) error + message: (kim_string) message + description: (kim_string) description +{ + NSDictionary *info = [[NSDictionary alloc] initWithObjectsAndKeys: + [NSNumber numberWithInteger:client_port], @"client_port", + [NSNumber numberWithInteger:reply_port], @"reply_port", + [NSString stringWithUTF8String:identity_string], @"identity_string", + [NSNumber numberWithUnsignedInt:error], @"error", + [NSString stringWithUTF8String:message], @"message", + [NSString stringWithUTF8String:description], @"description", + nil]; + [[NSApp delegate] performSelectorOnMainThread:@selector(handleError:) + withObject:info + waitUntilDone:NO]; + +} + +// contains reply_port ++ (void) didHandleError: (NSDictionary *) info + error: (int32_t) error +{ + kim_error err = KIM_NO_ERROR; + mach_port_t reply_port = [[info objectForKey:@"reply_port"] integerValue]; + + err = kim_handle_reply_handle_error(reply_port, error); +} + ++ (void) removeClientMatchingPort: (mach_port_t) client_port + replyPort: (mach_port_t) reply_port +{ + NSDictionary *info = [[NSDictionary alloc] initWithObjectsAndKeys: + [NSNumber numberWithInteger:client_port], @"client_port", + [NSNumber numberWithInteger:reply_port], @"reply_port", + nil]; + [[NSApp delegate] performSelectorOnMainThread:@selector(removeClient:) + withObject:info + waitUntilDone:NO]; +} + +// contains reply_port ++ (void) didRemoveClient: (NSDictionary *)info + error: (int32_t) error +{ + kim_error err = KIM_NO_ERROR; + mach_port_t reply_port = [[info objectForKey:@"reply_port"] integerValue]; + if (reply_port) { + err = kim_handle_reply_fini(reply_port, error); + } +} + +@end diff --git a/src/kim/agent/mac/SelectIdentityController.h b/src/kim/agent/mac/SelectIdentityController.h index 01bcd9228..2251d2773 100644 --- a/src/kim/agent/mac/SelectIdentityController.h +++ b/src/kim/agent/mac/SelectIdentityController.h @@ -26,8 +26,11 @@ #import "BadgedImageView.h" #import "Identities.h" -@interface SelectIdentityController : NSWindowController { +@class IPCClient; +@interface SelectIdentityController : NSWindowController { + IPCClient *associatedClient; + IBOutlet NSObjectController *identitiesController; IBOutlet NSArrayController *identityArrayController; @@ -49,8 +52,12 @@ IBOutlet NSObjectController *identityOptionsController; IBOutlet NSTextField *nameField; IBOutlet NSTextField *realmField; + + IBOutlet NSButton *ticketOptionsOkButton; } +@property (readwrite, retain) IPCClient *associatedClient; + - (IBAction) newIdentity: (id) sender; - (IBAction) addToFavorites: (id) sender; - (IBAction) removeFromFavorites: (id) sender; @@ -63,14 +70,12 @@ - (IBAction) select: (id) sender; - (IBAction) cancel: (id) sender; -- (int) runWindow; +- (void)controlTextDidChange:(NSNotification *)aNotification; + - (void) showOptions: (NSString *) contextInfo; - (void) didEndSheet: (NSWindow *) sheet returnCode: (int) returnCode contextInfo: (void *) contextInfo; - (void) saveOptions; -- (void) reloadData; -- (void) refreshTable; - - (void) timedRefresh:(NSTimer *)timer; @end diff --git a/src/kim/agent/mac/SelectIdentityController.m b/src/kim/agent/mac/SelectIdentityController.m index e70189d68..897e0dcc7 100644 --- a/src/kim/agent/mac/SelectIdentityController.m +++ b/src/kim/agent/mac/SelectIdentityController.m @@ -23,22 +23,19 @@ */ #import "SelectIdentityController.h" +#import "IPCClient.h" #define identities_key_path @"identities" @implementation SelectIdentityController +@synthesize associatedClient; + // --------------------------------------------------------------------------- - (id) initWithWindowNibName: (NSString *) windowNibName { if ((self = [super initWithWindowNibName: windowNibName])) { - identities = [[Identities alloc] init]; - [identities addObserver:self - forKeyPath:identities_key_path - options:NSKeyValueObservingOptionNew - context:@"selectIdentityController"]; - refreshTimer = [NSTimer scheduledTimerWithTimeInterval:60.0 target:self selector:@selector(timedRefresh:) userInfo:nil repeats:true]; } return self; @@ -56,7 +53,6 @@ - (void) dealloc { [refreshTimer release]; - [identities removeObserver:self forKeyPath:identities_key_path]; [identities release]; [super dealloc]; } @@ -65,20 +61,36 @@ - (void) awakeFromNib { - [headerTextField setStringValue: @"Some header text"]; - [identityTableView setDoubleAction:@selector(select:)]; -} + NSString *key = nil; + NSString *message = nil; -// --------------------------------------------------------------------------- + // We need to float over the loginwindow and SecurityAgent so use its hardcoded level. + [[self window] center]; + [[self window] setLevel:2003]; -- (void) windowDidLoad -{ - [explanationTextField setStringValue: @"Some explanation text"]; + [identityTableView setDoubleAction:@selector(select:)]; + identities = [[Identities alloc] init]; [identitiesController setContent:identities]; -} + refreshTimer = [NSTimer scheduledTimerWithTimeInterval:60.0 target:self selector:@selector(timedRefresh:) userInfo:nil repeats:true]; + + [kerberosIconImageView setBadgePath:associatedClient.path]; + + if ([associatedClient.name isEqualToString:[[NSBundle mainBundle] bundlePath]]) { + key = @"SelectIdentityRequest"; + message = NSLocalizedStringFromTable(key, @"SelectIdentity", NULL); + } + else { + key = @"SelectIdentityApplicationRequest"; + message = [NSString stringWithFormat: + NSLocalizedStringFromTable(key, @"SelectIdentity", NULL), + associatedClient.name]; + } + [headerTextField setStringValue:message]; +} // --------------------------------------------------------------------------- + - (IBAction) newIdentity: (id) sender { Identity *newIdentity = [[Identity alloc] init]; @@ -87,7 +99,6 @@ identityOptionsController.content = newIdentity; [newIdentity release]; - NSLog(@"New identity %@", [newIdentity description]); [self showOptions:@"new"]; } @@ -97,7 +108,7 @@ { Identity *anIdentity = [identityArrayController.selectedObjects lastObject]; identityOptionsController.content = nil; - NSLog(@"Add %@ to favorites", [anIdentity description]); + anIdentity.favorite = TRUE; [self saveOptions]; @@ -109,7 +120,7 @@ { Identity *anIdentity = [identityArrayController.selectedObjects lastObject]; identityOptionsController.content = anIdentity; - NSLog(@"Remove %@ from favorites", [anIdentity description]); + anIdentity.favorite = FALSE; [self saveOptions]; @@ -119,18 +130,22 @@ - (IBAction) select: (id) sender { + Identity *selectedIdentity = nil; + // ignore double-click on header if ([sender respondsToSelector:@selector(clickedRow)] && [sender clickedRow] < 0) { return; } - NSLog(@"Select identity: %@", identityArrayController.selectedObjects.description); + selectedIdentity = [[identityArrayController selectedObjects] lastObject]; + + [associatedClient didSelectIdentity: selectedIdentity.principalString]; } // --------------------------------------------------------------------------- - (IBAction) cancel: (id) sender { - NSLog(@"Cancel identity selection"); + [associatedClient didCancel]; } // --------------------------------------------------------------------------- @@ -176,21 +191,12 @@ [NSApp endSheet: identityOptionsWindow]; } -// --------------------------------------------------------------------------- - -- (int) runWindow +- (void)controlTextDidChange:(NSNotification *)aNotification { - //[[NSApp delegate] addActiveWindow: [self window]]; - //NSWindow *window = [self window]; - - //[window center]; - [self showWindow: self]; -// [NSApp run]; -// [self close]; - - //[[NSApp delegate] removeActiveWindow: [self window]]; - - return 0; + BOOL valid = [KIMUtilities validatePrincipalWithName:[nameField stringValue] + realm:[realmField stringValue]]; + [ticketOptionsOkButton setEnabled:valid]; + [ticketOptionsOkButton setNeedsDisplay]; } // --------------------------------------------------------------------------- @@ -219,9 +225,8 @@ Identity *newIdentity = identityOptionsController.content; err = [identities addIdentity:newIdentity]; -#warning Add validation to prevent the addition of existing principals and invalid principals if (err) { - NSLog(@"%s received error %@ trying to add identity %@", _cmd, [NSString stringForLastKIMError:err], [newIdentity description]); + NSLog(@"%s received error %@ trying to add identity %@", _cmd, [KIMUtilities stringForLastKIMError:err], [newIdentity description]); } [self saveOptions]; } @@ -258,52 +263,10 @@ // --------------------------------------------------------------------------- -- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context -{ - if ([(NSString *) context isEqualToString:@"selectIdentityController"]) { - if ([keyPath isEqualToString:identities_key_path]) { -// [self reloadData]; - } - } - else { - [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; - } -} - -// --------------------------------------------------------------------------- - -- (void) reloadData -{ - // Preserve selection - Identity *selectedIdentity = [[identityArrayController.selectedObjects lastObject] retain]; - NSUInteger a, b, c; - a = [identityArrayController selectionIndex]; - b = [[identityArrayController content] count]; - c = NSNotFound; - - NSLog(@"== updating table == %s", _cmd); - identityArrayController.content = identities.identities; - - c = [identityArrayController.content indexOfObject:selectedIdentity]; - if ([[identityArrayController content] count] >= a) - [identityArrayController setSelectionIndex:(c == NSNotFound) ? (a > b) ? b : a : c]; - - [selectedIdentity release]; -} - -// --------------------------------------------------------------------------- - -- (void) refreshTable -{ - [identityArrayController rearrangeObjects]; -} - -// --------------------------------------------------------------------------- - - (void) timedRefresh:(NSTimer *)timer { // refetch data to update expiration times - [self refreshTable]; + [identityArrayController rearrangeObjects]; } @end diff --git a/src/kim/agent/mac/ServerDemux.m b/src/kim/agent/mac/ServerDemux.m index ed4e73d1d..b24011d74 100644 --- a/src/kim/agent/mac/ServerDemux.m +++ b/src/kim/agent/mac/ServerDemux.m @@ -24,6 +24,7 @@ #import "ServerDemux.h" #import "kim_selection_hints_private.h" +#import "KerberosAgentListener.h" // --------------------------------------------------------------------------- @@ -91,10 +92,11 @@ static int32_t kim_handle_request_init (mach_port_t in_client_port, } if (!err) { -#warning Send init message to main thread with 2 ports, name and path - NSLog (@"Got init message with pid '%d', name '%s', path '%s'", - pid, name, path); - err = kim_handle_reply_init (in_reply_port, 0); + // performs selector on main thread + [KerberosAgentListener addClientWithPort:in_client_port + replyPort:in_reply_port + name:name + path:path]; } k5_ipc_stream_free_string (name); @@ -139,14 +141,9 @@ static int32_t kim_handle_request_enter_identity (mach_port_t in_client_port, int32_t err = 0; if (!err) { -#warning Send enter identity message to main thread with 2 ports - kim_identity identity = NULL; - NSLog (@"Got enter identity message"); - err = kim_identity_create_from_string (&identity, "nobody@TEST-KERBEROS-1.3.1"); - if (!err) { - err = kim_handle_reply_enter_identity (in_reply_port, identity, 0); - } - kim_identity_free (&identity); + // performs selector on main thread + [KerberosAgentListener enterIdentityWithClientPort:in_client_port + replyPort:in_reply_port]; } return err; @@ -162,7 +159,7 @@ int32_t kim_handle_reply_enter_identity (mach_port_t in_reply_port, k5_ipc_stream reply = NULL; kim_string identity_string = NULL; - if (!err) { + if (!err && !in_error) { err = kim_identity_get_string (in_identity, &identity_string); } @@ -198,21 +195,17 @@ static int32_t kim_handle_request_select_identity (mach_port_t in_client_port, { int32_t err = 0; kim_selection_hints hints = NULL; - + if (!err) { err = kim_selection_hints_create_from_stream (&hints, in_request_stream); } if (!err) { -#warning Send select identity message to main thread with 2 ports - kim_identity identity = NULL; - NSLog (@"Got select identity message"); - err = kim_identity_create_from_string (&identity, "nobody@TEST-KERBEROS-1.3.1"); - if (!err) { - err = kim_handle_reply_select_identity (in_reply_port, identity, 0); - } - kim_identity_free (&identity); + // performs selector on main thread + [KerberosAgentListener selectIdentityWithClientPort:in_client_port + replyPort:in_reply_port + hints:hints]; } kim_selection_hints_free (&hints); @@ -230,7 +223,7 @@ int32_t kim_handle_reply_select_identity (mach_port_t in_reply_port, k5_ipc_stream reply = NULL; kim_string identity_string = NULL; - if (!err) { + if (!err && !in_error) { err = kim_identity_get_string (in_identity, &identity_string); } @@ -302,10 +295,16 @@ static int32_t kim_handle_request_auth_prompt (mach_port_t in_client_port, } if (!err) { - NSLog (@"Got auth prompt with identity '%s', type '%d', allow_save_reply '%d', hide '%d', title '%s', message '%s', description '%s'", - identity_string, type, allow_save_reply, hide_reply, title, message, description); - err = kim_handle_reply_auth_prompt (in_reply_port, "ydobon", 0, 0); -#warning Send auth prompt message to main thread with 2 ports and arguments + // performs selector on main thread + [KerberosAgentListener promptForAuthWithClientPort:in_client_port + replyPort:in_reply_port + identity:identity_string + promptType:type + allowSave:allow_save_reply + hideReply:hide_reply + title:title + message:message + description:description]; } k5_ipc_stream_free_string (identity_string); @@ -373,13 +372,11 @@ static int32_t kim_handle_request_change_password (mach_port_t in_client_port, } if (!err) { -#warning Send change password message to main thread with 2 ports and arguments - NSLog (@"Got change password with identity '%s', old_password_expired '%d'", - identity_string, old_password_expired); - err = kim_handle_reply_change_password (in_reply_port, - "ydobon", - "foo", - "bar", 0); + // performs selector on main thread + [KerberosAgentListener changePasswordWithClientPort:in_client_port + replyPort:in_reply_port + identity:identity_string + expired:old_password_expired]; } k5_ipc_stream_free_string (identity_string); @@ -458,10 +455,13 @@ static int32_t kim_handle_request_handle_error (mach_port_t in_client_port, } if (!err) { -#warning Send handle error message to main thread with 2 ports and arguments - NSLog (@"Got handle error with identity '%s', error '%d', message '%s', description '%s'", - identity_string, error, message, description); - err = kim_handle_reply_handle_error (in_reply_port, 0); + // performs selector on main thread + [KerberosAgentListener handleErrorWithClientPort:in_client_port + replyPort:in_reply_port + identity:identity_string + error:error + message:message + description:description]; } k5_ipc_stream_free_string (identity_string); @@ -505,12 +505,8 @@ static int32_t kim_handle_request_fini (mach_port_t in_client_port, k5_ipc_stream in_request_stream) { int32_t err = 0; - - if (!err) { -#warning Send fini message to main thread with 2 ports - NSLog (@"Got fini message"); - err = kim_handle_reply_fini (in_reply_port, 0); - } + + [KerberosAgentListener removeClientMatchingPort:in_client_port replyPort:in_reply_port]; return err; } @@ -564,7 +560,7 @@ int32_t k5_ipc_server_remove_client (mach_port_t in_client_port) if (!err) { /* Client exited. Main thread should check for windows belonging to * in_client_port and close any it finds. */ -#warning Insert code to handle client death here + [KerberosAgentListener removeClientMatchingPort:in_client_port replyPort:0]; } return err; diff --git a/src/kim/agent/mac/main.m b/src/kim/agent/mac/main.m index dec694093..1e887a003 100644 --- a/src/kim/agent/mac/main.m +++ b/src/kim/agent/mac/main.m @@ -1,27 +1,6 @@ #import -#import "ServerDemux.h" -#import "k5_mig_server.h" -#include -#include - - -int main(int argc, const char *argv[]) +int main(int argc, char *argv[]) { - int err = 0; - NSAutoreleasePool *pool = NULL; - - openlog (argv[0], LOG_CONS | LOG_PID, LOG_AUTH); - syslog (LOG_INFO, "Starting up."); - - pool = [[NSAutoreleasePool alloc] init]; - - [NSApplication sharedApplication]; - [NSBundle loadNibNamed: @"MainMenu" owner: NSApp]; - - err = k5_ipc_server_listen_loop (); - - syslog (LOG_NOTICE, "Exiting: %s (%d)", kipc_error_string (err), err); - - return err; + return NSApplicationMain(argc, (const char **) argv); } diff --git a/src/kim/agent/mac/resources/English.lproj/Authentication.xib b/src/kim/agent/mac/resources/English.lproj/Authentication.xib index 67d1b41a7..6b2aad9cd 100644 --- a/src/kim/agent/mac/resources/English.lproj/Authentication.xib +++ b/src/kim/agent/mac/resources/English.lproj/Authentication.xib @@ -2,14 +2,17 @@ 1050 - 9E17 - 670 - 949.33 + 9F33 + 672 + 949.34 352.00 YES - + + + + YES @@ -18,7 +21,7 @@ YES - NSApplication + AuthenticationController FirstResponder @@ -26,6 +29,39 @@ NSApplication + + + YES + username + realm + old_password + new_password + verify_password + title + message + description + allow_save_password + realm_history + should_save_password + isPrincipalValid + prompt_response + isPromptValid + isChangePasswordValid + save_response + allow_save + forwardable + addressless + renewable + valid_lifetime + renewal_lifetime + min_renewal_lifetime + max_renewal_lifetime + max_valid_lifetime + min_valid_lifetime + + YES + + 274 @@ -33,8 +69,8 @@ YES - 282 - {{101, 126}, {382, 40}} + 264 + {{101, 126}, {382, 54}} YES @@ -143,7 +179,7 @@ YES - + 1.200000e+01 1.000000e+01 1.000000e+03 @@ -264,54 +300,112 @@ 25 - + - 292 - {{18, 16}, {51, 27}} + 268 + {{20, 126}, {64, 64}} + + BadgedImageView + + + + -2147483356 + {{20, 17}, {38, 26}} YES - - 67239424 - 134217728 - - - LucidaGrande - 1.000000e+01 - 16 - - - -2033434369 - 2 - - NSImage - Gear - + + -2076049856 + 134219776 + + + -2030944001 + 34 400 75 + + + YES + + + 2147483647 + 1 + + NSImage + NSActionTemplate + + + NSImage + NSMenuCheckmark + + + NSImage + NSMenuMixedState + + _popUpItemAction: + + + YES + + OtherViews + + YES + + + + VGlja2V0IE9wdGlvbnPigKY + + 2147483647 + + + _popUpItemAction: + + + + + YES + YES + + + 2147483647 + + + _popUpItemAction: + + + + + QWJvdXQgS2VyYmVyb3PigKY + + 2147483647 + + + _popUpItemAction: + + + + + YES + 1 + YES + YES + 2 - - - 268 - {{20, 126}, {64, 64}} - - BadgedImageView - {500, 210} NSView - + 274 YES - 290 + 266 {{104, 82}, {376, 22}} YES @@ -370,7 +464,7 @@ - 282 + 266 {{101, 126}, {382, 41}} YES @@ -386,7 +480,7 @@ - 292 + 268 {{17, 84}, {80, 17}} YES @@ -402,7 +496,7 @@ - 294 + 290 {{102, 58}, {380, 18}} YES @@ -414,11 +508,11 @@ 1211912703 130 - + NSImage NSSwitch - + NSSwitch @@ -436,10 +530,11 @@ {500, 210} + NSView - + 274 YES @@ -563,65 +658,25 @@ {500, 210} + NSView - - 268 + + 286 YES 268 - {{20, 126}, {64, 64}} + {{20, 146}, {64, 64}} BadgedImageView - - - 289 - {{404, 12}, {82, 32}} - - YES - - 67239424 - 134217728 - Yes - - - -2038284033 - 1 - - - DQ - 200 - 25 - - - - - 289 - {{322, 12}, {82, 32}} - - YES - - 67239424 - 134217728 - No - - - -2038284033 - 129 - - - 200 - 25 - - 274 - {{101, 60}, {382, 76}} + {{101, 146}, {382, 19}} YES @@ -640,7 +695,7 @@ 266 - {{101, 144}, {382, 17}} + {{101, 173}, {382, 17}} YES @@ -653,8 +708,150 @@ + + + 290 + {{161, 116}, {319, 22}} + + YES + + -1804468671 + 272630784 + + + + YES + + + + + + + 292 + {{62, 118}, {94, 17}} + + YES + + 68288064 + 71304192 + Old Password: + + + + + + + + + 292 + {{57, 90}, {99, 17}} + + YES + + 68288064 + 71304192 + New Password: + + + + + + + + + 292 + {{17, 62}, {139, 17}} + + YES + + 68288064 + 71304192 + Verify New Password: + + + + + + + + + 290 + {{161, 88}, {319, 22}} + + YES + + -1804468671 + 272630784 + + + + YES + + + + + + + 290 + {{161, 60}, {319, 22}} + + YES + + -1804468671 + 272630784 + + + + YES + + + + + + + 289 + {{388, 12}, {98, 32}} + + YES + + 67239424 + 134217728 + Continue + + + -2038284033 + 1 + + + DQ + 200 + 25 + + + + + 289 + {{295, 12}, {93, 32}} + + YES + + 67239424 + 134217728 + Cancel + + + -2038284033 + 1 + + + Gw + 200 + 25 + + - {500, 210} + {500, 230} + NSView @@ -725,32 +922,12 @@ 25 - - - 268 - {{314, 12}, {90, 32}} - - YES - - 67239424 - 134217728 - Cancel - - - -2038284033 - 129 - - - 200 - 25 - - {500, 210} NSView - 13 + 5 2 {{378, 236}, {500, 210}} 1886912512 @@ -774,56 +951,237 @@ {600, 422} - - Menu - - YES - - - VGlja2V0IE9wdGlvbnPigKY - - 1048576 - 2147483647 - - NSImage - NSMenuCheckmark - - - NSImage - NSMenuMixedState - - - - - Q2hhbmdlIFBhc3N3b3Jk4oCmA - - 1048576 - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - About Kerberos... - - 1048576 - 2147483647 - - - - - + + 1 + 2 + {{21, 50}, {430, 283}} + 1886912512 + Kerberos Ticket Options + NSWindow + + View + + {3.40282e+38, 3.40282e+38} + {430, 283} + + + 256 + + YES + + + 266 + {{38, 214}, {374, 25}} + + YES + + 67501824 + 0 + + + + + Helvetica + 1.200000e+01 + 16 + + + 1.000000e+02 + 0.000000e+00 + 0.000000e+00 + 0.000000e+00 + 11 + 0 + NO + NO + + + + + 268 + {{17, 246}, {396, 17}} + + YES + + 67239424 + 4194304 + Get tickets that are valid for: + + + + + + + + + 266 + {{37, 192}, {376, 14}} + + YES + + 67239424 + 4325376 + 10 hours + + + + + + + + + 266 + {{37, 82}, {375, 25}} + + YES + + 67501824 + 0 + + + + + + 1.000000e+05 + 0.000000e+00 + 0.000000e+00 + 0.000000e+00 + 11 + 0 + NO + NO + + + + + 268 + {{18, 134}, {394, 18}} + + YES + + 67239424 + 0 + Get tickets without IP addresses (NAT mode) + + + 1211912703 + 2 + + + + + 200 + 25 + + + + + 268 + {{18, 156}, {394, 18}} + + YES + + 67239424 + 0 + Get tickets that can be forwarded to other computers + + + 1211912703 + 2 + + + + + 200 + 25 + + + + + 268 + {{18, 112}, {394, 18}} + + YES + + 67239424 + 0 + Get tickets that can be renewed for: + + + 1211912703 + 2 + + + + + 200 + 25 + + + + + 266 + {{36, 60}, {377, 14}} + + YES + + 67239424 + 4325376 + 7 days + + + + + + + + + 256 + {{334, 12}, {82, 32}} + + YES + + 67239424 + 134217728 + OK + + + -2038284033 + 1 + + + DQ + 200 + 25 + + + + + 256 + {{252, 12}, {82, 32}} + + YES + + 67239424 + 134217728 + Cancel + + + -2038284033 + 1 + + + Gw + 200 + 25 + + + + {430, 283} + + {{0, 0}, {1440, 878}} + {430, 305} + {3.40282e+38, 3.40282e+38} @@ -847,33 +1205,862 @@ - menu - - + identityView + + - 300259 + 300295 - menu - - + passwordView + + - 300261 + 300296 - - - - YES - - 0 - - YES - - - + + + samView + + - + 300297 + + + + expiredPasswordView + + + + 300298 + + + + value: selection.message + + + + + + value: selection.message + value + selection.message + 2 + + + 300301 + + + + value: selection.message + + + + + + value: selection.message + value + selection.message + 2 + + + 300303 + + + + value: selection.description + + + + + + value: selection.description + value + selection.description + 2 + + + 300304 + + + + glueController + + + + 300326 + + + + window + + + + 300339 + + + + contentValues: selection.realm_history + + + + + + contentValues: selection.realm_history + contentValues + selection.realm_history + 2 + + + 300342 + + + + value: selection.message + + + + + + value: selection.message + value + selection.message + 2 + + + 300344 + + + + enterIdentity: + + + + 300347 + + + + cancel: + + + + 300348 + + + + value: selection.username + + + + + + value: selection.username + value + selection.username + + NSContinuouslyUpdatesValue + + + 2 + + + 300349 + + + + value: selection.realm + + + + + + value: selection.realm + value + selection.realm + + NSContinuouslyUpdatesValue + + + + 2 + + + 300350 + + + + enabled: selection.isPrincipalValid + + + + + + enabled: selection.isPrincipalValid + enabled + selection.isPrincipalValid + 2 + + + 300351 + + + + usernameField + + + + 300352 + + + + enterBadge + + + + 300353 + + + + passwordField + + + + 300354 + + + + cancel: + + + + 300359 + + + + passwordBadge + + + + 300360 + + + + answerAuthPrompt: + + + + 300361 + + + + rememberPasswordInKeychainCheckBox + + + + 300364 + + + + enabled: selection.isPromptValid + + + + + + enabled: selection.isPromptValid + enabled + selection.isPromptValid + 2 + + + 300365 + + + + value: selection.prompt_response + + + + + + value: selection.prompt_response + value + selection.prompt_response + + NSContinuouslyUpdatesValue + + + 2 + + + 300368 + + + + samBadge + + + + 300369 + + + + samPromptField + + + + 300370 + + + + answerAuthPrompt: + + + + 300371 + + + + cancel: + + + + 300372 + + + + value: selection.title + + + + + + value: selection.title + value + selection.title + 2 + + + 300373 + + + + value: selection.message + + + + + + value: selection.message + value + selection.message + 2 + + + 300374 + + + + value: selection.description + + + + + + value: selection.description + value + selection.description + 2 + + + 300375 + + + + value: selection.prompt_response + + + + + + value: selection.prompt_response + value + selection.prompt_response + + NSContinuouslyUpdatesValue + + + 2 + + + 300377 + + + + enabled: selection.isPromptValid + + + + + + enabled: selection.isPromptValid + enabled + selection.isPromptValid + 2 + + + 300378 + + + + errorView + + + + 300379 + + + + errorBadge + + + + 300380 + + + + showedError: + + + + 300381 + + + + changePasswordView + + + + 300382 + + + + changePasswordBadge + + + + 300383 + + + + changePassword: + + + + 300403 + + + + cancel: + + + + 300404 + + + + value: selection.message + + + + + + value: selection.message + value + selection.message + 2 + + + 300405 + + + + value: selection.description + + + + + + value: selection.description + value + selection.description + 2 + + + 300406 + + + + oldPasswordField + + + + 300407 + + + + nextKeyView + + + + 300408 + + + + nextKeyView + + + + 300409 + + + + nextKeyView + + + + 300410 + + + + enabled: selection.isChangePasswordValid + + + + + + enabled: selection.isChangePasswordValid + enabled + selection.isChangePasswordValid + 2 + + + 300411 + + + + value: selection.old_password + + + + + + value: selection.old_password + value + selection.old_password + + NSContinuouslyUpdatesValue + + + 2 + + + 300412 + + + + value: selection.new_password + + + + + + value: selection.new_password + value + selection.new_password + + NSContinuouslyUpdatesValue + + + 2 + + + 300413 + + + + value: selection.verify_password + + + + + + value: selection.verify_password + value + selection.verify_password + + NSContinuouslyUpdatesValue + + + 2 + + + 300414 + + + + containerView + + + + 300415 + + + + value: selection.save_response + + + + + + value: selection.save_response + value + selection.save_response + 2 + + + 300416 + + + + hidden: selection.allow_save + + + + + + hidden: selection.allow_save + hidden + selection.allow_save + + NSValueTransformerName + NSNegateBoolean + + 2 + + + 300417 + + + + takeObjectValueFrom: + + + + 300441 + + + + takeObjectValueFrom: + + + + 300442 + + + + delegate + + + + 300443 + + + + delegate + + + + 300444 + + + + value: selection.forwardable + + + + + + value: selection.forwardable + value + selection.forwardable + 2 + + + 300445 + + + + value: selection.addressless + + + + + + value: selection.addressless + value + selection.addressless + 2 + + + 300446 + + + + value: selection.renewable + + + + + + value: selection.renewable + value + selection.renewable + 2 + + + 300447 + + + + enabled: selection.renewable + + + + + + enabled: selection.renewable + enabled + selection.renewable + 2 + + + 300450 + + + + maxValue: selection.max_renewal_lifetime + + + + + + maxValue: selection.max_renewal_lifetime + maxValue + selection.max_renewal_lifetime + 2 + + + 300453 + + + + minValue: selection.min_renewal_lifetime + + + + + + minValue: selection.min_renewal_lifetime + minValue + selection.min_renewal_lifetime + + 2 + + + 300454 + + + + value: selection.renewal_lifetime + + + + + + value: selection.renewal_lifetime + value + selection.renewal_lifetime + + 2 + + + 300455 + + + + maxValue: selection.max_valid_lifetime + + + + + + maxValue: selection.max_valid_lifetime + maxValue + selection.max_valid_lifetime + 2 + + + 300456 + + + + minValue: selection.min_valid_lifetime + + + + + + minValue: selection.min_valid_lifetime + minValue + selection.min_valid_lifetime + + 2 + + + 300458 + + + + value: selection.valid_lifetime + + + + + + value: selection.valid_lifetime + value + selection.valid_lifetime + + 2 + + + 300459 + + + + + YES + + 0 + + YES + + + + + -2 @@ -909,39 +2096,6 @@ Application - - 300030 - - - YES - - - - - - - GearMenu - - - 300031 - - - - - 300032 - - - - - 300033 - - - - - 300034 - - - 300157 @@ -949,13 +2103,13 @@ YES - + Enter Identity @@ -978,15 +2132,6 @@ - - 300164 - - - YES - - - - 300162 @@ -1062,11 +2207,6 @@ - - 300166 - - - 300167 @@ -1292,23 +2432,20 @@ YES - - + + + + + + + + - Expired Password - - - 300278 - - - YES - - - + Change Password 300275 @@ -1333,20 +2470,6 @@ - - 300284 - - - YES - - - - - - 300285 - - - 300287 @@ -1357,18 +2480,12 @@ - - 300281 - - - 300211 YES - @@ -1385,15 +2502,6 @@ - - 300290 - - - YES - - - - 300212 @@ -1427,16 +2535,347 @@ - - 300291 - - - 300216 + + 300300 + + + Glue Controller + + + 300312 + + + YES + + + + + + 300313 + + + YES + + + + + + 300314 + + + YES + + + + + + + + + 300316 + + + + + 300317 + + + + + 300320 + + + + + 300384 + + + YES + + + + + + 300385 + + + YES + + + + + + 300386 + + + + + 300387 + + + + + 300388 + + + YES + + + + + + 300389 + + + + + 300390 + + + YES + + + + + + 300391 + + + + + 300392 + + + YES + + + + + + 300393 + + + + + 300394 + + + YES + + + + + + 300395 + + + + + 300396 + + + YES + + + + + + 300397 + + + YES + + + + + + 300398 + + + + + 300399 + + + + + 300419 + + + YES + + + + Options + + + 300420 + + + YES + + + + + + + + + + + + + + + 300421 + + + YES + + + + + + 300422 + + + YES + + + + + + 300423 + + + YES + + + + + + 300424 + + + YES + + + + + + 300425 + + + YES + + + + + + 300426 + + + YES + + + + + + 300427 + + + YES + + + + + + 300428 + + + YES + + + + + + 300429 + + + YES + + + + + + 300430 + + + YES + + + + + + 300431 + + + + + 300432 + + + + + 300433 + + + + + 300434 + + + + + 300435 + + + + + 300436 + + + + + 300437 + + + + + 300438 + + + + + 300439 + + + + + 300440 + + + + + 300464 + + + @@ -1457,17 +2896,6 @@ 19.windowTemplate.minSize 20.IBPluginDependency 20.ImportedFromIB2 - 300030.IBEditorWindowLastContentRect - 300030.IBPluginDependency - 300030.ImportedFromIB2 - 300031.IBPluginDependency - 300031.ImportedFromIB2 - 300032.IBPluginDependency - 300032.ImportedFromIB2 - 300033.IBPluginDependency - 300033.ImportedFromIB2 - 300034.IBPluginDependency - 300034.ImportedFromIB2 300157.IBEditorWindowLastContentRect 300157.IBPluginDependency 300158.IBPluginDependency @@ -1482,9 +2910,6 @@ 300162.ImportedFromIB2 300163.IBPluginDependency 300163.ImportedFromIB2 - 300164.CustomClassName - 300164.IBPluginDependency - 300164.ImportedFromIB2 300165.IBPluginDependency 300175.IBEditorWindowLastContentRect 300175.IBPluginDependency @@ -1532,117 +2957,207 @@ 300274.IBEditorWindowLastContentRect 300274.IBPluginDependency 300275.IBPluginDependency - 300278.IBPluginDependency - 300278.ImportedFromIB2 - 300284.IBPluginDependency - 300285.IBPluginDependency 300286.IBPluginDependency 300286.ImportedFromIB2 300288.IBPluginDependency 300288.ImportedFromIB2 - 300290.IBPluginDependency - 300291.IBPluginDependency + 300300.IBPluginDependency + 300312.IBPluginDependency + 300313.IBPluginDependency + 300314.IBEditorWindowLastContentRect + 300314.IBPluginDependency + 300316.IBPluginDependency + 300317.IBPluginDependency + 300320.IBPluginDependency + 300384.CustomClassName + 300384.IBPluginDependency + 300384.ImportedFromIB2 + 300385.IBPluginDependency + 300386.IBPluginDependency + 300388.IBPluginDependency + 300389.IBPluginDependency + 300390.IBPluginDependency + 300391.IBPluginDependency + 300392.CustomClassName + 300392.IBPluginDependency + 300392.ImportedFromIB2 + 300394.CustomClassName + 300394.IBPluginDependency + 300394.ImportedFromIB2 + 300396.IBPluginDependency + 300396.ImportedFromIB2 + 300397.IBPluginDependency + 300397.ImportedFromIB2 + 300419.IBEditorWindowLastContentRect + 300419.IBPluginDependency + 300419.IBWindowTemplateEditedContentRect + 300419.ImportedFromIB2 + 300419.windowTemplate.hasMaxSize + 300419.windowTemplate.hasMinSize + 300419.windowTemplate.maxSize + 300419.windowTemplate.minSize + 300420.IBPluginDependency + 300420.ImportedFromIB2 + 300421.IBPluginDependency + 300421.ImportedFromIB2 + 300422.IBPluginDependency + 300422.ImportedFromIB2 + 300423.IBPluginDependency + 300423.ImportedFromIB2 + 300424.IBPluginDependency + 300424.ImportedFromIB2 + 300425.IBPluginDependency + 300425.ImportedFromIB2 + 300426.IBPluginDependency + 300426.ImportedFromIB2 + 300427.IBPluginDependency + 300427.ImportedFromIB2 + 300428.IBPluginDependency + 300428.ImportedFromIB2 + 300429.IBPluginDependency + 300429.ImportedFromIB2 + 300430.IBPluginDependency + 300430.ImportedFromIB2 + 300464.IBPluginDependency YES com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - {{696, 646}, {500, 210}} + {{628, 646}, {500, 210}} com.apple.InterfaceBuilder.CocoaPlugin - {{696, 646}, {500, 210}} - - + {{628, 646}, {500, 210}} + + {{932, 664}, {484, 199}} - - + + {600, 400} {484, 199} com.apple.InterfaceBuilder.CocoaPlugin - - {{557, 1049}, {203, 73}} + + {{602, 432}, {500, 210}} + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - - {{602, 364}, {500, 210}} + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + {{477, 611}, {500, 210}} + com.apple.InterfaceBuilder.CocoaPlugin + NSSecureTextField com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - PopupButton com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - {{547, 453}, {500, 210}} + {{628, 163}, {500, 210}} com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + NSSecureTextField com.apple.InterfaceBuilder.CocoaPlugin - + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{553, 638}, {500, 210}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + {{512, 496}, {500, 230}} com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - {{557, 743}, {500, 210}} + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{611, 402}, {176, 73}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - NSSecureTextField com.apple.InterfaceBuilder.CocoaPlugin - + + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - {{546, 628}, {500, 210}} + NSSecureTextField + com.apple.InterfaceBuilder.CocoaPlugin + + NSSecureTextField com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin - + + {{579, 449}, {430, 283}} com.apple.InterfaceBuilder.CocoaPlugin - + {{579, 449}, {430, 283}} + + + + {3.40282e+38, 3.40282e+38} + {430, 283} com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - - {{619, 442}, {500, 210}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin @@ -1666,42 +3181,94 @@ - 300293 + 300467 YES - BadgedImageView - NSView - - IBProjectSource - ../Sources/kim/agent/mac/BadgedImageView.h + AuthenticationController + NSWindowController + + YES + + YES + answerAuthPrompt: + cancel: + changePassword: + enterIdentity: + showedError: + + + YES + id + id + id + id + id + + + + YES + + YES + changePasswordBadge + changePasswordView + containerView + enterBadge + errorBadge + errorView + expiredPasswordView + glueController + identityView + oldPasswordField + passwordBadge + passwordField + passwordView + rememberPasswordInKeychainCheckBox + samBadge + samPromptField + samView + usernameField + + + YES + BadgedImageView + NSView + NSView + BadgedImageView + BadgedImageView + NSView + NSView + NSObjectController + NSView + NSTextField + BadgedImageView + NSTextField + NSView + NSButton + BadgedImageView + NSTextField + NSView + NSTextField + - - - FirstResponder - NSObject - IBUserSource - + IBProjectSource + ../Sources/kim/agent/mac/AuthenticationController.h - PopupButton - NSButton + BadgedImageView + NSView IBProjectSource - ../Sources/kim/agent/mac/PopupButton.h + ../Sources/kim/agent/mac/BadgedImageView.h - PopupButton - NSButton - - popupMenu - NSMenu - + FirstResponder + NSObject IBUserSource diff --git a/src/kim/agent/mac/resources/English.lproj/AuthenticationController.strings b/src/kim/agent/mac/resources/English.lproj/AuthenticationController.strings new file mode 100644 index 000000000..fc0f2f711 Binary files /dev/null and b/src/kim/agent/mac/resources/English.lproj/AuthenticationController.strings differ diff --git a/src/kim/agent/mac/resources/English.lproj/MainMenu.xib b/src/kim/agent/mac/resources/English.lproj/MainMenu.xib index da6d9efed..f56ab6337 100644 --- a/src/kim/agent/mac/resources/English.lproj/MainMenu.xib +++ b/src/kim/agent/mac/resources/English.lproj/MainMenu.xib @@ -2,12 +2,13 @@ 1050 - 9E17 - 670 - 949.33 + 9F33 + 672 + 949.34 352.00 YES + YES @@ -57,114 +58,6 @@ - - - YES - YES - - - 1048576 - 2147483647 - - - - - - UHJlZmVyZW5jZXPigKY - , - 1048576 - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Services - - 1048576 - 2147483647 - - - submenuAction: - - - Services - - - YES - - _NSServicesMenu - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Hide KerberosAgent - h - 1048576 - 2147483647 - - - - - - Hide Others - h - 1572864 - 2147483647 - - - - - - Show All - - 1048576 - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Quit KerberosAgent - q - 1048576 - 2147483647 - - - _NSAppleMenu @@ -387,14 +280,6 @@ 122 - - - terminate: - - - - 139 - orderFrontStandardAboutPanel: @@ -403,30 +288,6 @@ 142 - - - hideOtherApplications: - - - - 146 - - - - hide: - - - - 152 - - - - unhideAllApplications: - - - - 153 - cut: @@ -523,6 +384,14 @@ 300448 + + + delegate + + + + 300468 + @@ -622,16 +491,6 @@ YES - - - - - - - - - - @@ -640,65 +499,6 @@ - - 129 - - - - - 131 - - - YES - - - - - - 130 - - - - - 134 - - - - - 136 - - - - - 143 - - - - - 144 - - - - - 145 - - - - - 149 - - - - - 150 - - - - - 202 - - - 103 @@ -819,26 +619,6 @@ 106.editorWindowContentRectSynchronizationRect 111.IBPluginDependency 111.ImportedFromIB2 - 129.IBPluginDependency - 129.ImportedFromIB2 - 130.IBPluginDependency - 130.ImportedFromIB2 - 131.IBPluginDependency - 131.ImportedFromIB2 - 134.IBPluginDependency - 134.ImportedFromIB2 - 136.IBPluginDependency - 136.ImportedFromIB2 - 143.IBPluginDependency - 143.ImportedFromIB2 - 144.IBPluginDependency - 144.ImportedFromIB2 - 145.IBPluginDependency - 145.ImportedFromIB2 - 149.IBPluginDependency - 149.ImportedFromIB2 - 150.IBPluginDependency - 150.ImportedFromIB2 156.IBPluginDependency 156.ImportedFromIB2 157.IBPluginDependency @@ -851,6 +631,7 @@ 163.ImportedFromIB2 164.IBPluginDependency 164.ImportedFromIB2 + 169.IBEditorWindowLastContentRect 169.IBPluginDependency 169.ImportedFromIB2 169.editorWindowContentRectSynchronizationRect @@ -862,8 +643,6 @@ 173.ImportedFromIB2 19.IBPluginDependency 19.ImportedFromIB2 - 202.IBPluginDependency - 202.ImportedFromIB2 203.IBPluginDependency 203.ImportedFromIB2 210.IBPluginDependency @@ -886,6 +665,7 @@ 5.ImportedFromIB2 56.IBPluginDependency 56.ImportedFromIB2 + 57.IBEditorWindowLastContentRect 57.IBPluginDependency 57.ImportedFromIB2 57.editorWindowContentRectSynchronizationRect @@ -915,26 +695,7 @@ com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - + {{630, 563}, {253, 173}} com.apple.InterfaceBuilder.CocoaPlugin {{1022, 430}, {253, 173}} @@ -958,10 +719,8 @@ com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - {{1066, 510}, {197, 93}} - {{492, 1079}, {314, 20}} + {{492, 736}, {314, 20}} com.apple.InterfaceBuilder.CocoaPlugin {{884, 603}, {314, 20}} @@ -970,6 +729,7 @@ com.apple.InterfaceBuilder.CocoaPlugin + {{504, 713}, {202, 23}} com.apple.InterfaceBuilder.CocoaPlugin {{896, 420}, {240, 183}} @@ -997,7 +757,7 @@ - 300448 + 300484 @@ -1013,6 +773,10 @@ KerberosAgentController NSObject + + fakeANewClient: + id + IBProjectSource ../Sources/kim/agent/mac/KerberosAgentController.h diff --git a/src/kim/agent/mac/resources/English.lproj/SelectIdentity.strings b/src/kim/agent/mac/resources/English.lproj/SelectIdentity.strings new file mode 100644 index 000000000..6eb2109b2 Binary files /dev/null and b/src/kim/agent/mac/resources/English.lproj/SelectIdentity.strings differ diff --git a/src/kim/agent/mac/resources/English.lproj/SelectIdentity.xib b/src/kim/agent/mac/resources/English.lproj/SelectIdentity.xib index bf65e894d..3a1fb160e 100644 --- a/src/kim/agent/mac/resources/English.lproj/SelectIdentity.xib +++ b/src/kim/agent/mac/resources/English.lproj/SelectIdentity.xib @@ -8,8 +8,8 @@ 352.00 YES - + YES @@ -464,7 +464,6 @@ - 1 YES 2 YES @@ -818,7 +817,7 @@ YES - 67239424 + 604110336 134217728 OK @@ -1653,6 +1652,30 @@ 300455 + + + ticketOptionsOkButton + + + + 300459 + + + + delegate + + + + 300460 + + + + delegate + + + + 300461 + @@ -2373,10 +2396,10 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - {{360, 291}, {427, 378}} + {{553, 305}, {427, 378}} com.apple.InterfaceBuilder.CocoaPlugin - {{360, 291}, {427, 378}} - + {{553, 305}, {427, 378}} + {10000, 354} com.apple.InterfaceBuilder.CocoaPlugin @@ -2464,7 +2487,7 @@ - 300455 + 300461 @@ -2547,6 +2570,7 @@ nameField realmField selectIdentityButton + ticketOptionsOkButton YES @@ -2565,6 +2589,7 @@ NSTextField NSTextField NSButton + NSButton