Авторизация для всех
Mac OS X является многопользовательской системой, которая может использоваться во множестве сред, каждого с радикально различными требованиями авторизации. Например, iMac, установленная в доме Вашего дяди, является абсолютно открытой системой, тогда как чрезвычайно ограничивается iMac, установленная в университетской лаборатории.
Нет никакого способа, которым, разработчик приложений, можно сделать решения об авторизации, которые являются подходящими для всех этих сред. Вместо этого необходимо позволить системному администратору принять те решения. Можно использовать Authorization Services, чтобы сделать это безопасным, стандартным способом.
Этот technote предназначен для всех разработчиков приложений Mac OS X. Это представляет определенный интерес для тех разработчиков, не использовавших Authorization Services в прошлом. Это объясняет, как можно добавить поддержку авторизации приложению, не ставя под угрозу опыт из поля для традиционных клиентов.
Введение
Большинство разработчиков, создающих приложения для Mac OS X только, рассматривает проблемы авторизации, когда модель полномочий системы препятствует тому, чтобы они выполнили свою работу. Using Authorization Services для реализации этого вида приложения (названный системным ограниченным приложением в документации Authorization Services) хорошо понята, и существует пример программы, BetterAuthorizationSample, демонстрирующие это ясно. Однако Authorization Services разработана для больше, чем просто это. Когда Вы пишете приложение Mac OS X, даже если оно не имеет никакой ограниченной системой функциональности, необходимо рассмотреть, можно ли использовать в своих интересах Authorization Services. В частности необходимо думать о том, мог ли бы системный администратор хотеть ограничить некоторые функции приложения к некоторому подмножеству пользователей. Рассмотрите следующие примеры:
Предположите, что Вы пишете CD горящее приложение. Администратор школьной системы мог бы хотеть позволить всем пользователям записывать данные CDs, но только определенные пользователи для записи аудиокомпакт-дисков.
Предположите запись потребительского приложения ТВ-тюнера. Вы могли бы хотеть позволить родителю (системный администратор, в этом случае) ограничивать определенные каналы от дочерних элементов.
Важно подчеркнуть, что системная модель полномочий не предотвращает эти виды действия. Например, насколько драйвер CD затронут, любому пользователю разрешают записать диски. Однако можно использовать Authorization Services для добавления уровня авторизации к приложению, которое тогда известно как самоограниченное приложение.
Возможность создать самоограниченное приложение всегда была частью Authorization Services. Однако до версии 10.3 Mac OS X, не было никакого способа сделать это и поддержать простой опыт из поля (т.е. никакие диалоговые окна пароля) ожидаемый теми пользователями, администрирующими их собственную систему. Версия 10.3 Mac OS X представила новые подпрограммы Authorization Services, дающие Вам лучший из обоих миров.
Можно использовать Authorization Services так, чтобы системный администратор, если Вы существуете, мог ограничить доступ к определенным частям приложения.
Однако по умолчанию эти ограничения не причиняют беспокойство пользователям, администрирующим их собственные системы.
Следующие восемь разделов объясняют, как достигнуть этого идеального решения на версии 10.3 Mac OS X и позже. Последний раздел, Поддерживая Более ранние Системы, объясняет, как достигнуть аналогичного решения на более ранних версиях Mac OS X.
Поддержка версии 10.3 Mac OS X и Позже
Следующие разделы объясняют, как можно использовать Authorization Services на версии 10.3 Mac OS X и позже реализовать самоограниченное приложение, никогда не выводящее на экран диалоговое окно пароля, если системный администратор не конфигурирует его, чтобы сделать так. Первый шаг должен определить Ваше пользовательское право авторизации. Затем необходимо изменить приложение для получения того права. Для понимания, почему это иногда переводит диалоговое окно авторизации в рабочее состояние необходимо понять базу данных политики авторизации. Как только Вы понимаете базу данных, можно изменить ее для включения правильной спецификации для пользовательского права, или вручную или программно. Наконец, можно изучить приблизительно два общих глюка при использовании этого метода, а именно, как поддерживать локализуемую подсказку для пользовательского права и как работать вокруг ошибки в Authorization Services.
Определите свое право авторизации
Первый шаг должен выяснить, какие операции пользовательского уровня системный администратор мог бы хотеть ограничить и определить правильное имя для каждой из этих авторизованных операций. Правильные имена формируют иерархическое пространство имен с помощью обратной нотации DNS. В этом примере я буду использовать правильное имя, показанное в Перечислении 1, перечисляющем название компании сначала com.apple.
тогда моя организация в компании dts.
тогда название продукта SelfRestrictedSample.
и наконец работа в том продукте (в этом примере это лишено воображения именовано xyz
).
Перечисление 1 , Определяющее правильное имя.
const char kXYZRightName[] = "com.apple.dts.SelfRestrictedSample.xyz"; |
Обычно Вы определяете одно право на авторизованную работу. Например, CD горящее приложение мог бы определить com.SurfSoftware.SurfBurner.Burn.Audio
и com.SurfSoftware.SurfBurner.Burn.Data
.
Получение права
Следующий шаг должен изменить Ваш код так, чтобы это получило право прежде, чем сделать работу. Пример этого показан в Перечислении 2. Основная идея состоит в том, чтобы вызвать AuthorizationCopyRights
, передача его имя права Вы хотите получить (например, имя, определенное в Перечислении 1). AuthorizationCopyRights
попытается получить право от Вашего имени (возможно выводящий на экран диалоговое окно пароля; мы доберемся до этого в следующем разделе), и возвратите код ошибки, указывающий, было ли это успешно.
Перечисление 2 Получая право.
extern OSStatus AcquireRight(const char *rightName) // This routine calls Authorization Services to acquire // the specified right. { OSStatus err; static const AuthorizationFlags kFlags = kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights; AuthorizationItem kActionRight = { rightName, 0, 0, 0 }; AuthorizationRights kRights = { 1, &kActionRight }; assert(gAuthorization != NULL); // Request the application-specific right. err = AuthorizationCopyRights( gAuthorization, // authorization &kRights, // rights NULL, // environment kFlags, // flags NULL // authorizedRights ); return err; } |
Тур по базе данных политики авторизации
Если Вы измените свой код, как описано выше и затем протестируете его полностью, то Вы найдете, что при некоторых обстоятельствах он переводит диалоговое окно в рабочее состояние, прося, чтобы Вы ввели пользовательское имя администратора и пароль. Один простой способ воспроизвести это состоит в том, чтобы создать пользователя неадминистратора, войти в систему как тот пользователь, затем запустить Ваше приложение и сделать авторизованную работу. При поставке пользовательского приложения этот диалог аутентификации, вероятно, не, что Вы хотите. Однако, прежде чем Вы пытаетесь предотвратить выводимое на экран диалоговое окно, необходимо понять, почему оно выведено на экран.
Авторизацией на Mac OS X управляет база данных правил управления. В текущих версиях Mac OS X сдержана эта база данных /etc/authorization
. Формат базы данных описан в комментариях наверху того файла.
Фактически, формат базы данных правил управления изменился с версией 10.3 Mac OS X. Этот раздел использует новый формат, несмотря на то, что для Вам должно быть относительно просто отобразить понятия, описанные здесь на старом формате.
Для понимания, почему Authorization Services переводит диалоговое окно в рабочее состояние, когда Вы просите пользовательское право необходимо понять немного о том, как работает база данных правил управления. Перечисление 3 показывает выдержку от базы данных правил управления, как установлено версией 10.3 Mac OS X.
Перечисление 3 выдержка базы данных правил управления.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC [...]> <plist version="1.0"> <dict> [...] <key>rights</key> <dict> <key>system.device.dvd.setregion.initial</key> <dict> <key>class</key> <string>user</string> <key>comment</key> <string>Used by the dvd player to set the regioncode the first time. Note that changed the region code after it has been set requires a different right (system.device.dvd.setregion.change) Credentials remain valid indefinitely after they've been obtained. An acquired credential is shared amongst all clients.</string> <key>group</key> <string>admin</string> <key>mechanisms</key> <array> <string>builtin:authenticate</string> </array> <key>shared</key> <true/> </dict> [...] <key>config.add.</key> <dict> <key>class</key> <string>allow</string> <key>comment</key> <string>wildcard right for adding rights. Anyone is allowed to add any (non-wildcard) rights</string> </dict> [...] <key></key> <dict> <key>class</key> <string>rule</string> <key>comment</key> <string>All other rights will be matched by this rule. Credentials remain valid 5 minutes after they've been obtained. An acquired credential is shared amongst all clients. </string> <key>rule</key> <string>default</string> </dict> [...] </dict> <key>rules</key> <dict> [...] </dict> </dict> </plist> |
База данных состоит из двух словарей, rights
и rules
. В этом обсуждении мы сконцентрируемся на rights
словарь. Это содержит ряд пар ключ/значение, названных правильными спецификациями. Ключ является правильным именем, и значение является информацией о праве, включая описание того, что пользователь должен сделать для получения права. Перечисление 2 показывает три права.
system.device.dvd.setregion.initial
средства управления, разрешают ли пользователю установить начальный код области для DVD-привода. По умолчанию пользователь должен доказать, что они - администратор (в группеadmin
) для установки области DVD.config.add.
подстановочная спецификация права (она заканчивается точкой), который соответствует любое право, имя которого запускается с»config.add.
«. Это управляет, разрешают ли пользователю добавить правильную спецификацию к базе данных правил управления. По умолчанию любому пользователю разрешают добавить правильную спецификацию.Правильная спецификация с пустой строкой ключа известна как правильная спецификация по умолчанию. Для получения этого права, пользователь должен удовлетворить стандартное правило, которое, по умолчанию на текущих версиях Mac OS X, должно доказать, что они - администратор.
Когда Ваша программа просит право, Authorization Services выполняет следующий алгоритм.
Это ищет базу данных правил управления правильную спецификацию, ключ которой точно соответствует правильное имя.
Если это перестало работать, это ищет базу данных правил управления подстановочную спецификацию права, ключ которой соответствует правильное имя. Если многократный присутствуют, это использует тот с самым длинным ключом.
Если это перестало работать, это использует правильную спецификацию по умолчанию.
Как только это нашло надлежащую правильную спецификацию, Authorization Services оценивает спецификацию, чтобы решить, предоставить ли право. В некоторых случаях это просто (в примере в Перечислении 3 config.add.
всегда предоставляется), но в других случаях это может быть более сложно (например, устанавливание области DVD требует, чтобы Вы ввели пароль администратора).
Присутствие права по умолчанию имеет импликации для самоограниченных программ, использующих пользовательские права. Рассмотрите код в Перечислении 1 и Перечислении 2. Требуемое право, com.apple.dts.SelfRestrictedSample.xyz
, не соответствует определенной правильной спецификации в базе данных правил управления, и таким образом правильная спецификация по умолчанию используется. Это - то, почему код в Перечислении 2 переводит в рабочее состояние диалоговое окно, просящее пароль администратора.
Изменение базы данных правил управления
Очевидный способ избежать этого диалогового окна пароля состоит в том, чтобы добавить новую правильную спецификацию к базе данных правил управления, в частности позволяющей любому получать Ваше пользовательское право. Для системного администратора это просто. Они могут просто открыть файл с текстовым редактором (или действительно Редактор Списка свойств), внести надлежащие изменения, и затем сохранить изменения назад. Например, для добавления правильной спецификации, позволяющей любому получать право от Перечисления 1 можно просто добавить текст в Перечислении 4 к базе данных правил управления.
Перечисление 4 'всегда позволяет' правильную спецификацию для права от Перечисления 1.
[...] <key>com.apple.dts.SelfRestrictedSample.xyz</key> <dict> <key>rule</key> <string>allow</string> </dict> [...] |
Этот подход является надлежащим, если Вы ожидаете, что у Ваших клиентов будет квалифицированный удобный системный администратор (например, если Ваше приложение будет продано для использования в университетских лабораториях). Однако это не подходяще для программ, проданных большинству пользователей Mac, у которых нет преданного системного администратора. Кроме того, программно изменение файла базы данных правил управления непосредственно не является опцией, потому что, как отмечалось ранее, расположение и формат базы данных подвержен изменениям.
Решением этой загадки является новая база данных Authorization API, добавленный в версии 10.3 Mac OS X.
Добавление права программно
Код в Перечислении 5 показывает, как можно добавить правильную спецификацию к базе данных правил управления с помощью подпрограмм Authorization Services, выпущенных с версией 10.3 Mac OS X. Код является довольно прямым. Это сначала вызывает AuthorizationCreate
создать ссылку на экземпляр авторизации приложения. Это тогда вызывает AuthorizationRightGet
видеть, существует ли существующая правильная спецификация для ее пользовательского права. Если это существует, код ничего не делает: или системный администратор уже добавил, их собственная правильная спецификация или программа были ранее выполнены. Если, однако, никакая спецификация не существует для пользовательского права, код создает новую спецификацию (использование AuthorizationRightSet
) это позволяет любому получать право.
Перечисление 5 , Добавляющее право программно.
static OSStatus SetupRight( AuthorizationRef authRef, const char * rightName, CFStringRef rightRule, CFStringRef rightPrompt ) // Checks whether a right exists in the authorization database // and, if not, creates the right and sets up its initial value. { OSStatus err; // Check whether our right is already defined. err = AuthorizationRightGet(rightName, NULL); if (err == noErr) { // A right already exists, either set up in advance by // the system administrator or because this is the second // time we've run. Either way, there's nothing more for // us to do. } else if (err == errAuthorizationDenied) { // The right is not already defined. Let's create a // right definition based on the rule specified by the // caller (in the rightRule parameter). This might be // kAuthorizationRuleClassAllow (which allows anyone to // acquire the right) or // kAuthorizationRuleAuthenticateAsAdmin (which requires // the user to authenticate as an admin user) // or some other value from "AuthorizationDB.h". The // system administrator can modify this right as they // see fit. err = AuthorizationRightSet( authRef, // authRef rightName, // rightName rightRule, // rightDefinition rightPrompt, // descriptionKey NULL, // bundle, NULL indicates main NULL // localeTableName, ); // NULL indicates // "Localizable.strings" // The ability to add a right is, itself, governed by a non-NULLdescriptionKey // right. If we can't get that right, we'll get an error // from the above routine. We don't want that error // stopping the application from launching, so we // swallow the error. if (err != noErr) { #if ! defined(NDEBUG) fprintf( stderr, "Could not create default right (%ld)\n", err ); #endif err = noErr; } } return err; } extern OSStatus SetupAuthorization(void) // Called as the application starts up. Creates a connection // to Authorization Services and then makes sure that our // right (kActionRightName) is defined. { OSStatus err; // Connect to Authorization Services. err = AuthorizationCreate(NULL, NULL, 0, &gAuthorization); // Set up our rights. if (err == noErr) { err = SetupRight( gAuthorization, kAlphaRightName, CFSTR(kAuthorizationRuleClassAllow), CFSTR("YOU MUST BE AUTHORIZED TO DO XYZ") ); } [...] return err; } |
Эта последовательность гарантирует, что правильная спецификация добавляется к базе данных правил управления, таким образом, системный администратор, если таковые имеются, может тогда просмотреть базу данных правил управления, чтобы исследовать правильную спецификацию, создаваемую Вашим приложением и установить его значение соответственно. Перечисление 6 показывает, как могла бы посмотреть та правильная спецификация.
Перечисление 6 правильная спецификация, добавленная кодом от Перечисления 5.
[...] <key>com.apple.dts.SelfRestrictedSample.xyz</key> <dict> <key>default-prompt</key> <dict> <key></key> <string>YOU MUST BE AUTHORIZED TO DO XYZ</string> </dict> <key>rule</key> <string>allow</string> </dict> [...] |
AuthorizationRightSet
имеет большую гибкость, не продемонстрированную Перечислением 5. Последние два параметра позволяют Вам предоставлять локализованную форму подсказки права. Это объяснено подробно в следующем разделе. Кроме того, третий параметр может или быть строкой (имя одного из общих правил для того, чтобы получить право) или словарь (подробная спецификация того, как получить право). Можно считать комментарии заголовка в AuthorizationDB.h
(часть Концепции безопасности) для получения дополнительной информации об этом.
Локализация
Когда Вы вызываете AuthorizationRightSet
Вы передаете в трех параметрах, управляющих подсказкой, выведенной на экран, когда пользователь должен аутентифицировать для получения права. Эти параметры:
descriptionKey
, aCFStringRef
bundle
, aCFBundleRef
localeTableName
, aCFStringRef
bundle
и localeTableName
параметры определяют табличный файл локализации, содержащий подсказку. Если bundle
NULL
, подпрограмма использует основной пакет (как возвращено CFBundleGetMainBundle
). Если localeTableName
NULL
, подпрограмма использует Localizable.strings
файл в указанном пакете.
descriptionKey
параметр используется для поиска строки в табличном файле локализации. Если это NULL
, никакая подсказка не связана с правом.
Когда Вы вызываете AuthorizationRightSet
с не -NULL
descriptionKey
, это выполняет итерации по каждой локализации в пакете. Для каждой локализации это ищет надлежащий табличный файл локализации и затем ищет, описание вводят ту таблицу. Это тогда добавляет получившую строку к default-prompt
словарь в правильной спецификации.
Например, если Ваше приложение не имеет никаких локализаций, код в Перечислении 5 генерирует правильную спецификацию в Перечислении 6. Однако, если у Вас будет две локализации, английский (en) и австралийский английский язык (en_AU), то код в Перечислении 5 генерирует правильную спецификацию, показанную в Перечислении 7.
Перечисление 7 локализованная правильная спецификация, добавленная кодом от Перечисления 5.
[...] <key>com.apple.dts.SelfRestrictedSample.xyz</key> <dict> <key>default-prompt</key> <dict> <key></key> <string>YOU MUST BE AUTHORIZED TO DO XYZ</string> <key>en</key> <string>You must be authorized to do xyz.</string> <key>en_AU</key> <string>Strewth! You must be authorised to do xyz.</string> </dict> <key>rule</key> <string>allow</string> </dict> [...] |
С этой правильной спецификацией Вы будете видеть, что, когда запрошено для авторизации пользователь, предпочитающий английский язык по австралийскому английскому языку, будет видеть сообщение, «Вы должны быть разрешены сделать xyz», в то время как пользователь, предпочитающий австралийский английский язык по английскому языку, будет видеть «Strewth! Вы должны быть разрешены сделать xyz». Для австралийцев очень важно, чтобы каждое предложение содержало ругательство (и который «авторизовал» быть записанным с').
При выборе подсказки важно, чтобы Вы использовали все предложение. Строка, выведенная на экран в диалоговом окне авторизации, составлена из многих отличных предложений, и Ваша строка приглашения должна вписаться в ту модель.
Наконец, необходимо знать, что в настоящее время Authorization Services не поддерживает синонимы локализации (r. 3430642). Таким образом папка для локализации в Вашем пакете должна быть «en.lproj», не «English.lproj».
Свяжите проблемы подсчета ссылок
Существует один заключительный глюк, связанный с AuthorizationRightSet
. Вследствие ошибки (r. 3446163), AuthorizationRightSet
постепенно уменьшит подсчет ссылок пакета, на который он воздействует. Если Вы вызываете AuthorizationRightSet
многократно подсчет ссылок пакета перейдет к нулю, и пакет будет выпущен. Если Вы передаете NULL
к bundle
параметр AuthorizationRightSet
, это выпустит основной пакет, и Ваше приложение быстро откажет.
Эта ошибка исправлена в Mac OS X 10.4 и позже. Однако можно все еще работать вокруг ошибки путем искусственного постепенного увеличения подсчета ссылок пакета. Перечисление 8 показывает рекомендуемый код для этого обходного решения.
Перечисление 8 Рекомендуемое обходное решение для ошибки подсчета ссылок.
static OSStatus AuthorizationRightSetWithWorkaround( AuthorizationRef authRef, const char * rightName, CFTypeRef rightDefinition, CFStringRef descriptionKey, CFBundleRef bundle, CFStringRef localeTableName ) // The AuthorizationRightSet routine has a bug where it // releases the bundle parameter that you pass in (or the // main bundle if you pass NULL). If you do pass NULL and // call AuthorizationRightSet multiple times, eventually the // main bundle's reference count will hit zero and you crash. // // This routine works around the bug by doing an extra retain // on the bundle. It should also work correctly when the bug // is fixed. // // Note that this technique is not thread safe, so it's // probably a good idea to restrict your use of it to // application startup time, where the threading environment // is very simple. { OSStatus err; CFBundleRef clientBundle; CFIndex originalRetainCount; // Get the effective bundle. if (bundle == NULL) { clientBundle = CFBundleGetMainBundle(); } else { clientBundle = bundle; } assert(clientBundle != NULL); // Remember the original retain count and retain it. We force // a retain because if the retain count was 1 and the bug still // exists, the next call might decrement the count to 0, which // would free the object. originalRetainCount = CFGetRetainCount(clientBundle); CFRetain(clientBundle); // Call through to Authorization Services. err = AuthorizationRightSet( authRef, rightName, rightDefinition, descriptionKey, clientBundle, localeTableName ); // If the retain count is now magically back to its original // value, we've encountered the bug and we print a message. // Otherwise the bug must've been fixed and we just balance // our retain with a release. if ( CFGetRetainCount(clientBundle) == originalRetainCount ) { fprintf( stderr, "AuthForAll: Working around <rdar://problems/3446163>\n" ); } else { CFRelease(clientBundle); } return err; } |
Поддержка более ранних систем
Если Ваше приложение должно работать на более ранних версиях Mac OS X, подпрограммы Authorization Services, описанные в предыдущих разделах, не помогают. Существует две стратегии, которые можно использовать при необходимости в совместимости с более ранними системами.
Можно просто протестировать на присутствие новых подпрограмм Authorization Services и не сделать никакой авторизации вообще, если они не присутствуют. Это обладает преимуществом простоты, несмотря на то, что системные администраторы, не обновившие к версии 10.3 Mac OS X, не получают контроля над тем, кто может использовать который функции Вашего приложения.
Можно попытаться эмулировать
AuthorizationRightGet
путем чтения базы данных политики авторизации непосредственно. Это непосредственно противоречит уведомлению, данному ранее, но приемлемо, если Вы только делаете это в более старых системах, где не изменятся формат и расположение базы данных политики авторизации. На версии 10.3 Mac OS X и позже, где новые подпрограммы Authorization Services присутствуют, необходимо использовать новые подпрограммы и не предположить что-либо о формате базы данных политики авторизации.
Сводка
Authorization Services позволяет Вам создавать самоограниченное приложение, в чем системный администратор может управлять, к которому пользователям разрешают получить доступ который функции. С версией 10.3 Mac OS X можно реализовать это, не ставя под угрозу опыт из поля для типичных клиентов. Просто принять Authorization Services, и выполнение так сделает системных администраторов мира очень счастливыми.
Ссылки
История версии документа
Дата | Примечания |
---|---|
30.01.2008 | Обновленный для включения Ссылок BetterAuthorizationSample. |
23.10.2003 | Новый документ, описывающий приложения Authorization Services вне простого запроса полномочия. |